2 votes

Sérialisation de l'objet ActiveRecord de Rails, y compris les associations

J'ai un formulaire à soumettre pour un modèle complexe comprenant de nombreuses associations. J'ai besoin de sauvegarder 2 copies de l'objet. L'une ne sera pas touchée et ne servira que de référence pour ce qui a été soumis à l'origine, l'autre sera modifiée par mes utilisateurs.

J'ai d'abord effectué un clonage profond de l'objet et de toutes les associations, puis j'ai enregistré les deux dans la base de données, en marquant un champ pour savoir lequel était l'original, et lequel peut être modifié. Mais cela devient trop complexe et j'aimerais une solution plus simple.

Je pensais que la meilleure solution serait de sérialiser l'objet entier et de le stocker dans un seul champ de l'objet modifiable. Ma question est donc la suivante : est-il possible de sérialiser un objet, y compris les associations, et de le stocker dans un champ unique ?

Dois-je faire cela au niveau du modèle ? ou dois-je simplement enregistrer ce qui a été renvoyé par le formulaire dans un champ unique ? (c'est-à-dire application.original_form = params[:application]).

Merci pour votre aide ! Ryan

EDIT ---

Je tente cette approche :

https://gist.github.com/1298567

J'enregistre les paramètres renvoyés dans un champ serialize.

Quelqu'un voit-il un inconvénient à cela ? Cela semble être l'approche la plus simple.

Merci encore !

Ryan

1voto

Tilo Points 13833

J'ai déjà fait quelque chose de similaire, mais avec des associations profondes à un seul niveau. Par exemple, des comptes qui avaient un ou plusieurs adresses, contacts, etc.

Vous devez vous assurer que vos associations avec d'autres modèles apparentés sont plutôt simples, de sorte qu'il n'y ait pas de boucles ou de pointeurs arrière.

Il est probablement préférable de l'enregistrer de manière à pouvoir la comparer facilement par la suite avec la version actuelle du modèle. Vous devrez exclure le champ "original" de la comparaison ;-)

Lors de la première sauvegarde du modèle, je ferais un clone profond du modèle et de ses associations et je le sauvegarderais dans un champ sérialisé 'original' dans le modèle principal.

Je pense que j'utiliserais le serialize caractéristique d'ActiveRecord, par exemple :

Class YourMainModel
  ...
  serialize :original   # will serialize this ; make this TEXT field in DB
  ...
end

vous voulez vous assurer que le type de données du champ dans la base de données est un texte ou une chaîne, et non un champ binaire ! (cela causerait des bugs plus tard !)

Ensuite, pendant l'after_save du modèle, j'ajouterais du code pour faire quelque chose comme ceci :

# do this in the after_save - so the validations have run:
main_model_object.original ||= main_model_object.deep_clone  # ||= to do this only once
main_model_object.save(:validate => false) if main_model_object.original_changed?  # save if we added the "copy"

Il y a probablement d'autres façons de le faire, mais after_save a l'avantage de permettre l'exécution des validations.

Vous devrez vous assurer que tous les enregistrements associés sont créés au moment de la première sauvegarde, vous pourriez probablement faire un "formulaire monstre" avec Gem nested_forms.

Voir :

https://github.com/moiristo/deep_cloneable (Fourchette pour Rails 3)

https://github.com/openminds/deep_cloning (projet original)

Regardez les autres RailsCasts de Ryan :

http://railscasts.com/episodes/196-nested-model-form-part-1

http://railscasts.com/episodes/197-nested-model-form-part-2

J'espère que cela vous aidera


EDIT :

En y réfléchissant bien, pour limiter la taille de votre base de données et de ses vidages, pour simplifier votre schéma et pour conserver les originaux, vous pourriez stocker les originaux dans un magasin de documents distinct, par exemple en utilisant MongoDB.

Si vous n'avez pas besoin d'accéder fréquemment aux originaux, le stockage dans MongoDB sous la forme d'un document structuré pourrait être très avantageux et votre application principale serait moins compliquée.

Lorsque vous comparez des enregistrements, vous devez cloner profondément votre modèle modifié de la même manière que vous l'avez fait la première fois, puis rechercher l'enregistrement MongoDB via le champ "id" de l'enregistrement original, puis comparer les deux (les deux sont des clones profonds).

Avantage supplémentaire de cette solution : il serait plus difficile de modifier accidentellement l'original, car il n'est pas directement lié à l'enregistrement de la base de données SQL, par exemple, vous ne pourriez pas accidentellement faire object.original = something

1voto

lundie Points 126

J'ai opté pour cette approche :

 def create
    @app = App.new(params[:app])
    @app.original = params[:app]
    respond_to do |format|
      if @app.save
        format.html { redirect_to(done_path(@app.member.id)) }
      else
        format.html { render :action => "new" }
      end
    end
end

#show the original Application
def show
   @app = App.new(App.find(params[:id]).original)
end

#Model

class App < ActiveRecord::Base
  has_one :applicant

  serialize :original, Hash

  accepts_nested_attributes_for :applicant
end

Cela me semble être l'approche la plus simple du problème.

0voto

davidb Points 5208

Je ne l'ai pas encore essayé mais ça a l'air super flippant ! Vous devriez utiliser les colones des instances et les référencer comme ceci :

belongs_to :original_version, :class_name => 'ModelName', :foreign_key => 'original_version_id'
has_one :original_version, :class_name => 'ModelName', :foreign_key => 'original_version_id'
belongs_to :user_version, :class_name => 'ModelName', :foreign_key => 'user_version_id'
has_one :user_version, :class_name => 'ModelName', :foreign_key => 'user_version_id'

Et oui, vous mettez tout cela dans le même modèle et ensuite vous écrivez des méthodes pour vérifier laquelle de ces versions est l'instance.

0voto

Eric G Points 528

J'ai dû faire quelque chose de similaire il y a quelque temps, et j'ai fait de nombreux allers-retours sur ces questions. Je pense que j'ai finalement stocké l'objet graphique modifiable sérialisé, mais en XML. Dans mon cas, les utilisateurs n'éditaient pas vraiment, sauf par rapport à l'objet "modèle" d'origine, donc il n'y avait pas vraiment de raison de ré-instancier le graphe d'objets.

Mais je suis intéressé par les solutions des autres.

Est-il possible d'afficher un résumé ? Je pourrais peut-être donner un retour plus détaillé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X