Ich möchte in der Lage sein, einen Datensatz in der Datenbank zu erstellen, dann aber verhindern, dass Rails von diesem Punkt an Änderungen vornimmt. Ich verstehe, dass Änderungen auf DB-Ebene noch möglich sind.
Ich glaube, dass attr_readonly das tut, was ich auf Attributebene will, aber ich möchte keine Felder manuell angeben müssen ... Ich hätte lieber einen White-List-Ansatz.
Ich weiß auch, dass es eine: read_only -Option für Assoziationen gibt, aber ich möchte die "Readonlyness" des Objekts nicht darauf beschränken, ob es über eine Assoziation abgerufen wurde oder nicht.
Schließlich möchte ich noch eine Platte zerstören können, also Sachen wie: abhängige =>: Werke in den Assoziationen zerstören.
Zusammenfassend gilt: 1) Erlaube die Erstellung von Datensätzen, 2) Erlaube das Löschen von Datensätzen und 3) verhindere die Änderung von Datensätzen, die beibehalten wurden.
Beim Betrachten von ActiveRecord::Persistence
ruft alles hinter den Kulissen create_or_update
auf.
def create_or_update
raise ReadOnlyRecord if readonly?
result = new_record? ? create : update
result != false
end
So! Gerade:
def readonly?
!new_record?
end
Ich habe eine etwas prägnantere Lösung gefunden, die den after_initialize
-Callback verwendet:
class Post < ActiveRecord::Base
after_initialize :readonly!
end
Warum erstellen Sie nicht einfach einen Benutzer in der Datenbank, der nur Lesezugriff hat, und dass Rails dieses Konto verwendet.
Wenn Sie jedoch Zugriff auf Modellebene haben möchten, können Sie einem bestimmten Modell Folgendes hinzufügen:
def readonly?
true
end
def before_destroy
raise ActiveRecord::ReadOnlyRecord
end
Dieser Blogeintrag ist noch gültig: http://ariejan.net/2008/08/17/activerecord-read-only-models/
Grundsätzlich können Sie sich auf die Validierung von ActiveRecord verlassen, wenn Sie eine Methode hinzufügen:
def readonly?
true
end
class YourModel < ActiveRecord::Base
before_save { false } # prevent create & update, allows destroy
# ...
end
before_create { false }
before_update { false }
before_destroy { false } # does not prevent delete
Siehe auch: http://guides.rubyonrails.org/active_record_callbacks.html
Dies scheint ziemlich effektiv zu sein und ist wahrscheinlich ein wenig übertrieben, aber für meinen Fall möchte ich wirklich sicherstellen, dass meine Anwendung niemals Datensätze im Modell erstellt, speichert, aktualisiert oder zerstört.
module ReadOnlyModel
def readonly?() true end
def create_or_update() raise ActiveRecord::ReadOnlyRecord end
before_create { raise ActiveRecord::ReadOnlyRecord }
before_destroy { raise ActiveRecord::ReadOnlyRecord }
before_save { raise ActiveRecord::ReadOnlyRecord }
before_update { raise ActiveRecord::ReadOnlyRecord }
end
class MyModel < ActiveRecord::Base
include ReadOnlyModel
# ...
end
Seit OP gebeten wurde, erstellen und zerstören zu können, aber nicht zu speichern oder zu aktualisieren, glaube ich, dass dies funktionieren wird
module SaveAndDestroyOnlyModel
before_save { raise ActiveRecord::ReadOnlyRecord }
before_update { raise ActiveRecord::ReadOnlyRecord }
end
class MyModel < ActiveRecord::Base
include SaveAndDestroyOnlyModel
# ...
end
Nicht genau die richtige Ausnahme, aber nahe genug denke ich.
Ein benutzerdefinierter Prüfer kann dies tun:
validate :nothing_changed, unless: :new_record? # make immutable
...
def nothing_changed
errors.add(:base, "Record is read-only") if self.changed?
end
Suchen nach einem Weg, um dieselbe von @Nate vorgeschlagene Steuerung zu erreichen (jegliche Art von Erstellen/Aktualisieren/Löschen zu vermeiden), aber diese Option nur in bestimmten Teilen meiner Anwendung und für alle Modelle gleichzeitig verwenden. Ich habe diesen Ruby erstellt Verfeinerung :
module ReadOnlyRailsMode
CLASS_METHODS = ActiveRecord::Base.methods
.select { |m| m =~ /(update|create|destroy|delete|save)[^\?]*$/ }
INSTANCE_METHODS = ActiveRecord::Base.instance_methods
.select { |m| m =~ /(update|create|destroy|delete|save)[^\?]*$/ }
refine ActiveRecord::Base.singleton_class do
CLASS_METHODS.each do |m|
define_method(m) do |*args|
raise ActiveRecord::ReadOnlyRecord
end
end
end
refine ActiveRecord::Base do
def readonly?; true; end
INSTANCE_METHODS.each do |m|
define_method(m) do |*args|
raise ActiveRecord::ReadOnlyRecord
end
end
end
end
Und um es nur in einem bestimmten Teil des Codes zu verwenden:
class MyCoolMailerPreview < ActionMailer::Preview
using ReadOnlyRailsMode
end
(Dies ist ein echter Anwendungsfall. Ich habe nach einer Möglichkeit gesucht, Personen zu vermeiden, die in ActionMailer :: Previews echte Datensätze erstellen und bearbeiten, weil ich Vorschauen in der Produktion zulassen möchte. Wenn jedoch aus Versehen eine Vorschau erstellt wird, die echte Daten verändert, das würde ein Chaos werden).
Der Code ist ein wenig hässlich und definiert alle Methoden neu (create, create! Usw.), da das Verhalten aller Modelle geändert werden soll und Rückrufe wie "before_create" nicht für diesen Zweck verwendet werden können, da sie nicht nur lokal vorliegen würden im Bereich "using", Ändern der gesamten Anwendung.
Dieser Ansatz funktioniert für mich, ich kann alle diese Methoden für alle Modelle in nur einer Klasse explizit blockieren und den Rest der Anwendung nicht verwirren. Leider gelten Verfeinerungen bisher nicht für Unterklassen. Daher konnte ich in meinem Fall nicht standardmäßig alle Einfügungen in die übergeordnete Klasse (ActionMailer :: Preview) blockieren. Dies war mein ursprüngliches Ziel, aber das Blockieren pro Klasse ist dies ein guter Ausgangspunkt.
Meine Anwendung erfordert eine Verfeinerung aller Methoden, aber die Steuerung kann nur für die interessanten Methoden wie Zerstören oder Aktualisieren durchgeführt werden, und dies kann für alle Fälle funktionieren, auch für den Fall der ursprünglichen Frage.