Создание вложенных моделей — Rails 3.1

#model #ruby-on-rails-3.1

#Модель #ruby-on-rails-3.1

Вопрос:

Я пытаюсь создать 3 вложенные модели одновременно, и у меня проблемы с моими проверками.

Это мои модели:

 class UserEntity < ActiveRecord::Base
  has_many    :users,           :dependent => :restrict, :autosave => true
end

class User < ActiveRecord::Base
  has_many    :user_login_services, :dependent => :destroy, :autosave => true
  belongs_to  :user_entity
end

class UserLoginService < ActiveRecord::Base
  belongs_to :user
  #validates :user_id, :presence => true
end
  

(UserEntity может быть компанией со многими пользователями. UserLoginService используется для служб Omniauth, таких как Facebook, OpenID и т. Д.)

Пользователи создаются с

 def new_user_login(tokens)
  user_entity = UserEntity.new
  user = user_entity.users.build(:email => tokens[:email], :password => Devise.friendly_token[0,20], :first_name => tokens[:first_name], :last_name => tokens[:last_name], :has_local_password => false)
  user.skip_confirmation!
  user.user_login_services.build(:provider => tokens[:provider], :uid => tokens[:uid], :uname => tokens[:name], :uemail => tokens[:email])     
  user_entity.save! 
  user.confirm!
  

Этот код, похоже, работает нормально и создает соответствующие записи во всех 3 таблицах. Проблема возникает, когда я раскомментирую ‘validates :user_id, : presence => true’ в UserLoginService, что приводит меня

 ActiveRecord::RecordInvalid (Validation failed: Users user login services user can't be blank):
  app/controllers/users/omniauth_callbacks_controller.rb:86:in `new_user_login'
  

Обратите внимание, что код, похоже, работает нормально без проверки, а user_id в таблице user_login_services установлен в правильное значение.

Почему я получаю эту ошибку и как мне ее решить?

Ответ №1:

В двух словах:

в ваших моделях

 class UserEntity < ActiveRecord::Base
  has_many :users
  accepts_nested_attributes_for :users
end

class User < ActiveRecord::Base
  belongs_to :user_entity
  has_many   :user_login_services, :dependent => :destroy
  accepts_nested_attributes_for :user_login_services
end

class UserLoginService < ActiveRecord::Base
  belongs_to :user
  validates :user_id, :presence => true
end
  

в ваших контроллерах

 def create
  @user_entity = UserEntity.new(params[:user_entity])
  @user_entity.save
  # To Do: handle redirections on error and success
end
  

и в вашей форме

 <%= form_for(@user_entity) do |f| %>
  <%= f.fields_for :users do |u| %>
    <%= u.fields_for :user_login_services do |ul| %>
      <%= ul.select :user_id, @user_entity.users.collect{|u| [u.name, u.id]} %>
    <% end %>
  <% end %>
<% end %>
  

Кроме того, я рекомендую вам проверить: http://railscasts.com/episodes/196-nested-model-form-part-1

Ответ №2:

Вам необходимо accepts_nested_attributes_for inverse_of определить отношения и.

 class UserEntity < ActiveRecord::Base
  has_many   :users, :dependent => :restrict, :autosave => true, :inverse_of => :users_entity
  accepts_nested_attributes_for :users
end

class User < ActiveRecord::Base
  has_many    :user_login_services, :dependent => :destroy, :autosave => true, :inverse_of => :user
  belongs_to  :user_entity, :inverse_of => :users
  accepts_nested_attributes_for :user_login_services
end

class UserLoginService < ActiveRecord::Base
  belongs_to :user, :inverse_of => :users_login_services
  validates :user, :presence => true 
end
  

Я также переключился validates :user_id на validates :user предположение, что вы хотите проверить существование связанного user , а не только то, что UserLoginService у user_id него есть.

Комментарии:

1. Большое спасибо, указание inverse_of сделало это за меня! В противном случае при сохранении Rails сначала попытается сохранить вложенную модель (с вложенной моделью belongs_to top model), и это приведет к сбою, поскольку верхняя модель еще не создана.