web-dev-qa-db-fra.com

Comment mettre à jour un seul attribut sans l'attribut touch updated_at?

Comment puis-je atteindre cet objectif?

essayé de créer 2 méthodes, appelées 

def disable_timestamps
  ActiveRecord::Base.record_timestamps = false
end

def enable_timestamps
  ActiveRecord::Base.record_timestamps = true
end

et la méthode de mise à jour elle-même:

def increment_pagehit
  update_attribute(:pagehit, pagehit+1)
end

activer et désactiver les horodatages à l'aide de rappels tels que:

before_update :disable_timestamps, :only => :increment_pagehit
after_update :enable_timestamps, :only => :increment_pagehit

mais il ne met rien à jour, même l'attribut souhaité (pagehit).

Aucun conseil? Je ne veux pas avoir à créer une autre table juste pour compter le nombre de pages.

41
Kleber S.
12
apneadiving

Au lieu de update_attribute, vous pouvez utiliser update_column dans Rails 3.1+.

update_attribute ignore les validations, mais touchera updated_at et exécutera des rappels.

update_column ignore les validations, ne touche pas updated_at et n'exécute pas de rappels.

Ainsi, update_column est un excellent choix si vous ne souhaitez pas affecter updated_at et n'avez pas besoin de rappels.

Voir http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html pour plus d'informations.

Notez également que update_column mettra à jour la valeur de l'attribut dans le modèle en mémoire et qu'il ne sera pas marqué comme sale. Par exemple:

p = Person.new(:name => "Nathan")
p.save
p.update_column(:name, "Andrew")
p.name == "Andrew" # True
p.name_changed? # False
88
Nathan

Si tout ce que vous voulez faire, c'est incrémenter un compteur, utilisez plutôt la méthode increment_counter

ModelName.increment_counter :pagehit, id
12
idlefingers

ce n'est pas une bonne idée de faire ceci:

self.class.update_all({ pagehit: pagehit+1 }, { id: id })

cA devrait etre

self.class.update_all("pagehit = pagehit + 1", { id: id })

la raison en est que si deux requêtes sont parallèles, la première version mettra à jour les pageshits avec le même numéro, car elle utilise le numéro enregistré dans la mémoire de Ruby. La deuxième option utilise le serveur SQL pour augmenter le nombre de 1, au cas où deux de ces requêtes arriveraient en même temps, le serveur les traiterait les unes après les autres et se retrouverait avec le nombre correct de requêtes de page.

2
Tom Caspy

Pour éviter Monkeypatchingtroubles, vous pouvez également utiliser ModelName.update_all à cette fin:

def increment_pagehit
  self.class.update_all({ pagehit: pagehit+1 }, { id: id })
end

Cela ne touche pas non plus les horodatages de l'enregistrement.

2
fredostarr

Vous avez également decrement et increment (et leurs versions bang) qui ne modifient pas updated_at, ne déclenchent pas de rappel de validation et sont évidemment pratiques pour les compteurs/entiers.

1
tirdadc

Si la précision n'est pas vraiment importante et que vous ne vous attendez pas à ce que le code soit exécuté plusieurs fois, vous pouvez essayer de modifier la valeur enregistrée dans la base de données updated_at, comme suit:

u = User.first
u.name = "Alex 2" # make some changes...
u.updated_at = u.updated_at + 0.000001.second # alter updated_at
u.save

de sorte que Rails essaiera en réalité de sauvegarder la même valeur et non de la remplacer par Time.now.

0
glampr