Nasty nested_attributes ;-)

Witam.

Jestem chyba zbyt zmęczony żeby dalej szukać rozwiązania i/lub obejścia czy wyjaśnienia więc piszę w nadziei, że ktoś przed takim problemem też stanął.

Do rzeczy…
Konto mamy sobie :

class Account < ActiveRecord::Base belongs_to :company accepts_nested_attributes_for :company end
Dla konta firmę…

class Company < ActiveRecord::Base has_one :account belongs_to :city accepts_nested_attributes_for :city end
A firma posiada sobie miasto

class City < ActiveRecord::Base has_one :company validates_uniqueness_of :name end
Teraz tak… gdy skasuję (validates_uniqueness_of :name) w bazie dostaję

1, Katowice
2, Katowice
3, Katowice

Raczej niepożądana sprawa. Gdy zostawiam validację unikatowości, form nie chce mi się wysłać… bo nazwa nie jest unikatowa… oczywiste. Dlaczego więc ten mechanizm sam nie odkrywa, że w bazie istnieje dany wpis i automatycznie nie przypisuje go dla tego rekordu? Nie za bardzo wiem jak to przeciążyć, miałbym gdzieś w modelu Account sprawdzać już czy city.name istnieje i… jakoś zmieniać mu flagę @new_record na false? Miło by było ale nie dałem rady tego tak obejść.

Brak mi czegoś w modelu?

jeszcze przytoczę mój controller

[code] def add_account
@given_action = “create_account”
@model_form = Account.new
@model_form.build_company
@model_form.build_company.build_city
end

#dla samego dodania
def create_account
if request.post?
@model_form = Account.new(params[:account])
@model_form.password_generate
if @model_form.save
redirect_to :controller => :admin, :action => :accounts
else
#bla bla
end
end[/code]
a z desperacji i widok

[code]- form_for @model_form, :url => { :action => @given_action , :controller => ‘admin’} do |f|

ja używam 2.3.5

@update: czyżbym musiał tutaj zrobić powiązania polimorficzne? Troszkę w AR się gubię…

Asocjacja City - Company powinna być w drugą stronę. Company has one city, city - has many companies. Nie jestem na 100% pewien że tak to ma być w Twojej aplikacji, ale raczej w 99% tak będzie.

Nie rozumiem… has_one => has_many ?? co to za relacja… coś raczej w deseń has_one_and_belongs_to_many… ale tego tez nie widze…

na chłopski rozum belongs_to zawsze musi sie znalezc w tej tabeli gdzie mamy klucz do innej tabeli…

dla mojej bazy mam

Miasta(id, nazwa)
1, Katowice
2, Opole
3. Siedlce

Logiczne wydaje sie uzywanie tego:

Firmy(id, nazwa, id_miasta)
1, Firma henka, 1
2, Firma zbyszka, 1
3, Firma zdzicha, 2

has_many czy has_one w city… nic nie daje…

Woops, sorry no to pojechałem.

City has many companies.
Company belongs to city.

Twoja baza powinna wyglądać tak:

City:

  • id
  • name

Company:

  • id
  • name
  • address etc
  • city_id

[code]class City < ActiveRecord::Base
has_many :companies
end

class Company < ActiveRecord::Base
belongs_to :city
accepts_nested_attributes_for :city
end

#dla

create_table “cities”, :force => true do |t|
t.string “name”, :null => false
end

create_table “companies”, :force => true do |t|
t.string “name”, :null => false
t.string “address”, :null => false
t.string “nip”, :null => false
t.string “regon”, :null => false
t.integer “city_id”, :null => false
t.string “postal_code”, :null => false
t.datetime “created_at”
t.datetime “updated_at”
end[/code]
nadal podaje mi do bazy
1 Katowice
2 Katowice

najbardziej denerwuje mnie fakt że umieszczenie takiego fixa w modelu account nie pomaga… ponieważ before_save uznaje że taki wpis w bazie jest? (before_validation => zadziała, ale takie rozwiązanie mnie nie intereesuje)

[code] before_save :validate_city

#city fix
def validate_city
if City.exists?(:name => self.company.city.name)
self.company.city = City.first(:conditions => [‘name = ?’, self.company.city.name])
end
end[/code]
ten sam kod umieszczony w kontrolerze przed wywołaniem save działa poprawnie i naprawia mój problem

@update: skończyłem na takim oto fixie umieszczonym w Accounts, który jednak nadal mnie nie zadawala (muszę sprawdzać czy cokolwiek nie jest puste bo używam validacji AR poprzez ajax-a) więc jeśli ktoś ma pomysł jak to powinno działać w myśli twórców nested_attributes, to będę wdzięczny za info

[code] before_validation :validate_city

#city fix
def validate_city
unless self.company.nil? || self.company.city.nil? || self.company.city.blank? || self.company.city.name.nil? || self.company.city.name.blank?
if City.exists?(:name => self.company.city.name)
self.company.city = City.first(:conditions => [‘name = ?’, self.company.city.name])
end
end
end[/code]

to powinno zrobić robote

accepts_nested_attributes_for :city, :reject_if => Proc { |c| City.find_by_name(c[:name]) }

[quote=Artur79]to powinno zrobić robote

accepts_nested_attributes_for :city, :reject_if => Proc { |c| City.find_by_name(c[:name]) }

[/quote]
poprawione:

accepts_nested_attributes_for :city, :reject_if => Proc.new { |c| City.find_by_name(c[:name]) }

zmartwię Cię, ale próbowałem, jednak zapomniałem wspomniec o tym… to a i owszem w części by mi odpowiadało… tylko że wtedy nie zostaje uzupełniony city_id w modelu Account… a to powoduje szybki błąd typu:

Mysql::Error: Column 'city_id' cannot be null:

więc rozwiązanie złe…

podpiąłem się pod ticket na lighthouse dotyczący użycia nested_attributes z validates_uniqueness_of…

https://rails.lighthouseapp.com/projects/8994/tickets/2160-nested_attributes-validates_uniqueness_of-fails

idealnym rozwiązaniem byłoby gdyby uruchomienie validates_uniqueness_of wykrywało kontekst (nested_attributes) i wtedy decydowało o pobraniu istniejącego id dla danego pola lub dodaniu nowego rekordu