2009-10-20 17 views
9

Mam jednostkę użytkownika, która ma pole Bieżąca lokalizacja (miasto i kraj). Aby przechowywać te informacje, utworzyłem encję o nazwie Location, która ma wielu użytkowników.Ruby na szynach: Zagnieżdżone atrybuty, belongs_to relacja

Nie jestem do końca pewien, czy powinienem wstawić model użytkownika "has_one" lub "belongs_to", ale dla tego, co przeczytałem, gdybym chciał, aby miał klucz obcy lokalizacji, powinienem umieścić "belongs_to". Chcę także móc edytować bieżącą lokalizację użytkownika podczas edycji użytkownika. więc używam atrybutów zagnieżdżonych. Ale kiedy edytuję użytkownika, za każdym razem dodam nową lokalizację, nigdy nie kojarząc jej z edytowanym użytkownikiem. Czy możesz mi pomóc?

Mój kod jest następujący:

#User Model 
class User < ActiveRecord::Base 
    ## Relationships 
    belongs_to :current_location, :class_name => 'Location' 
    accepts_nested_attributes_for :current_location 
end 

#Location Model 
class Location < ActiveRecord::Base 
    #Relationship 
    has_many :users 
end 

# part of the _form_edit.haml 
- form_edit.fields_for :current_location do |location_form| 
    = location_form.label :location, "Current Location" 
    = location_form.text_field :location 

#Application Helper 
#nested attributes for user and location 
def setup_user(user) 
    returning(user) do |u| 
    u.build_current_location if u.current_location.nil? 
    end 
end 

#in the user controller (added after edit) 
def update 
    @user = @current_user 
    if @user.update_attributes(params[:user]) 
     flash[:notice] = "Account updated!" 
     redirect_to account_url 
    else 
     render :action => :edit 
    end 
    end 
+0

A w kontrolerze, który zapisuje dane, co masz? –

+0

mam: def aktualizować @user = @current_user jeśli @ user.update_attributes (params [: użytkownika]) błysku: "Konto aktualizowane" [Zawiadomienie] = redirect_to account_url else render: action =>: edit koniec koniec – simaob

Odpowiedz

9

Dokładny problem, z którym mierzysz się, o czym inni zwrócili uwagę, to to, że kontroler nie otrzymuje identyfikatora lokalizacji tak, jak powinien. Wygląda na to, że identyfikator lokalizacji jest przekazywany za pomocą niewłaściwego parametru. Niestety identyfikator lokalizacji nie istnieje w nowym rekordzie, więc nie jest to możliwe w formularzu.

Twój problem wynika z użycia accepts_nested_attributes_for w relacji belongs_to. Zachowanie nie jest jasno zdefiniowane. Wygląda na udokumentowany błąd. A więc accepts_nested_attributes_for powinno znajdować się na jednym lub ma wiele stron relacji.

Oto kilka możliwych rozwiązań:

  1. przesunąć accepted_nested_attributes_for do modelu lokalizacji i budowy formularzy na odwrót.

    -form_for @location do |location_form| 
    ... 
    =location_form.fields_for @user do |user_form| 
        .... 
    

    Niestety nie pozwala to na logiczny sposób prezentacji informacji. I utrudnia edytowanie odpowiedniego użytkownika.

  2. Skorzystaj z modelu łączenia i utwórz połączenie jeden: przez.

    Szczerze mówiąc, nie jestem pewien, jak dobrze accept_nested_attributes_for działa z relacją: through, ale z pewnością rozwiąże problem z łączeniem rekordów.

  3. Ignoruje accepts_nested_attributes_for i obsługuje powiązanie w kontrolerze w staroświecki sposób.

    Właściwie zachowaj accepts_nested_attributes_for. Zapewnia kilka wygodnych metod, po prostu nie pozwól, aby dostał się do instrukcji update_attributes/create.

    def update 
        @user = @current_user 
        completed = false 
        location_params = params[:user].delete(:current_location_attributes) 
    
        User.transaction do 
        @location = Location.find_or_create_by_id(location_params) 
        @user.update_attributes(params[:user]) 
        @user.current_location = @location 
        @user.save! 
        completed = true 
        end 
        if completed 
        flash[:notice] = "Account updated!" redirect_to account_url 
        else 
        render :action => :edit 
        end 
    end 
    

Pola dla wypełni pole ID w current_location_attributes hash automatycznie, jeśli nie jest stworzenie nowej lokalizacji. Jednak find_or_create_by_id wymaga wpisu: id w hashu, aby działał. Zostanie utworzony z poprawnie automatycznie zwiększanym identyfikatorem, jeśli identyfikator nie znajduje się w bazie danych.Jeśli tworzysz nową lokalizację, musisz ją dodać. Najprościej dodaj go do formularza za pomocą =location_form.hidden_field :id, 0 unless current\_location.new\_record?.

Można jednak ograniczyć duplikowanie tworzenia lokalizacji i zmienić wiersz Location.find_or_create_by_id na Location.find_or_create_by_location. Spowoduje to również ograniczenie wszelkich błędów związanych z błędnymi zatwierdzeniami unikalności.

+0

cześć EmFI tks do twojej odpowiedzi Nie wiedziałem, że to znany błąd ... ale naprawdę próbowałem szukaj rozwiązania i niczego nie znalazłeś Odnośnie 3 opcji. 1- Czy rzeczywiście nie jest najlepszy, w tym celu .. =/Ponieważ chcę zmienić lokalizację użytkownika. 2- Nie wiem, jak to zrobić, aby powiedzieć prawdę ... 3- To wydawało się całkiem miłe i próbowałem, ale to nie działa. =/Na początku powiedział, że nie było "usunięcia!" funkcja, ale po usunięciu "!" nie ma żadnych błędów. Po prostu nie kojarzy lokalizacji z użytkownikiem: s Po prostu tworzy i to wszystko ...: s Jakieś pomysły? :/ – simaob

+0

Tak, założyłem, że na Hash było niszczycielskie usunięcie. Również w moim pośpiechu, zapomniałem dodać linię, która faktycznie kojarzy użytkownika z lokalizacją. Poprawiłem błąd. – EmFi

+0

Nie wiem, co jest nie tak, ale niestety to nie działa :(sorry: s – simaob

0

Nie zapewniają id zagnieżdżonego atrybutu. Więc tory sądzą, że to nowy.

- form_edit.fields_for :current_location do |location_form| 
    = location_form.label :location, "Current Location" 
    = location_form.text_field :location 
    = location_form.hidden_field :id unless location_form.new_record? 
+0

Witaj. Dziękuję za szybką odpowiedź. Dodałem to pole, ale to samo się dzieje. Dodaje nową lokalizację do tabeli. I nie tworzy powiązania dla użytkownika. Tak więc show.haml nigdy nie pokazuje aktualnej lokalizacji :(jest zawsze pusta – simaob

0

Nie wiem, czy poprzednia odpowiedź jest rzeczywiście poprawna. Musisz określić identyfikator użytkownika dla danej lokalizacji, a nie samą lokalizację.

- form_edit.fields_for :current_location do |location_form| 
    = location_form.label :location, "Current Location" 
    = location_form.text_field :location 
    = location_form.hidden_field :user_id 
+0

Witam, dziękuję za odpowiedź Po dodaniu tego ukrytego pola pojawia się następujący komunikat o błędzie: "niezdefiniowana metoda" id_użytkownika "dla # ". Czy nie powinien go mieć ze względu na relację" has_many: users "? – simaob

+0

oh, przepraszam, pomieszam coś w twoim modelu domeny Powinieneś spróbować formularza_edit.hidden_field: id_połeczności –

+0

Cześć jeszcze raz.spróbowałem ponownie hidden_field z: location_id i: current_location_id ... ale mimo to kontynuuje to samo: s nie działa tak, jak powinno = ( – simaob

0

Domyślnie belongs_to :current_location, :class_name => 'Location' będzie oczekiwać, że tabela Users ma pole current_location_id. Gdy to zrobisz, powinieneś być w stanie zrobić coś w rodzaju:

@user = @current_user 
@user.update_attributes(params[:user]) 

@location = @user.current_location or @user.build_current_location 
@location.update_attributes(params[:location]) 

@user.current_location.save! 
@user.save!