1

Этот вопрос следует за Rails has_many :through association: save instance into join table, и я повторяю здесь все для большей ясности.Rails has_many: через ассоциацию: Обновление всех 3 моделей одновременно

В нашем приложении Rails, есть 3 модели:

class User < ActiveRecord::Base 
    has_many :administrations, dependent: :destroy 
    has_many :calendars, through: :administrations 
end 

class Administration < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :calendar 
end 

class Calendar < ActiveRecord::Base 
    has_many :administrations, dependent: :destroy 
    has_many :users, through: :administrations 
end 

А вот соответствующие Миграции:

class CreateUsers < ActiveRecord::Migration 
    def change 
    create_table :users do |t| 
     t.string :first_name 
     t.string :last_name 
     t.string :email 
     t.integer :total_calendar_count 
     t.integer :owned_calendar_count 

     t.timestamps null: false 
    end 
    end 
end 

class CreateAdministrations < ActiveRecord::Migration 
    def change 
    create_table :administrations do |t| 
     t.references :user, index: true, foreign_key: true 
     t.references :calendar, index: true, foreign_key: true 
     t.string :role 

     t.timestamps null: false 
    end 
    end 
end 

class CreateCalendars < ActiveRecord::Migration 
    def change 
    create_table :calendars do |t| 
     t.string :name 

     t.timestamps null: false 
    end 
    end 
end 

Вот что мы пытаемся достичь:

Когда зарегистрированный пользователь (current_user) создает календарь, мы должны:

  • Создать новую @calendar и сохранить его в таблицу
  • Календаря Присвоить «Creator» роль для пользователя (current_user) для этого вновь созданного календаря через колонку Роли в таблице администрирования
  • увеличивают total_calendar_count и столбцы owner_calendar_count таблицы пользователей

Для этого мы считаем, что нам нужно работать над календарями # create.

В CalendarsController, у нас уже есть следующий код:

def create 
    @calendar = current_user.calendars.create(calendar_params) 
    if @calendar.save 
     flash[:success] = "Calendar created!" 
     redirect_to root_url 
    else 
     render 'static_pages/home' 
    end 
    end 

И мы собираем данные от пользователей через следующие _calendar_form.html.erb формы:

<%= form_for(@calendar) do |f| %> 
    <%= render 'shared/error_messages', object: f.object %> 
    <div class="field"> 
    <%= f.text_field :name, placeholder: "Your new calendar name" %> 
    </div> 
    <%= f.submit "Create", class: "btn btn-primary" %> 
<% end %> 

Мы рассматриваем обновление контроллера следующим образом:

def create 
    @calendar = current_user.calendars.create(calendar_params) 
    @current_user.total_calendar_count += 1 
    @current_user.owned_calendar_count += 1 
    current_user.administrations << @calendar.id 
    @calendar.administration.role = 'Creator' 
    if @calendar.save 
     flash[:success] = "Calendar created!" 
     redirect_to root_url 
    else 
     render 'static_pages/home' 
    end 
    end 

ActiveRecord::AssociationTypeMismatch in CalendarsController#create 
Administration(#70307724710480) expected, got Fixnum(#70307679752800) 

unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize) 
    message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})" 
    raise ActiveRecord::AssociationTypeMismatch, message 
    end 
end 

app/controllers/calendars_controller.rb:7:in `create' 

Как мы можем сделать это к?

+1

'@calendar = current_user.calendars.create (calendar_params)' должен быть '@calendar = Calendar.create (calendar_params)' для создания нового календаря. Вы получаете ошибку b/c 'current_user.calendars' не возвращает то, что имеет метод создания класса Calendar. –

+2

@steveklein 'current_user.calendars.create (calendar_params)' отлично работает, даже если 'current_user.calendars' не возвращает календаря. –

+1

Узнал что-то новое ... спасибо @Arslan. –

ответ

1

Эта строка вызывает ошибку: current_user.administrations << @calendar.id.

current.administrations ожидает объект типа Administration, пока вы проходите мимо Fixnum.

Вы можете выполнить ту же функциональность следующим образом:

current_user.administrations.create(calendar_id: @calendar.id) 

Edit:

Как просили ОП в комментариях, что это хорошая практика или нет. Видите, есть правило, в котором говорится, что контроллеры должны быть тощими, а модели должны быть жирными. Ну, это означает, что вы должны попытаться написать минимальный код, и вся логика и выборка объектов должны присутствовать в моделях. Но это не так в вашем сценарии кода. Вы должны перенести свой код в модель и затем вызвать это в свой контроллер.

Вот как:

class User < ActiveRecord::Base 
    def add_calendar_and_role(calendar_id, role) 
    self.administrations.find_by(calendar_id: calendar_id).update(role: role) 
    end 
end 

Таким образом, ваш код сводится к просто:

current_user.add_calendar_and_role(@calendar.id, 'Creator') 

И таким же образом, вы можете дополнительно реорганизовать код контроллера.

+0

Спасибо. Этот код частично работает. Действительно, он создает календарь. Но на самом деле это создает два. Наше предположение: первый создается при запуске '@calendar = current_user.calendars.create (calendar_params)', а второй создается из-за 'current_user.administrations.create (calendar_id: @ calendar.id)'. Есть ли способ создать новый календарь и обновить соответствующую @administration (ту, где calendar_id == @ calendar.id) с ролью «Создатель»? –

+0

Мы нашли решение: мы заменили 'current_user.administrations.create (calendar_id: @ calendar.id)' на 'current_user.administrations.find_by (calendar_id: @ calendar.id) .update (role: 'Creator')'. Он действительно работает, но является ли это хорошей практикой или другой анти-моделью, которая является плодом или нашей неопытности? –

+0

@ThibaudClement Я обновил свой ответ. Если вы думаете, что мой ответ помог, вы можете принять его. –