Wie kann ich den Standardwert in ActiveRecord festlegen?
Ich sehe einen Beitrag von Pratik, der einen hässlichen, komplizierten Codeabschnitt beschreibt: http://m.onkey.org/2007/7/24/how-to-set-default-values-in-your-model
class Item < ActiveRecord::Base
def initialize_with_defaults(attrs = nil, &block)
initialize_without_defaults(attrs) do
setter = lambda { |key, value| self.send("#{key.to_s}=", value) unless
!attrs.nil? && attrs.keys.map(&:to_s).include?(key.to_s) }
setter.call('scheduler_type', 'hotseat')
yield self if block_given?
end
end
alias_method_chain :initialize, :defaults
end
Ich habe die folgenden Beispiele gesehen, die herum gegoogelt haben:
def initialize
super
self.status = ACTIVE unless self.status
end
und
def after_initialize
return unless new_record?
self.status = ACTIVE
end
Ich habe auch Leute gesehen, die es in ihre Migration aufgenommen haben, aber ich würde es lieber im Modellcode definiert sehen.
Gibt es eine kanonische Möglichkeit, den Standardwert für Felder im ActiveRecord-Modell festzulegen?
Antworten:
Bei jeder der verfügbaren Methoden gibt es mehrere Probleme, aber ich glaube, dass das Definieren eines
after_initialize
Rückrufs aus folgenden Gründen der richtige Weg ist:default_scope
initialisiert Werte für neue Modelle, aber dann wird dies der Bereich, in dem Sie das Modell finden. Wenn Sie nur einige Zahlen auf 0 initialisieren möchten, ist dies nicht das, was Sie wollen.initialize
kann funktionieren, aber vergessen Sie nicht anzurufensuper
!after_initialize
ist ab Rails 3 veraltet . Wenn ichafter_initialize
in Rails 3.0.3 überschreibe, wird in der Konsole die folgende Warnung angezeigt:Daher würde ich sagen, schreiben Sie einen
after_initialize
Rückruf, mit dem Sie zusätzlich zu den Standardattributen Standardattribute festlegen können:Jetzt haben Sie nur noch einen Ort, an dem Sie nach der Initialisierung Ihrer Modelle suchen können. Ich benutze diese Methode, bis jemand eine bessere findet.
Vorsichtsmaßnahmen:
Für boolesche Felder gilt Folgendes:
self.bool_field = true if self.bool_field.nil?
Weitere Einzelheiten finden Sie in Paul Russells Kommentar zu dieser Antwort
Wenn Sie nur eine Teilmenge von Spalten für ein Modell auswählen (dh
select
in einer Abfrage wiePerson.select(:firstname, :lastname).all
) verwenden, erhalten Sie eine,MissingAttributeError
wenn Ihreinit
Methode auf eine Spalte zugreift, die nicht in derselect
Klausel enthalten ist. Sie können sich wie folgt gegen diesen Fall schützen:self.number ||= 0.0 if self.has_attribute? :number
und für eine boolesche Spalte ...
self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?
Beachten Sie auch, dass die Syntax vor Rails 3.2 anders ist (siehe den Kommentar von Cliff Darling unten).
quelle
initialize
, scheint nur für etwas wirklich kompliziert zu sein, das klar und klar definiert sein sollte. Ich habe Stunden damit verbracht, durch die Dokumentation zu kriechen, bevor ich hier gesucht habe, weil ich angenommen habe, dass diese Funktionalität bereits irgendwo vorhanden ist und ich es einfach nicht wusste.self.bool_field ||= true
, da dies das Feld auf true erzwingt, selbst wenn Sie es explizit auf false initialisieren. Stattdessen tunself.bool_field = true if self.bool_field.nil?
.MissingAttributeError
. Sie können eine zusätzliche Prüfung wieself.number ||= 0.0 if self.has_attribute? :number
folgt hinzufügen: Für Boolesche Werte :self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?
. Dies ist Rails 3.2+ - für frühere Verwendungself.attributes.has_key?
, und Sie müssen eine Zeichenfolge anstelle eines Symbols verwenden.initialize
mitreturn if !new_record?
, um Leistungsprobleme zu vermeiden.Schienen 5+
Sie können die Attributmethode in Ihren Modellen verwenden, z.
Sie können dem
default
Parameter auch ein Lambda übergeben . Beispiel:quelle
Value
und es wird keine Typumwandlung durchgeführt.store_accessor :my_jsonb_column, :locale
Sie dann definieren könnenattribute :locale, :string, default: 'en'
nil
. Wenn sie nichtnil
DBnot null
+ DB default + sein können, sind github.com/sshaw/keep_defaults meiner Erfahrung nach derWir setzen die Standardwerte durch Migrationen in die Datenbank (indem wir die
:default
Option für jede Spaltendefinition angeben) und lassen Active Record diese Werte verwenden, um den Standard für jedes Attribut festzulegen.IMHO, dieser Ansatz ist auf die Prinzipien von AR ausgerichtet: Konvention über Konfiguration, DRY, die Tabellendefinition treibt das Modell an, nicht umgekehrt.
Beachten Sie, dass sich die Standardeinstellungen weiterhin im Anwendungscode (Ruby) befinden, jedoch nicht im Modell, sondern in den Migrationen.
quelle
Einige einfache Fälle können durch Definieren eines Standardwerts im Datenbankschema behandelt werden, dies behandelt jedoch nicht eine Reihe schwierigerer Fälle, einschließlich berechneter Werte und Schlüssel anderer Modelle. Für diese Fälle mache ich Folgendes:
Ich habe mich für after_initialize entschieden, möchte aber nicht, dass es auf Objekte angewendet wird, die nur neu oder erstellt gefunden werden. Ich finde es fast schockierend, dass für diesen offensichtlichen Anwendungsfall kein after_new-Rückruf bereitgestellt wird, aber ich habe dies getan, indem ich bestätigt habe, ob das Objekt bereits beibehalten wurde, um anzuzeigen, dass es nicht neu ist.
Nachdem Brad Murrays Antwort gesehen wurde, ist dies noch sauberer, wenn die Bedingung in eine Rückrufanforderung verschoben wird:
quelle
:before_create
?Das Rückrufmuster after_initialize kann durch einfaches Ausführen der folgenden Schritte verbessert werden
Dies hat einen nicht trivialen Vorteil, wenn Ihr Init-Code sich mit Assoziationen befassen muss, da der folgende Code ein subtiles n + 1 auslöst, wenn Sie den ursprünglichen Datensatz lesen, ohne den zugehörigen einzuschließen.
quelle
Die Phusion-Jungs haben ein nettes Plugin dafür.
quelle
:default
Werte in Schemamigrationen "nur funktionieren" könnenModel.new
.:default
Werte in Migrationen dazu bringen, "nur zu arbeiten"Model.new
, im Gegensatz zu dem, was Jeff in seinem Beitrag gesagt hat. Verifiziertes Arbeiten in Rails 4.1.16.Ein noch besserer / sauberer möglicher Weg als die vorgeschlagenen Antworten besteht darin, den Accessor wie folgt zu überschreiben:
Siehe "Überschreiben von Standard-Accessoren" in der ActiveRecord :: Base-Dokumentation und mehr von StackOverflow zur Verwendung von self .
quelle
attributes
. In Schienen getestet 5.2.0.Ich benutze den
attribute-defaults
EdelsteinAus der Dokumentation: Führen
sudo gem install attribute-defaults
Sierequire 'attribute_defaults'
Ihre App aus und fügen Sie sie hinzu.quelle
Ähnliche Fragen, aber alle haben einen leicht unterschiedlichen Kontext: - Wie erstelle ich einen Standardwert für Attribute im Modell von Rails activerecord?
Beste Antwort: Kommt darauf an, was du willst!
Wenn Sie möchten, dass jedes Objekt mit einem Wert beginnt: Verwenden Sie
after_initialize :init
Sie möchten, dass das
new.html
Formular beim Öffnen der Seite einen Standardwert hat? Verwenden Sie https://stackoverflow.com/a/5127684/1536309Wenn Sie möchten, dass für jedes Objekt ein Wert aus Benutzereingaben berechnet wird: use
before_save :default_values
Sie möchten, dass der Benutzer Folgendes eingibt,X
und dannY = X+'foo'
? verwenden:quelle
Dafür sind Konstruktoren da! Überschreiben Sie dieinitialize
Methode des Modells .Verwenden Sie die
after_initialize
Methode.quelle
after_initialize
stattdessen die Methode verwenden.Sup Jungs, am Ende habe ich folgendes gemacht:
Klappt wunderbar!
quelle
Dies wurde lange beantwortet, aber ich benötige häufig Standardwerte und ziehe es vor, sie nicht in die Datenbank aufzunehmen. Ich mache mir
DefaultValues
Sorgen:Und dann benutze es in meinen Modellen so:
quelle
Der kanonische Rails-Weg vor Rails 5 bestand darin, ihn in der Migration festzulegen und nur in den zu schauen
db/schema.rb
festzulegen und nachzuschauen, wann immer Sie sehen möchten, welche Standardwerte von der Datenbank für ein Modell festgelegt werden.Im Gegensatz zu den Antwortzuständen von @Jeff Perrin (die etwas alt sind) wendet der Migrationsansatz bei Verwendung sogar die Standardeinstellung an
Model.new
aufgrund einiger Rails-Magie . Verifiziertes Arbeiten in Rails 4.1.16.Das Einfachste ist oft das Beste. Weniger Wissensschulden und potenzielle Verwirrungspunkte in der Codebasis. Und es funktioniert einfach.
Oder führen Sie für einen Spaltenwechsel, ohne einen neuen zu erstellen, Folgendes aus:
Oder vielleicht sogar noch besser:
Überprüfen Sie den offiziellen RoR-Leitfaden auf Optionen für Spaltenänderungsmethoden .
Das
null: false
erlaubt NULL-Werte in der Datenbank nicht und wird als zusätzlichen Vorteil auch aktualisiert, sodass alle bereits vorhandenen DB-Datensätze, die zuvor null waren, ebenfalls mit dem Standardwert für dieses Feld festgelegt werden. Sie können diesen Parameter in der Migration ausschließen, wenn Sie möchten, aber ich fand ihn sehr praktisch!Der kanonische Weg in Rails 5+ ist, wie @Lucas Caton sagte:
quelle
Das Problem bei den after_initialize-Lösungen besteht darin, dass Sie jedem einzelnen Objekt, das Sie in der Datenbank suchen, eine after_initialize hinzufügen müssen, unabhängig davon, ob Sie auf dieses Attribut zugreifen oder nicht. Ich schlage einen faulen Ansatz vor.
Die Attributmethoden (Getter) sind natürlich selbst Methoden, sodass Sie sie überschreiben und einen Standard angeben können. Etwas wie:
Es sei denn, wie jemand darauf hingewiesen hat, müssen Sie Foo.find_by_status ('ACTIVE') ausführen. In diesem Fall müssten Sie den Standard in Ihren Datenbankeinschränkungen wirklich festlegen, wenn die Datenbank dies unterstützt.
quelle
Ich lief in Probleme mit
after_initialize
gebenActiveModel::MissingAttributeError
Fehler bei komplexen Funde tun:z.B:
"Suche" im
.where
Hash der BedingungenAm Ende habe ich die Initialisierung folgendermaßen überschrieben:
Der
super
Aufruf ist erforderlich, um sicherzustellen, dass das Objekt korrekt initialisiert wurde,ActiveRecord::Base
bevor Sie meinen Anpassungscode ausführen, dh: default_valuesquelle
def initialize(*); super; default_values; end
in Rails 5.2.0 tun . Außerdem ist der Standardwert auch im.attributes
Hash verfügbar .quelle
Obwohl dies in den meisten Fällen verwirrend und umständlich ist, um Standardwerte festzulegen, können Sie dies auch verwenden
:default_scope
. Lesen Sie hier den Kommentar von squil .quelle
Nachdem die Methode after_initialize veraltet ist, verwenden Sie stattdessen den Rückruf.
Die Verwendung von : default in Ihren Migrationen ist jedoch immer noch der sauberste Weg.
quelle
after_initialize
Methode ist NICHT veraltet . Tatsächlich ist der Rückruf im Makro-Stil, den Sie als Beispiel für IS angeben, veraltet . Details : guides.rubyonrails.org/…Ich habe festgestellt, dass die Verwendung einer Validierungsmethode viel Kontrolle über das Festlegen von Standardeinstellungen bietet. Sie können sogar Standardeinstellungen (oder eine Fehlerüberprüfung) für Updates festlegen. Sie haben sogar einen anderen Standardwert für Einfügungen als für Aktualisierungen festgelegt, wenn Sie dies wirklich wollten. Beachten Sie, dass die Standardeinstellung erst festgelegt wird, wenn #valid? wird genannt.
Beim Definieren einer after_initialize-Methode können Leistungsprobleme auftreten, da after_initialize auch von jedem zurückgegebenen Objekt aufgerufen wird von: find: http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find
quelle
new
Wiederverwendung der Aktion zu behalten .Wenn es sich bei der Spalte zufällig um eine Spalte vom Typ "Status" handelt und Ihr Modell für die Verwendung von Zustandsautomaten geeignet ist , sollten Sie das Aasm-Juwel verwenden. Anschließend können Sie dies einfach tun
Der Wert für nicht gespeicherte Datensätze wird immer noch nicht initialisiert, aber es ist ein bisschen sauberer, als Ihren eigenen mit
init
oder was auch immer zu rollen , und Sie profitieren von den anderen Vorteilen von aasm, wie z. B. Gültigkeitsbereichen für alle Ihre Status.quelle
https://github.com/keithrowell/rails_default_value
quelle
Ich empfehle dringend, das Juwel "default_value_for" zu verwenden: https://github.com/FooBarWidget/default_value_for
Es gibt einige knifflige Szenarien, bei denen die Initialisierungsmethode, die dieses Juwel ausführt, überschrieben werden muss.
Beispiele:
Ihr DB-Standard ist NULL, Ihr vom Modell / Ruby definierter Standard ist "irgendein String", aber Sie möchten es tatsächlich den Wert aus irgendeinem Grund auf Null setzen:
MyModel.new(my_attr: nil)
Die meisten Lösungen hier setzen den Wert nicht auf Null und setzen ihn stattdessen auf den Standardwert.
OK, anstatt den
||=
Ansatz zu wählen, wechseln Sie zumy_attr_changed?
...ABER stellen Sie sich jetzt vor, Ihr DB-Standard ist "irgendein String", Ihr vom Modell / Ruby definierter Standard ist "irgendein anderer String", aber unter einem bestimmten Szenario möchten Sie den Wert auf "irgendeinen String" setzen (der DB-Standard):
MyModel.new(my_attr: 'some_string')
Dies führt dazu
my_attr_changed?
, dass der Wert falsch ist, da der Wert mit dem Standardwert von db übereinstimmt, wodurch Ihr von Ruby definierter Standardcode ausgelöst und der Wert auf "eine andere Zeichenfolge" gesetzt wird - wiederum nicht das, was Sie gewünscht haben.Aus diesen Gründen denke ich nicht, dass dies mit nur einem after_initialize-Hook richtig erreicht werden kann.
Auch hier denke ich, dass das Juwel "default_value_for" den richtigen Ansatz verfolgt: https://github.com/FooBarWidget/default_value_for
quelle
Hier ist eine Lösung, die ich verwendet habe und die mich ein wenig überrascht hat. Sie wurde noch nicht hinzugefügt.
Es gibt zwei Teile. Im ersten Teil wird die Standardeinstellung für die eigentliche Migration festgelegt, und im zweiten Teil wird dem Modell eine Validierung hinzugefügt, um sicherzustellen, dass die Anwesenheit wahr ist.
Hier sehen Sie also, dass der Standard bereits festgelegt ist. Jetzt möchten Sie in der Validierung sicherstellen, dass immer ein Wert für die Zeichenfolge vorhanden ist
Dadurch wird der Standardwert für Sie festgelegt. (Für mich habe ich "Willkommen im Team"), und dann geht es noch einen Schritt weiter und stellt sicher, dass für dieses Objekt immer ein Wert vorhanden ist.
Ich hoffe, das hilft!
quelle
quelle
Verwenden Sie default_scope in Schienen 3
api doc
ActiveRecord verdeckt den Unterschied zwischen den in der Datenbank definierten Standardeinstellungen (Schema) und den in der Anwendung (Modell) vorgenommenen Standardeinstellungen. Während der Initialisierung wird das Datenbankschema analysiert und alle dort angegebenen Standardwerte notiert. Später beim Erstellen von Objekten werden diese schemaspezifischen Standardwerte zugewiesen, ohne die Datenbank zu berühren.
Diskussion
quelle
default_scope
. Dadurch fügen alle Ihre Abfragen diese Bedingung zu dem von Ihnen festgelegten Feld hinzu. Es ist fast NIE was du willst.In den API-Dokumenten http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html Verwenden Sie die
before_validation
Methode in Ihrem Modell. Sie haben die Möglichkeit, eine spezifische Initialisierung für das Erstellen und Aktualisieren von Aufrufen zu erstellen, z. B. in diesem Beispiel (erneut Code verwendet) Aus dem API-Dokumentbeispiel wird das Nummernfeld für eine Kreditkarte initialisiert. Sie können dies einfach anpassen, um die gewünschten Werte festzulegenÜberrascht, dass sein hier nicht vorgeschlagen wurde
quelle