Schienen: Aktualisieren Sie das Modellattribut, ohne Rückrufe aufzurufen


75

Ich habe ein Benutzermodell mit dem Attribut: Credits. Ich möchte eine einfache Schaltfläche, die den Credits des Benutzers 5 über eine Route mit dem Namen "add" hinzufügt, sodass / users / 3 / add 5 Credits der Benutzer-ID = 3 hinzufügt.

def add
    @user = User.find(params[:id])
    @user.credits += 5
    redirect_to root_path
end

Das ist der relevante Teil meines Controllers. Das Problem ist, dass ich @ user.save nicht aufrufen möchte, da ich einen before_save-Rückruf habe, der das Kennwort des Benutzers basierend auf der aktuellen UTC-Zeit neu verschlüsselt. Ich möchte einfach nur 5 zum Attribut hinzufügen und den Rückruf vermeiden. Ich hätte nie gedacht, dass so eine einfache Sache so schwer sein könnte.

BEARBEITEN:

Ich habe den Rückruf geändert in: before_create, hier ist mein neuer Controller-Code (relevanter Teil):

  def add
    @user = User.find(params[:id])
    @user.add_credits(5)
    @user.save
    flash[:success] = "Credits added!"
    redirect_to root_path
  end

und hier ist mein Code im Modell:

 def add_credits(num)
    self.credits = num
 end

EDIT 2:

Ok, es war ein Validierungsproblem, das dazu führte, dass die Änderungen in "EDIT" nicht funktionierten, aber ich würde trotzdem gerne eine Antwort auf die ursprüngliche Frage der Aktualisierung ohne Rückrufe erhalten!


Ich habe einen Link mit einer Liste der Methoden bereitgestellt, die keine Rückrufe auslösen, und sowohl Finbarr als auch ich haben vorgeschlagen, einen bedingten Rückruf zu verwenden. Welche zusätzlichen Lösungen suchen Sie?
Dave Newton

Antworten:



13

Als allgemeine Antwort ist dies in Rails 4 eine einfache Möglichkeit, Attribute zu aktualisieren, ohne Rückrufe auszulösen:

@user.update_column 'credits', 5

Wenn Sie mehrere Attribute aktualisieren müssen, ohne Rückrufe auszulösen:

@user.update_columns credits: 5, bankrupt: false  

Es gibt andere Optionen hier in den Rails Guides, wenn Sie es vorziehen, aber ich fand diesen Weg am einfachsten.


4
user.update_column Credits: 5 funktioniert nicht. Es sollte user.update_column 'Credits' sein, 5
Richard H Fung

Ich benutze Rails 4.2 und das funktioniert definitiv für mich. Diese Zeile generiert die folgende Abfrage:UPDATE "users" SET "credits" = $1, "updated_at" = $2 WHERE "users"."id" = $3 [["credits", 5], ["updated_at", "2017-04-01 21:34:52.746626"], ["id", 1]]
Matt

8

Um mehrere Attribute ohne Rückruf zu aktualisieren, können Sie update_all in Ihrem Modell folgendermaßen verwenden:

self.class.update_all({name: value, name: value}, self.class.primary_key => id)

Wenn Sie wirklich möchten, können Sie sogar eine update_columns-Methode ausprobieren und diese mit Ihrer aktiven Datensatzbasisklasse mischen.

Um ein Attribut zu aktualisieren, können Sie update_column verwenden. Darüber hinaus finden Sie einige spezifische Methoden in den Schienenhandbüchern http://guides.rubyonrails.org/active_record_callbacks.html#skipping-callbacks



3

Ich denke, Sie sollten in diesem Fall die Methode update_counters verwenden . Verwenden Sie es so in Ihrer Controller-Aktion:

def add
  User.update_counters params[:id], :credits => 5
  redirect_to root_path
end

2

Sie sollten update_all verwenden können , um das Auslösen von Rückrufen zu vermeiden.

def add
 @user = User.find(params[:id])
 User.where(:id=>@user.id).update_all(:credits => @user.credits+5)
 redirect_to root_path
end

Ich würde es vorziehen, diese Logik in das Modell aufzunehmen, aber dies sollte funktionieren, um Ihr ursprüngliches Problem zu lösen, wie es in der Steuerung angegeben ist.



1

Möglicherweise sollte Ihr anderer before_save-Hook prüfen, ob sich das Kennwort des Benutzers tatsächlich geändert hat, bevor Sie es erneut verschlüsseln.


1

Sie haben eine Reihe von Optionen, einschließlich der Änderung des von Ihnen verwendeten Rückrufs, z after_create.

Sie können Spalten aktualisieren, ohne Rückrufe auszulösen (siehe Überspringen von Rückrufen im AR-Handbuch). Löst beispielsweise update_columnkeine Rückrufe aus. Der vorherige Link listet nicht auslösende Funktionen auf.

Sie können auch eines der bedingten Rückrufformulare (oder sogar einen Beobachter) verwenden, wenn das Kennwort geändert wird. Siehe ActiveModel :: Dirty , z @user.password_changed?.


Ok, ich habe es in "after_create" geändert, aber es wird immer noch nicht funktionieren! In meiner Bearbeitung finden Sie meinen neuen Code. Ich kann keine Credits zum Ändern erhalten.
Sam Stern

@hatboysam Nun, es macht etwas anderes - bevor Sie hinzugefügt haben, setzen Sie jetzt. Verwenden save!Sie diese Option, um festzustellen, ob eine Ausnahme vorliegt (und / oder überprüfen Sie die Protokolle und / oder entfernen Sie die Schalldämpfer für die Rückverfolgung).
Dave Newton

0

Sie können dabei eine Spalte aktualisieren

User.where (Name: 'Lilie') .update_all (Alter: '10')

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.