Rails: Verwenden von Build mit einer has_one-Zuordnung in Rails

143

In diesem Beispiel erstelle ich ein usermit no profileund später ein profilefür diesen Benutzer. Ich habe versucht, Build mit einer has_oneAssoziation zu verwenden, aber das hat explodiert. Die einzige Möglichkeit, wie ich das sehe, ist die Verwendung has_many. Der usersoll höchstens einen haben profile.

Ich habe es versucht. Ich habe:

class User < ActiveRecord::Base
  has_one :profile
end

class Profile < ActiveRecord::Base
  belongs_to :user
end

Aber wenn ich es mache:

user.build_profile 

Ich bekomme den Fehler:

ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'profiles.user_id' in 'where clause': SELECT * FROM `profiles` WHERE (`profiles`.user_id = 4)  LIMIT 1

Gibt es eine Möglichkeit in Schienen, 0 oder 1 Assoziation zu haben?

Espinet
quelle
Was genau hast du versucht? Könnten Sie bitte einen Code posten?
Ju Nogueira

Antworten:

359

Die buildMethodensignatur unterscheidet sich für has_oneund has_manyAssoziationen.

class User < ActiveRecord::Base
  has_one :profile
  has_many :messages
end

Die Build-Syntax für die has_manyZuordnung:

user.messages.build

Die Build-Syntax für die has_oneZuordnung:

user.build_profile  # this will work

user.profile.build  # this will throw error

Lesen Sie den has_oneVerein Dokumentation für weitere Details.

Harish Shetty
quelle
28
Die unterschiedliche Syntax für has_one fängt mich immer auf ... verdammt!
Galaxy
11
Es ist lustig, wie die am besten bewertete und akzeptierte Antwort hier eine andere Frage beantwortet als die, die das OP gestellt hat.
Ajedi32
Angeblich, wenn der Benutzer zum Profil gehört (dh die Benutzertabelle enthält die Schlüssel-Profil-ID "Foreign_key"), funktioniert auch das Erstellen eines Profils für den Benutzer wie oben erwähnt, dh für neue Aktionen nur user.build_profile zum Bearbeiten. user.build_profile if user.profile.nil? Wenn Sie beim Erstellen des Benutzers ein Profil erstellen möchten, schreiben Sie accepts_nested_attributes_for :profiledies ein Benutzermodell. und in der Form, in der der Benutzer erstellt wird, schreiben Sie <%= f.simple_fields_for :profile do |p| %>diese und fahren Sie fort.
Eifer
aber warum wurde dieses andere Verhalten für has_one oder has_many beibehalten? Ich denke und erwarte, dass es beim Entwerfen einen Grund gibt.
neugierig
@ Ajedi32 Die Antwort entspricht dem Titel der Frage, aber nicht dem Text. Angesichts der Tatsache, dass dies ( build_<association>) ein ziemlich seltsames und unerwartetes Verhalten in Rails ist, suchen viel mehr Menschen nach dieser Antwort als nach der Antwort der eigentlichen Fragen, wenn Sie wissen, was ich meine.
Max Williams
19

Schauen Sie sich die Fehlermeldung genau an. Es sagt Ihnen, dass Sie keine erforderliche Spalte user_idin der Profiltabelle haben . Das Festlegen der Beziehungen im Modell ist nur ein Teil der Antwort.

Sie müssen auch eine Migration erstellen, die die user_idSpalte zur Profiltabelle hinzufügt . Rails erwartet, dass dies vorhanden ist, und wenn dies nicht der Fall ist, können Sie nicht auf das Profil zugreifen.

Weitere Informationen finden Sie unter folgendem Link:

Grundlagen der Vereinigung

sosborn
quelle
1
Ich habe gerade mein Problem herausgefunden. Das Buch, aus dem ich lerne, hat die Erstellung von Fremdschlüsseln nicht sehr gut erklärt. Ich habe eine neue Migration erstellt, die meinem Modell einen Fremdschlüssel hinzufügt. Vielen Dank.
Espinet
Müssen Sie die Spalte jedes Mal selbst erstellen? Ich hatte die Idee, dass es automatisch passiert. Ich weiß nicht, woher ich diese Idee habe.
Rimian
Sie können die Spalte hinzufügen, wenn Sie ein Modell über die Befehlszeile generieren rails g model profile user:references:index address:string bio:text.
Duykhoa
1

Je nach Anwendungsfall kann es praktisch sein, die Methode zu verpacken und die Zuordnung automatisch zu erstellen, wenn sie nicht gefunden wird.

old_profile = instance_method(:profile)
define_method(:profile) do
  old_profile.bind(self).call || build_profile
end

Wenn Sie jetzt die #profileMethode aufrufen , wird entweder das zugehörige Profil zurückgegeben oder eine neue Instanz erstellt.

Quelle: Können Sie beim Patchen einer Methode durch Affen die überschriebene Methode aus der neuen Implementierung aufrufen?

Shiyason
quelle
1
In aktuellen Schienen (getestet am 6.0.2.2) können Sie dies vereinfachen, um : def profile; super || build_profile; end.
Glasz
-14

Es sollte ein sein has_one. Wenn buildes nicht funktioniert, können Sie einfach Folgendes verwenden new:

ModelName.new( :owner => @owner )

ist das gleiche wie

@owner.model_names.build
Karl
quelle
11
Dies ist nicht dasselbe: Wenn Sie mit build einen neuen Modellnamen erstellen und @owner gespeichert wird, wird auch der neue Modellname gespeichert. Sie können also build verwenden, um Eltern und Kinder zu erstellen, die zusammen gespeichert werden. Dies ist nicht der Fall, wenn Sie einen Modellnamen mit .new
Max Williams