accept_nested_attributes_ для того, чтобы не сохранять вложенные атрибуты

Я уже несколько дней зацикливаюсь на этой проблеме. Это мое первое приложение на Rails, и я почти закончил, только для того, чтобы это значительно замедлило работу.

Я читал, что использование accept_nested_attributes_for - лучшее решение моей проблемы вложения ингредиентов в рецепт для использования с формами, но пока мне не повезло. Я прочитал все, что смог найти по этой теме. API сообщает, что у модели теперь есть (в моем случае) метод ингридиентов_атрибутов =, которого я пока не вижу. Я попытался использовать update_attributes в консоли с хешем

recipe.update_attrubutes {:ingredients_attributes=>[{:name=>"Test Ingredient"}]}

Это возвращается true, но не показывает никаких изменений в объекте рецепта.

Я пробовал много подходов, первым из которых я просто использовал fields_for внутри form_for внутри моего View. Поскольку это не сработало, и я безрезультатно тестировал код, я начал искать глубже, и проблема определенно лежит глубже, чем представление.

Любая помощь приветствуется. Мой код следует

Модель рецепта

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

class Recipe < ActiveRecord::Base

  DISH_TYPES={""=>"", "Breakfast"=>"breakfast", "Lunch"=>"lunch", "Soup"=>"soup", "Entree"=>"entree", "Desert"=>"desert"}
  SEASONS={"Any Season"=>"any", "Fall"=>"fall", "Winter"=>"winter", "Spring"=>"spring", "Summer"=>"summer"}
  DIETS={""=>"", "Vegan"=>"vegan", "Vegetarian"=>"vegetarian", "Omnivore"=>"omnivore"}

  DISH_TYPES_R={""=>"", "breakfast"=>"Breakfast", "lunch"=>"Lunch", "soup"=>"Soup", "entree"=>"Entree", "desert"=>"Desert"}
  SEASONS_R={"any"=>"Any Season", "fall"=>"Fall", "winter"=>"Winter", "spring"=>"Spring", "summer"=>"Summer"}
  DIETS_R={""=>"", "vegan"=>"Vegan", "vegetarian"=>"Vegetarian", "omnivore"=>"Omnivore"}

  attr_protected :user_id
  # Do NOT include user_id in the attr_accessible method, to avoid
  # the possibility of it being changed externally.
  belongs_to :user
  validates_presence_of :user  
  has_many :ingredients, dependent: :destroy # , inverse_of: :recipe

  # Allows for forms to write attributes down the hierarchy.
  accepts_nested_attributes_for :ingredients, allow_destroy: true , reject_if: lambda { |a| a[:content].blank? }

  before_save do
    # Lowercase and convert to strings all of the attributes
    # that require a specific format, namely the values in the
    # constant hashes above.
    STRING_ATTRIBUTES.each do |s|
      self.send("#{s}=".to_sym, self.send(s).downcase.to_s)
    end
  end

  validates :user_id, presence: true
  validates :name,  presence: true,
                    length: { maximum: 64 } #,uniqueness: true
  validates :dish_type, inclusion: { in: DISH_TYPES.values }
  validates :season, inclusion: { in: SEASONS.values }
  validates :diet, inclusion: { in: DIETS.values}
  validates :directions,  presence: true,
                          length: { maximum: 8192 }

  validates_associated :ingredients


  default_scope order: "recipes.created_at DESC"


  def method_missing (method)
    method = method.to_s
    if method.slice!("display_")
      if STRING_ATTRIBUTES.include?(method.to_sym)
        hash_name = method.upcase + 'S_R'
        Recipe.const_get(hash_name)[self.send(method.to_sym)]
      else
        method
      end
    else 
      method.class
    end
  end

  private

    STRING_ATTRIBUTES = [:dish_type, :season, :diet]

end

Ингредиент Модель

class Ingredient < ActiveRecord::Base

  attr_protected :id, :recipe_id
  belongs_to :recipe

  validates_presence_of :name
  #validates_presence_of :recipe
end

Контроллер рецептов

Я читал, что мне не нужно ничего менять в контроллере. Я добавил только одну строку, так что форма ингредиента отображается в моем представлении

class RecipesController < ApplicationController
  before_filter :signed_in_user, only: [:create, :edit, :destroy]

  def index
    @recipes = Recipe.all
  end

  def new
    if signed_in?
      @recipe = current_user.recipes.build 
      @recipe.ingredients.build
    else
      flash[:error] = "First you have to register! Sign up here and start adding recipes ASAP."
      redirect_to signup_path
    end
  end

  def create
    @new_recipe = current_user.recipes.build(params[:recipe])
    if @new_recipe.save
      flash[:success] = "You've successfully added #{@new_recipe.name}!"
      redirect_to @new_recipe
    else
      redirect_to 'new'
    end
  end

  def edit
    @recipe = Recipe.find(params[:id])
  end

  def show
    @recipe = Recipe.find(params[:id].to_i)
  end

end

Контроллер ингредиентов

Я не верю, что в контроллере ингредиентов есть какая-то польза, поскольку к ингредиентам (пока) можно будет получить доступ только через их родительский рецепт.

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


person David Frey    schedule 28.12.2012    source источник


Ответы (1)


  1. Вам нужно установить attr_accessible для Recipe модели и добавить туда :ingredients_attributes
  2. Как я вижу в вашей конфигурации, у вас есть reject_if: lambda { |a| a[:content].blank? } для компонентов_attributes, и вы устанавливаете его только с именем
person Phobos98    schedule 28.12.2012
comment
Не могу поверить, что я это пропустил. Я нашел это в Интернете, но подумал, что: content относится ко всей совокупности атрибутов content. Я прокомментировал это раньше, чтобы узнать, не вызывает ли это проблема, но она все еще была у меня. Кажется, теперь он работает !!! Что вы имеете в виду под attribute_acceptable? Я не вижу ссылок на этот метод. - person David Frey; 28.12.2012
comment
Это был неправильный ввод - я имел в виду attr_accessible - person Phobos98; 28.12.2012
comment
В этом есть смысл. Основываясь на моем недавнем чтении по этому вопросу, моя строка attr_protected :user_id противоположна методу attr_accessible, поскольку она создает доступ ко всему, НО: user_id. Так что я считаю, что пока это нормально. Ты согласен? - person David Frey; 28.12.2012