has_one :through czy :class_name czy jakoś innaczej?

Witam

mam następujący problem, chciałbym aby produkt miał wiele wariantów. Jeden z nich jest główny (master = true) i chciałbym go ustawiać podczas tworzenia/edytowania produktu, a dodawać kolejne normalnie w widoku wariantów dla danego produktu ( product/1/variants )
wariant składa się z pól product_id, master, price

has_many :variants, :dependent => :destroy has_one :price, :class_name => 'Variant', :conditions => "master = true"

.field = f.label :price %br/ = f.text_field :price
Kod wyżej w text_fieldzie zwraca cały obiekt wariantu :confused: a nie tylko cene. Przy edycji/tworzeniu produktu to się sypie.
Jak zrobić to w sensowny dla rubiego i railsów sposób?
Probowałem z :through ale nie wyszło…

No pewnie, że zwraca cały obiekt, dlatego, że przypisujesz mu cały obiekt w asocjacji. Możesz zrobić tak w modelu Product :

has_one :master, :class_name => 'Variant'

Potem będziesz mógł odwoływać sie do tego wariantu poprzez @product.master, np. @product.master.price

Natomiast w formularzu można wówczas zastosować zagnieżdżanie (Railscasty 196 i 197). Generalnie idea jest taka, że w formularzu tworzącym Product od razu utworzysz obiekt klasy Variant, który zostanie przypisany jako master tworzonego produktu.
Jeśli koniecznie chcesz odwoływać się bezpośrednio przez @product.price, to dodaj jakiś callback, który przy aktualizacji ustawi wartość zmiennej price produktu, na taką jak ma jego główny wariant. Osobiście zostałbym jednak przy pierwszej opcji, tzn. odwoływania się @product.master.price

Jeśli chcesz używać np. @product.price to http://apidock.com/rails/Module/delegate będzie jak znalazł. Czyli np.
delegate :price, :to => @master, :prefix => false

To nie działa tak jak bym chciał.

Nie wiem po co mi railscasty 196 i 197. Przy tworzeniu produktu tworzy sie wariant z master => nil. Przy edycji wyswietla mi wszystkie warianty przypisane do danego produktu! a ja chce jedynie master. tak działa fields_for.

W konsoli to dziala. Tylko jak zrobic to w widokach?

Możesz wkleić formularze do tworzenia i edycji produktu?

- f.fields_for :variants do |b| .field = b.label :price %br/ = b.text_field :price
Dla tworzenia i edycji jest to samo jak na razie. Jest to wg railscastow 196 i 197.

:variants powoduje wywołanie metody @product.variants, pewnie dlatego masz wszystkie warianty przy edycji. Jeśli chcesz zawsze edytować tylko master, to możesz to zrobić na dwa sposoby:

  1. w formularzu:

fields_for :master_variant do |variant| variant.hidden_field :master, true variant.text_field :price
w modelu Product:

has_one :master_variant, :class_name => 'Variant', :conditions => {:master => true}
  1. ewentualnie możesz wydelegować metodę price do master_variant:
    w modelu Product:

has_one :master_variant, :class_name => 'Variant', :conditions => {:master => true} delegate :price, :price=, :to => :master_variant
w formularzu:

[code]form_for :product do |f|

pola dla produktu

f.text_field :price[/code]
W tym przypadku będziesz musiał zadbać też o to, żeby master_variant był tworzony w kontrolerze, pewnie w akcji create.

1 metoda w ogole nie działa, ani przy tworzeniu ani przy edycji nie ma wariantu…

2 metoda przy edycji zwraca poprawnie wariant master, ale przy tworzeniu nie ma żadnego pola. w kontrolerze w akcji new probowalem dodac

def new @product = Product.new @product.master_variant.build
co daje bład:
undefined method `build’ for nil:NilClass
a chyba nic innego zrobic sie nie da? bo w akcji new nie bede przeciez tworzył juz obiektów.

Jeśli masz :has_one to metodą nie jest master_variant.build tylko build_master_variant.

Chyba, że w nowych railsach zmienili… :wink:

Dzieki, dalej to nie dziala ale tylko dlatego bo nie mialem czasu nad tym jeszcze posiedzieć :stuck_out_tongue:

No i jedank nie dziala.

[code=fragment widoku]

  • f.fields_for :master_variant do |variant|
    variant.hidden_field :master, true
    .field
    = variant.label :price
    %br/
    = variant.text_field :price[/code]
    w kontrolerze

def new @product = Product.new @product.build_master_variant
a model

has_many :variants, :dependent => :destroy has_one :master_variant, :class_name => 'Variant' , :conditions => {:master => true}
W widoku nie wyswietla sie pole Price, ani podczas tworzenia ani podczas edycji istniejacego produktu z wariantem master.

rails -v
Rails 3.0.0.beta4
ruby -v
ruby 1.9.2dev

Nie wyświetla się pole tekstowe? Czy nie wyświetla się zawartość?
W formularzu powinieneś mieć

= variant.hidden_field :master, :value => true

Udało mi się zrobić to co chcesz w ten sposób:

class Product < ActiveRecord::Base has_many :variants, :dependent => :destroy has_one :master_variant, :class_name => 'Variant', :conditions => {:master => true} accepts_nested_attributes_for :master_variant end

# products_controller.rb def new @product = Product.new @product.build_master_variant end

[code=ruby]- form_for @product do |f|

pola produktu

  • f.fields_for :master_variant, @product.master_variant do |variant|
    = variant.hidden_field :master, :value => true
    %p
    = variant.label :price
    = variant.text_field :price[/code]
    Pisane w Ruby 1.8.7 i Rails 2.3.5, ale nie powinno się jakoś specjalnie różnić od Twojego setupu.

Dzieki wszystkim, w szczególności squilowi. Jednak nie działa to na railsach 3beta4 i ruby 1.9.2 , pewnie jest jakiś inny sposób… Jednak cofnę się do rubiego 1.8.7 i railsow 2.3.8

Rozwiązałem swój problem w rails3 :slight_smile:

[code]= form_for [:admin, @product] do |f|
= f.error_messages

.field
= f.label :name
%br/
= f.text_field :name

= f.fields_for :master_variant, @product.master_variant do |variant|
.field
= variant.hidden_field :master, :value => true
= variant.label :price
%br/
= variant.text_field :price[/code]
Czyli przed fields_for oraz form_for korzystamy z bloków <%= %>, a nie <% %> i teraz działa to na railsach 3beta4 i ruby 1.9.2