it-swarm.com.de

Gibt es eine einfache Möglichkeit, ein Rails ActiveRecord-Modell schreibgeschützt zu machen?

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. 

55
brettish

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
73
scragz

Ich habe eine etwas prägnantere Lösung gefunden, die den after_initialize-Callback verwendet:

class Post < ActiveRecord::Base
  after_initialize :readonly!
end
46
kwarrick

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
25
Mike Lewis

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
16
apneadiving

TL; DR für OPs

class YourModel < ActiveRecord::Base
  before_save { false } # prevent create & update, allows destroy

  # ... 
end

Allgemein

  • Um das Erstellen nur zu verhindern: before_create { false } 
  • Nur Updates verhindern: before_update { false }
  • Um nur Zerstörungen zu verhindern: before_destroy { false } # does not prevent delete

Siehe auch: http://guides.rubyonrails.org/active_record_callbacks.html

4
Barry

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.

2
Nate

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
0
Burnzoire

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.

0