Jeden formularz i dwa modele

Witam mam taki problem
Chciałbym za pomocą jednego formularza wprowadzać elementy do dwóch tabel

def checkout
    if @cart.items.empty?
      redirect_to_index("Twój koszyk jest pusty.")
      @order = Order.new
      @shipping_address = ShippingAddress.new
    end
  end
  
  def save_order
    @order = Order.new(params[:order])
    @order.user_id = User.find(session[:user_id])
    @order.add_line_items_from_cart(@cart)
    @shipping_address = ShippingAddress.new(params[:shipping_address])
    if (@order.save && @shipping_address.save)
      session[:cart] = nil
      redirect_to_index("Dziękujemy za złożenie zamówienia")
    else
      render :action => :checkout
    end
  end

Widok wygląda tak

<div class="sklep-form">
  <%= error_messages_for 'order'%>
<fieldset>  
    <legend>Wpisz swoje dane:</legend>
    <% form_for :order, :url => {:action => 'save_order'} do |form|%>
      <fieldset>
      <legend>Adres wysyłki</legend>
<%= error_messages_for 'shipping_address'%>
<% fields_for 'shipping_addresses' do |shipping_address| %>
<p><label for="shipping_address_kod_p">Kod pocztowy</label>
<%= form.text_field :kod_p, :size => 6 %>

  <label for="shipping_address_miasto">Mejscowość</label>
<%= form.text_field :miasto, :size => 40 %>
<p>
  <label for="shipping_address_ulica">Ulica: </label>
<%= form.text_field :ulica, :size => 40 %>
<label for="shipping_address_nr_domu">Numer domu: </label>
<%= form.text_field :nr_domu, :size => 6 %>
<label for="shipping_address_nr_mieszkania">Numer mieszkania:</label>
<%= form.text_field :nr_mieszkania, :size => 6%>
  </p>
<%end%>
</fieldset>
     <p>
        <label for="order_pay_type">Forma płatności</label>
      <p>
        Przelew: 
        <%= form.radio_button(:pay_type, :przelew)%>
        Płatność przy odbiorze:
          <%= form.radio_button(:pay_type, :pobranie)%>
          </p>
        </p>

      <p>
        <label for="order_uwagi">Uwagi: </label>
<%= form.text_area(:uwagi, :colls => 40, :rows => 4)%>
            </p>
          </fieldset>
<%= submit_tag "Złóż zamówienie|", :class => "submit"%>
<% end %>

</div>

Dostaję komunikat

NoMethodError in StoreController#save_order undefined method `nr_domu=' for #<Order:0xc4b5f2>
Wszystkie nazwy w formularzach są takie jak w tabeli. Bez dodawania shipping_address wszystko działało normalnie.

Jeśli korzystasz z metody fields_for z argumentem bloku shipping_address to pola znajdujące się w nim powinny być wywoływane w tym przypadku tak:

<%= shipping_address.text_field :ulica, :size => 40 %>

zamiast

<%= form.text_field :ulica, :size => 40 %>

Inaczej Zostaną one przyporządkowane do modelu Order, co zresztą widać w komunikacie błędu.

Dzięki. Częściowo pomogło tzn w tabeli shipping_addresses pojawiają się nowe rekordy, ale nie pojawiają się dane wprowadzone z formularza, ani order_id i user_id

# == Schema Information
# Schema version: 8
#
# Table name: shipping_addresses
#
#  id            :integer(11)     not null, primary key
#  user_id       :integer(11)     
#  order_id      :integer(11)     
#  miasto        :string(255)     
#  kod_p         :string(255)     
#  ulica         :string(255)     
#  nr_domu       :integer(11)     
#  nr_mieszkania :integer(11)     

W modelach relacje wyglądają tak

class User < ActiveRecord::Base
  has_many :orders
  has_many :shipping_addresses

class Order < ActiveRecord::Base
  has_many :line_items
  belongs_to :user
  belongs_to :shipping_address

class ShippingAddress < ActiveRecord::Base
 has_many :users
 has_many :orders

Polecam obejrzeć wszystkie 3 episody Railscasts na ten temat, wyjaśniają problem dokładnie i w bardzo elegancki sposób:

http://railscasts.com/episodes/73
http://railscasts.com/episodes/74
http://railscasts.com/episodes/75

Poza tym:

@order.user_id = User.find(session[:user_id])

nie zadziała bo próbujesz przypisać obiekt do integera. W ogóle “Rails Way” jest napisać current_user.orders.build(attributes) zamiast przypisywać w ten sposób id.

Pozmieniałem trochę formularz i teraz dostaję taki komunikat

NameError in Store#checkout 
Showing app/views/store/checkout.rhtml where line #12 raised: 

`@order[shipping_address_attributes][]' is not allowable as an instance variable name

Extracted source (around line #12): 

9: <% fields_for 'order[shipping_address_attributes]' do |shipping_address| %>
10: <p><label for="shipping_address_kod_p">Kod pocztowy</label>
11: <%= shipping_address.text_field :kod_p, :size => 6 %>
12: 
13:   <label for="shipping_address_miasto">Mejscowość</label>
14: <%= shipping_address.text_field :miasto, :size => 40 %>
15: <p>

Kontroler wygląda tak

def checkout
    if @cart.items.empty?
      redirect_to_index("Twój koszyk jest pusty.")
      @order = Order.new
      @order.shipping_addresses.build
    end
  end

Model

 def shipping_address_attributes=(shipping_address_attributes)
    shipping_address_attributes.each do |attributes|
      shipping_addresses.build(attributes)
    end
  end

Co do przypisania to na pewno masz rację, że w ten sposób się tego nie robi, ale w moim przypadku akurat zadziałało bo w sesjii jest tylko user_id i do tabeli orders pobiera właściwe.

Nie wiem ile jeszcze osób Ci napisze, żebyś nadrobił podstawy i dopiero potem brał się za kodowanie rzeczy których na razie nie ogarniasz – ale mam nadzieję, że w końcu to zrobisz :wink:

Co do meritum: order[shipping_address_attributes] nie jest w języku Ruby poprawnym odwołaniem do metody instancji. Co zresztą wypluł Ci interpreter.

Mogę Ci podpowiedzieć: order.shipping_address_attributes, bez ciapków ‘’ i ewentualnie z @ na początku (jeśli @order utworzyłeś w metodzie kontrolera). Ale obiecaj, że teraz pójdziesz i doczytasz, cobyś zrozumiał co tu się dzieje i nie zadawał tak grubych pytań. Bo jak nie, to znajdziemy jakiegoś złego pana moderatora, który wyśle Cię do kąta :wink: