2009-09-10 5 views
4

Je rencontre des difficultés pour créer une nouvelle ligne de modèle dans la base de données à l'aide d'ActiveRecord dans une application Sinatra que je développe. L'objet en question est créé sans aucune erreur (en utilisant save !, aucune exception n'est déclenchée), mais la plupart des données que je spécifie pour l'enregistrement ne sont pas présentes.Problème lors de la création du modèle ActiveRecord: données manquantes de la sauvegarde

class ProjectMeta < ActiveRecord::Base 
    attr_accessor :completion_ratio, :num_stories, :num_completed_stories, :original_target_date, :current_target_date 

    ... 

    def self.create_from_project(project) 
     meta = ProjectMeta.new 
     meta.project_id = project.id 
     meta.num_stories = project.num_stories 
     meta.num_completed_stories = project.num_completed_stories 
     meta.completion_ratio = ProjectMeta.calculate_ratio(project.num_completed_stories, project.num_stories) 
     meta.current_target_date = project.current_target_date 
     meta.save! 
     meta 
    end 

    ... 

end 

Toutes les inspections sur les données de l'objet de projet, je vous envoie, ainsi que le nouvel objet méta je créais montrent que les données sont présentes. Mais quand je fais un meta.inspect avant et après la sauvegarde, cela montre que toutes les données (sauf project_id) sont dans leur état par défaut (zéros). J'ai également vérifié meta.errors.nil? et bien sûr, il n'y a pas d'erreurs après la sauvegarde. Ce qui est le plus étonnant, c'est que si je me retourne et que j'obtiens une nouvelle méta-instance avec ce project_id et que j'entre les données, cela ne gêne pas la base de données.

Cela me frustre parce que j'ai construit plusieurs sites dans Rails et Sinatra avec ActiveRecord. Ce seul problème me laisse complètement perplexe. Quelqu'un peut-il me dire ce que je fais mal?

+0

J'ai oublié de mentionner que j'ai essayé toutes les incantations de création auxquelles je peux penser: ProjectMeta.create (: project_id => project.id ...), ProjectMeta.create do ... end, etc ... – localshred

Répondre

6

Voici comment cela fonctionne

  1. Lors du premier accès au modèle, les colonnes de la table de base de données correspondante sont retri eved et stocké à l'intérieur des données du modèle. Cette information peut être récupérée par la méthode de classe :: columns. Lorsque vous accédez à l'attribut d'un modèle, Ruby ne trouve pas la méthode correspondante dans la classe et lance la méthode #method_missing. Cette méthode inspecte le modèle :: columns pour vérifier si la colonne correspondante existe. Si c'est le cas, il crée un accesseur pour cette colonne afin que la prochaine fois que vous accédez à l'attribut de ce modèle, une méthode accesseur sera appelée directement, sans avoir besoin d'appeler #method_missing (le dernier est plus lent).

Les accesseurs ressemblent à ceci:

def my_attribute 
    read_attribute(:my_attribute) 
end 

def my_attribute=(value) 
    write_attribute(:my_attribute, value) 
end 

Pour les méthodes de #read_attribute et #write_attribute il y a un raccourci: # [] et # []=. Si, pour une raison quelconque, vous devez accéder directement aux données sous-jacentes (par ex.faire une conversion de données), vous pouvez les écrire court:

def my_attribute 
    self[:my_attribute] 
end 

def my_attribute=(value) 
    self[:my_attribute] = value 
end 

modèle a un accesseur spécial - # attributes - qui retourne un "nom_colonne => valeur" Hash. REMARQUE: les données de chaque colonne sont stockées dans une instance Hash spéciale dans votre instance de modèle, et non dans les variables d'instance "@column_name". Lorsque vous définissez des accesseurs aveC#attr_accessor, vous bloquez la façon habituelle de définir les accesseurs d'attribut via #method_missing. Vos données sont stockées dans des variables d'instance au lieu du hachage "attributes", elles ne sont donc pas enregistrées dans la base de données.

Si vous souhaitez ajouter un nouvel attribut à votre modèle, vous devez ajouter une colonne à la table de base de données correspondant à ce modèle, puis recharger l'ensemble de l'application.

+0

est exactement ce qui me manquait. J'ai trouvé que le correctif était de supprimer la ligne attr_accessor, mais était confus quant à _why_ c'était le cas. Merci! – localshred

+0

C'est une vieille réponse mais ... MERCI! Je viens de passer 2 heures à essayer de comprendre pourquoi un modèle ne sauverait pas. Je l'avais développé sans AR et j'avais attr_accessor là-dedans. –

0

Les attributs attr_accessors ne seront jamais enregistrés dans la base de données. Ce sont des variables internes à l'instance. Si vous voulez enregistrer les valeurs, vous devez créer de vraies colonnes.

Effectuez une migration pour déclarer les colonnes, puis réessayez.

1

Il existe une distinction importante entre les champs de base de données et les propriétés déclarées attr_accessor temporaires. Si vous avez déclaré vos colonnes, les déclarations attr_accessor sont inutiles. Gardez à l'esprit que les données doivent être stockées dans la propriété attributes du modèle pour être enregistrées correctement, et non en tant que variables d'instance individuelles.

Par exemple, pour voir ce qui devrait être sauvé:

class MyModel < ActiveRecord::Base 
    attr_accessor :not_saved 
end 

model = MyModel.new(:not_saved => 'foo') 

puts model.attributes.inspect 

Il existe des méthodes pour obtenir des informations sur les colonnes sont disponibles dans un modèle, par exemple:

MyModel.columns_names 
Questions connexes