Как написать простую вложенную форму has_many: through, многие ко многим в Rails 3.1? - PullRequest
1 голос
/ 18 июля 2011

Я создаю менеджер рецептов в качестве первого приложения для рельсов.У меня есть множество вложенных моделей, основанных на этом довольно хорошем ERD .В идеале я хотел бы создать форму, которая позволит мне создать рецепт в одной форме.Кроме того, я бы хотел, чтобы пользователь мог писать / вставлять ингредиенты и шаги в одно текстовое поле соответственно.В приведенном ниже коде есть виртуальный атрибут, который анализирует разделенные на cr списки в форме, чтобы попытаться это сделать.

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

Чтобы упростить вещи, я бы хотел назначить список ингредиентов либо первому, либо каждому шагу. Как мне написать код, чтобы он вручную создавал модель соединения с виртуальным атрибутом?

Мои четыре модели:

recipe.rb

    class Recipe < ActiveRecord::Base
      has_many :steps, :dependent => :destroy
      has_many :stepingreds, :through => :steps
      has_many :ingredients, :through => :stepingreds
      validates_presence_of :name, :description
      attr_writer :step_instructions, :ingredient_names
      after_save :assign_steps, :assign_ingredients

      def step_instructions
        @step_instruction || steps.map(&:instruction).join("\n")
      end

      def ingredient_names
        @ingredient_name || ingredients.map(&:name).join("\n")
      end

    private

    def assign_steps
        if @step_instructions
          self.steps = @step_instructions.split(/\n+/).map do |instruction|
            Step.find_or_create_by_instruction(instruction)
          end
        end
    end

      def assign_ingredients
        if @ingredient_names
          self.ingredients = @ingredient_names.split(/\n+/).map do |name|
            Ingredient.find_or_create_by_name(name)
          end
        end
      end
    end

step.rb

    class Step < ActiveRecord::Base
      #attr_accessible :recipe_id, :number, :instructions
      belongs_to :recipe
      has_many :stepingreds, :class_name => 'Stepingred'
      has_many :ingredients, :through => :stepingreds
    end

stepingred.rb

    class Stepingred < ActiveRecord::Base
      belongs_to :ingredient
      belongs_to :step, :class_name => 'Step'
    end

ингридиент.rb

    class Ingredient < ActiveRecord::Base
      has_many :stepingreds
      has_many :steps, :through => :stepingred
      has_many :recipes, :through => :steps
    end

А вот моя урезанная форма:

    <%= form_for @recipe do |f| %>
      <%= f.error_messages %>
      <p>
        <%= f.label :name %><br />
        <%= f.text_field :name %>
      </p>
      <p>
        <%= f.label :description %><br />
        <%= f.text_area :description, :rows => 4 %>
        </p>
         <p>
        <%= f.label :ingredient_names, "Ingredients" %><br />
        <%= f.text_area :ingredient_names, :rows => 8 %>
      </p>
      <p>
        <%= f.label :step_instructions, "Instructions" %><br />
        <%= f.text_area :step_instructions, :rows => 8 %>
      </p>
      <p><%= f.submit %></p>
    <% end %>

Моя схема базы данных:

    ActiveRecord::Schema.define(:version => 20110714095329) do
      create_table "ingredients", :force => true do |t|
        t.string   "name"
        t.datetime "created_at"
        t.datetime "updated_at"
      end
      create_table "recipes", :force => true do |t|
        t.string   "name"
        t.text     "description"
        t.datetime "created_at"
        t.datetime "updated_at"
      end
      create_table "stepingreds", :force => true do |t|
        t.integer  "recipe_id"
        t.integer  "step_id"
        t.integer  "ingredient_id"
        t.float    "amount"
        t.datetime "created_at"
        t.datetime "updated_at"
      end
      create_table "steps", :force => true do |t|
        t.integer  "recipe_id"
        t.integer  "number"
        t.text     "instruction"
        t.datetime "created_at"
        t.datetime "updated_at"
      end
    end

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

1 Ответ

2 голосов
/ 23 июля 2011

Вам необходимо добавить accepts_nested_attributes_for :ingredients, :stepingreds, :steps в Recipe.rb, чтобы иметь возможность создавать связанные объекты с помощью одного @recipe объекта.

http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

...