Ruby on Rails - Добавление родительского объекта в дочерний - PullRequest
1 голос
/ 18 января 2010

У меня есть пользователь, сохраненный в сеансе, который я хотел бы добавить в качестве владельца комментария. Вместо того, чтобы иметь скрытое поле для user_id, я хотел бы добавить пользователя перед сохранением комментария в контроллере.

Каков наилучший способ сделать это?

@comment = @post.comments.create(params[:comment])

Спасибо.

Ответы [ 2 ]

1 голос
/ 18 января 2010

Во-первых, из соображений безопасности вы, вероятно, хотите защитить атрибут комментария user_id, поэтому в вашей модели должно быть что-то подобное:

attr_protected :user_id

Или используйте attr_accessible и перечислите все атрибуты, которые можно установить с помощью массового назначения (т. Е. Comment.create(...) или @comment.update_attributes(...)). Тогда, поскольку вы должны назначить через назначение, ваш контроллер будет выглядеть так:

@comment = @post.comments.new(params[:comment])
@comment.user_id = current_user.id
@comment.save

Это не так гладко, но необходимо, чтобы кто-то не мог подделать поддельное user_id значение.

1 голос
/ 18 января 2010

Есть несколько стратегий, которые работают довольно хорошо. Вы можете иметь вызов в контроллере, который прикрепляет пользователя к созданному комментарию:

def create
  @comment = @post.comments.build(params[:comment])
  @comment.user = session_user
  @comment.save!

  redirect_to(post_path(@post))

rescue ActiveRecord::RecordInvalid
  # Take appropriate action, such as show comment create form
  render(:action => 'new')
end

Другой способ - использовать что-то вроде model_helper (http://github.com/theworkinggroup/model_helper/) для предоставления доступа к свойствам контроллера в среде модели:

class ApplicationController < ActionController::Base
  # Makes the session_user method callable from the ActiveRecord context.
  model_helper :session_user
end

class Comment < ActiveRecord::Base
  before_validation :assign_session_user

protected
  def assign_session_user
    if (self.user.blank?)
      self.user = session_user
    end
  end
end

Этот метод более автоматический, но за счет прозрачности и, возможно, усложняет среду юнит-тестирования.

Третий подход - объединить параметры при вызове create:

@comment = @post.comments.build((params[:comment] || { }).merge(:user => session_user))

Недостатком этого является то, что некоторые свойства вашей модели работают не очень хорошо, как, вероятно, должно быть в любой производственной среде.

Еще одна хитрость заключается в создании метода класса, который помогает создавать вещи для вас:

class Comment < ActiveRecord::Base
  def self.create_for_user(user, params)
    created = new(params)
    created.user = user
    created.save
    created
  end
end

Это вызывается на отношения и будет строить в правильной области:

@comment = @post.comments.create_for_user(session_user, params[:comment])
...