2009-10-20 20 views
9

Ich habe eine Benutzer-Entität, die ein Feld Aktueller Standort (Stadt und Land) hat. Um diese Informationen zu speichern, habe ich eine Entität namens Location mit has_many Users erstellt.Ruby on Rails: Verschachtelte Attribute, gehört zu

Ich bin nicht ganz sicher, ob ich das Benutzermodell "has_one" oder "gehört_zu" einfügen sollte, aber für was ich lese, wenn ich wollte, dass es den Fremdschlüssel des Ortes hätte, sollte ich "gehört_zu". Ich möchte auch den aktuellen Standort des Benutzers bearbeiten können, wenn ich den Benutzer bearbeite. also verwende ich verschachtelte Attribute. Aber wenn ich den Benutzer bearbeite, beende ich jedes Mal einen neuen Ort, ohne ihn jemals dem bearbeiteten Benutzer zuzuordnen. Kannst du mir helfen?

Mein Code ist folgende:

#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

Und in der Steuerung, die die Daten speichert, was haben Sie? –

+0

ich habe: def aktualisieren @user = @current_user wenn @ user.update_attributes (params [: user]) Blitz: "Konto aktualisiert" [Ankündigung] = redirect_to account_url sonst render: action =>: bearbeiten Ende Ende – simaob

Antwort

9

Das genaue Problem Sie konfrontiert, wie andere haben darauf hingewiesen, dass der Controller nicht empfängt den Standort-ID, wie es sollte. Sieht für mich die Location-ID wird durch den falschen Parameter übergeben. Leider ist eine Standort-ID in einem neuen Datensatz nicht vorhanden, daher ist dies im Formular nicht möglich.

Ihr Problem ergibt sich aus der Verwendung von accresents_nested_attributes_for auf einer Angies_to-Beziehung. Das Verhalten ist nicht klar definiert. Dies scheint ein dokumentierter Fehler zu sein. Also sollte die access_nested_attributes_for auf einer sein oder hat viele Seiten einer Beziehung.

Hier sind einige mögliche Lösungen:

  1. Unterwegs accepted_nested_attributes_for zum Modell Ort und bauen Sie Ihre Formen umgekehrt.

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

    Leider ist dies für eine logische Darstellung von Informationen nicht möglich. Und macht die Bearbeitung der richtigen Benutzer schwierig.

  2. Verwenden Sie ein Join-Modell, und machen Sie eine one: through-Beziehung.

    Ich bin ehrlich gesagt nicht sicher, wie gut accept_nested_attributes_for mit einer: through-Beziehung funktioniert, aber es wird definitiv Ihr Problem mit der Verknüpfung von Datensätzen lösen.

  3. Ignorieren Sie access_nested_attributes_for und behandeln Sie die Zuordnung in Ihrem Controller auf altmodische Weise.

    Eigentlich behalten Sie die accesses_nested_attributes_for. Es bietet einige praktische Komfort-Methoden, aber lassen Sie es nicht zu der update_attributes/create-Anweisung gelangen.

    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 
    

Felder für wird ein ID-Feld in dem current_location_attributes bevölkern Hash automatisch, wenn es nicht einen neuen Standort zu schaffen. Find_or_create_by_id benötigt jedoch einen: id-Eintrag im Hash, damit er funktioniert. Es wird mit einer korrekt automatisch inkrementierten ID erstellt, wenn sich die ID nicht in der Datenbank befindet.Wenn Sie einen neuen Standort erstellen, müssen Sie ihn hinzufügen. Am einfachsten fügen Sie es dem Formular mit hinzu.

Sie können jedoch die Erstellung doppelter Standorte einschränken und die Position Location.find_or_create_by_id in Location.find_or_create_by_location ändern. Dies verringert auch Fehler bei fehlgeschlagenen Eindeutigkeitsprüfungen.

+0

hallo EmFI tks für deine Antwort. Ich wusste nicht, dass es ein bekannter Bug war ... aber ich habe es wirklich versucht Suche nach einer Lösung und habe noch nichts gefunden In Bezug auf die 3 Optionen. 1- Ist in der Tat nicht die beste, für diesen Zweck .. =/Weil ich den Standort eines Benutzers ändern möchte. 2- Ich weiß nicht, wie man das macht, um die Wahrheit zu sagen ... 3- Das schien ziemlich nett und ich habe es versucht, aber es funktioniert nicht. =/Zuerst hieß es, es gäbe kein "delete!" Funktion, aber nach dem Entfernen der "!" Es hatte keine Fehler. Es verbindet den Standort nicht mit dem Benutzer: s Er erstellt ihn einfach und das ist alles ...: s Irgendwelche Ideen? :/ – simaob

+0

Ja, ich nahm an, dass es eine zerstörerische Löschung auf Hash gab. Auch in meiner Eile habe ich vergessen, die Zeile hinzuzufügen, die den Benutzer tatsächlich mit dem Standort verbindet. Ich habe den Fehler korrigiert. – EmFi

+0

Ich weiß nicht, was falsch ist, aber leider scheint es nicht der Trick :(sorry: s – simaob

0

Sie bieten nicht die ID des verschachtelten Attribut. So denkt Schienen, dass es ein neues ist.

- 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

Hallo. Danke für deine schnelle Antwort. Ich habe dieses Feld hinzugefügt, aber das Gleiche passiert weiter. Es fügt der Tabelle einen neuen Standort hinzu. Und es macht nicht die Assoziation für den Benutzer. So zeigt das show.haml nie das current_location :(es ist immer leer. – simaob

0

Nicht sicher, ob die vorherige Antwort wirklich korrekt ist. Sie müssen die ID des Benutzers für den Standort angeben, nicht den Standort selbst.

- 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

Hallo, vielen Dank für Ihre Antwort. Wenn ich dieses versteckte Feld hinzufügen bekomme ich folgende Fehlermeldung: "undefined Methode' user_id 'für # ". Sollte er nicht wegen der" has_many: users "-Beziehung haben? – simaob

+0

oh, sorry, etwas in Ihrem Domain-Modell verwechselt. Sie sollten versuchen, form_edit.hidden_field: location_id –

+0

Hallo nochmal. Versuchte es erneut das hidden_field mit: location_id und: current_location_id ... aber es geht trotzdem weiter: s funktioniert nicht so wie es sein sollte = ( – simaob

0

Standardmäßig belongs_to :current_location, :class_name => 'Location' erwartet die Users Tabelle ein current_location_id Feld haben. Sobald Sie dies haben, sollten Sie in der Lage sein, etwas wie zu tun:

@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!