2015-06-18 1 views
1

Мои модели:метод Лучшая практика - создание 2 записей в 1 form_for

brand.rb

has_many :products 
    has_many :votes 
    belongs_to :user 
    accepts_nested_attributes_for :products, :allow_destroy => true 

product.rb

belongs_to :user 
    belongs_to :brand 

vote.rb

belongs_to :brand 
    belongs_to :user 

routes.rb

resources :brands do 
    resources :products 
    end 

Моя цель: Создать 2 записей (продукт и голосования) на существующих Марка запись в 1 форме, на brand/show странице.

Мое решение:

бренд/show.html.erb

<% form_for([@brand, @brand.send(:product).klass.new]) do |f| %> 
    <%= f.label :title %> 
    <%= f.text_field :title %> 

    <%= f.label :price %> 
    <%= f.text_field :price %> 

    <%= fields_for :votes, @brand.votes.new do |builder| %> 
    <%= builder.label :rating %> 
    <%= builder.text_field :rating %> 
    <% end %> 

    <%= f.submit %> 
<% end %> 

products_controller.rb

def create 
    if Brand.exists?(:id => params[:brand_id]) 
    @review   = Review.new(review_params) 
    @vote   = Vote.new(votes_params) 
    @review.user_id = @vote.user_id = current_user.id 
    @review.brand_id = @vote.brands_id = params[:brand_id] 

    if @vote.valid? && @review.valid? 
     @vote.save 
     @review.save 
     redirect_to brands_path 
    else 
     flash[:errors][:vote] = @vote.errors 
     flash[:errors][:review] = @review.errors 
     redirect_to brands_path 
    end 
    end 
end  

private 
def product_params 
    params.require(:review).permit(:title, :price) 
end  

def votes_params 
    params.require(:votes).permit(:rating) 
end 

Является ли это правильный путь решения моей задачи? Могу ли я использовать его так?

ответ

1

Это, как я бы реорганизовать создать метод:

def create 
    brand = Brand.find(params[:brand_id]) # no test to know if Brand exists, if it does not it means the user gave a wrong Brand id, then a 404 error should be rendered 
    @product = brand.products.create(products_params.merge({user_id: current_user.id}) # we can directly create this instance 
    @vote = brand.votes.create(votes_params) # we can directly create this instance 
    # we add the errors in the flash if they exist 
    flash[:errors][:vote] = @vote.errors if @vote.errors.present? 
    flash[:errors][:product] = @product.errors if @product.errors.present? 

    redirect_to brands_path # since we want to redirect to brands_path if the creation succeeded or failed, we don't need to use it twice in the code 
end 

Кроме того, небольшие улучшения:

@brand.send(:product).klass.new 
# can become 
@brand.products.new # produces a initialized Product instance 

fields_for :votes, @brand.votes.new 
# can become 
f.fields_for @brand.votes.new 
# notice the usage of `f` builder to generate the fields_for 
# it will nest the params in `params[:brand][:votes_attributes][0]` 

# brand.rb 
accepts_nested_attributes_for :products, :allow_destroy => true 
# add the following: 
accepts_nested_attributes_for :votes, :allow_destroy => true 

Вы, очевидно, должны обновить свой сильный Params соответственно, но это легкая часть ;-)

+0

Выглядит очень здорово! Но я не пишу 'f.fields_for', потому что я не хочу касаться уже созданной записи« brand », чтобы избежать повторной аттестации и т. Д. И о build_product, я получаю' undefined method 'build_reviews''. Может быть, я должен сделать что-то вроде '@ brand.products.build' в моем контроллере? – Src

+0

, если вам нужны объяснения, не стесняйтесь, многое изменилось в этом предлагаемом рефакторе, и вы не можете получить причину, почему он изменился на это. – MrYoshiji

+1

Кроме того, я использовал метод 'valid?' Для сохранения двух моделей или ничего не сохранять. – Src

0

Я изменил бы следующую логику:

@review.user_id = @vote.user_id = current_user.id 
@review.server_id = @vote.server_id = params[:server_id] 

Просто добавьте current_user.id и Params [:] server_id к product_params и votes_params соответственно.

Кроме того, не нужно использовать переменные экземпляра для голосования/обзора.

Кроме того, для меня две модели экономии сохраняются.

+0

'params' недоступен в моделях – MrYoshiji

+0

Хороший вопрос, редактируя ответ. – nesiseka

+0

Хорошо, ': set_ids' метод хорош для' current_user.id', но, возможно, не так хорош для ': server_id', из-за другого имени переменной post, которое мы можем использовать для передачи' server_id'. Может быть, я должен изменить ваш метод ': set_ids', чтобы проверить, установлен ли' server_id' и если true - пропустить его? И еще один, как я могу получить параметры в модели? – Src