2010-12-07 4 views
7

Dans mon application rails, j'ai un modèle avec start_date et end_date. Si l'utilisateur sélectionne le 1er janvier 2010 comme date de début et le 5 janvier 2010 comme date de fin, je souhaite qu'il y ait 5 instances de mon modèle créées (une pour chaque jour sélectionné). Donc, ça va ressembler à quelque chose commeComment remplacer la "nouvelle" méthode pour un modèle de rails

Jan 1, 2010 
Jan 2, 2010 
Jan 3, 2010 
Jan 4, 2010 
Jan 5, 2010 

Je sais qu'une façon de gérer cela est de faire une boucle dans le contrôleur. Quelque chose comme ...

# ...inside controller 
start_date.upto(end_date) { my_model.new(params[:my_model]) } 

Cependant, je veux garder mon contrôleur maigre, plus je veux garder la logique du modèle en dehors de celui-ci. Je devine que je dois remplacer la "nouvelle" méthode dans le modèle. Quelle est la meilleure façon de faire cela?

+0

Pourquoi voulez-vous? – Chowlett

+0

J'ai un formulaire que l'utilisateur remplit pour créer un modèle. Mais cette forme est juste un squelette pour remplir les détails de mon modèle. Le formulaire contient des détails tels que les points "début" et "fin". Pour créer un modèle complet, il faut que les points de début et de fin soient remplis. Je peux le faire dans le contrôleur mais je pense que ce type de logique devrait aller dans le modèle. – Lan

Répondre

16

Comme @brad dit, vous ne voulez certainement pas passer outre initialize. Bien que vous puissiez surcharger after_initialize, cela ne ressemble pas vraiment à ce que vous voulez ici. Au lieu de cela, vous voudrez probablement ajouter une méthode d'usine à la classe, comme le suggère @Pasta. Alors ajoutez ceci à votre modèle:

def self.build_for_range(start_date, end_date, attributes={}) 
    start_date.upto(end_date).map { new(attributes) } 
end 

Et puis l'ajouter à votre contrôleur:

models = MyModel.build_for_range(start_date, end_date, params[:my_model]) 
if models.all?(:valid?) 
    models.each(&:save) 
    # redirect the user somewhere ... 
end 
2

pourquoi ne créez-vous pas seulement une méthode dans votre modèle comme celui-ci

def self.create_dates(params) 
    [...] 
    end 

contenant cette logique (essentiellement la boucle?)

+0

Je le veux dans le modèle parce que c'est là que la logique de création devrait être, non? Mon modèle a juste besoin d'une date de début et de fin et ensuite il sera capable de créer une "instance" de lui-même. (l'instance se trouve être plusieurs lignes dans la table de base de données) – Lan

+0

donc je ferais ce que j'ai dit dans ma réponse. une méthode de classe create_dates avec votre boucle appelant new inside. – Pasta

+0

Désolé, je dois mieux répondre à ma question. Mais fondamentalement, je veux juste savoir comment remplacer MyModel.new (attributs) – Lan

2

Je suppose que vous voulez définir des valeurs par défaut de votre attribut modèle ?

Il existe une autre solution que le remplacement; vous pouvez définir: callbacks

class Model 

before_create :default_values 
def default_values 
    ... 
end 
2

Vous pouvez utiliser:

def initialize(attributes = nil) 
    # do your stuff... 
end 

Bien que quelque part je l'ai lu était pas recommandé ...

+3

ActiveRecord fait beaucoup de choses folles et cette méthode n'est pas garantie d'être exécutée comme prévu. – tadman

10

Ne contournez initialize Il pourrait casser beaucoup de choses dans vos modèles. Si nous savions pourquoi vous aviez besoin de nous pour aider mieux (ne comprenez pas complètement votre explication de la forme étant un squelette, vous voulez que les attributs de forme créent d'autres attributs, voir ci-dessous). J'utilise souvent un crochet comme Marcel l'a suggéré. Mais si vous voulez que cela se produise tout le temps, pas seulement avant de créer ou d'enregistrer un objet, utilisez le crochet after_initialize.

def after_initialize 
    # Gets called right after Model.new 
    # Do some stuff here 
end 

Aussi, si vous êtes à la recherche de certaines valeurs par défaut, vous pouvez fournir des accesseurs par défaut, quelque chose comme: (où some_attribute correspond au nom de la colonne de votre attribut modèle)

def some_attribute 
    attributes[:some_attribute] || "Some Default Value" 
end 

ou un écrivain

def some_attribute=(something) 
    attributes[:some_attribute] = something.with_some_changes 
end 

Si je comprends bien votre commentaire correctement, il semble que vous exposer une forme qui rendrait votre modèle incomplet, avec les autres attributs basés sur des parties de ce formulaire? Dans ce cas, vous pouvez utiliser l'une des méthodes ci-dessus after_initialize ou some_attribute= pour ensuite créer d'autres attributs sur votre modèle.

+1

Consultez la ActiveRecord :: source de base (https://github.com/rails/rails/blob/v3.0.3/activerecord/lib/active_record/base.rb) Regardez la méthode initialize et voir ce qui se passe, il est Récupérer des colonnes à partir de la base de données, assigner des variables d'instance etc, si vous surchargez ceci mais ne définissez pas ces attributs, il n'y a aucune garantie que active_record se comporte correctement. – brad

+0

Notez que dans Rails 3.2.x, les clés dans 'attributes' sont des chaînes plutôt que des symboles. Vous aurez donc besoin de 'attributes [" some_attribute "]' dans ce qui précède. – steakchaser

+2

Il n'y a aucune raison de ne pas remplacer l'initialisation. Au contraire, si vous regardez dans le code source des rails, vous comprendrez que l'initialisation était censée être surchargée. Tout ce que vous avez à faire est d'appeler super dans votre initialisation surchargée, mais c'est une règle qui devrait être suivie par tous les développeurs de ruby ​​et les développeurs de rails attend silencieusement que vous le fassiez. –

2

Cette odeur de la méthode usine patttern ... le chercher.

Si vous êtes réticent pour une raison quelconque d'utiliser create_date par @Pasta, alors créez éventuellement un simple objet ruby ​​(non soutenu par ActiveRecord), nommé YourModelFactory/Template/Whatever avec deux variables d'instance - vous pouvez utiliser votre standard params [: foo] pour les affecter - puis définissez et appelez une méthode sur cette classe qui retourne vos objets réels.

Votre logique du contrôleur ressemble maintenant quelque chose comme ceci:

mmf = MyModelFactory.new(params[:foo]) 
objs = mmf.create_real_deal_models 

Bonne chance.

+0

Je suis d'accord avec cela, maintenant qu'il a expliqué qu'il veut réellement créer plusieurs modèles, je pense qu'une usine est en ordre. – brad

+2

Je trouve que les usines sur les modèles sont mieux faites en tant que méthodes de classe que les classes séparées. Donc def.make_me_things sur MakeMeThings.new. – Trotter

+2

Je suis totalement +1 sur def.make_me_things - j'essaye juste de faire en sorte que cela ressemble à un modèle pour l'affiche originale. – Cory

0

strictement, bien que tardive, la bonne façon de passer outre nouvelle dans un modèle est

def initialize(args) 
    # 
    # do whatever, args are passed to super 
    # 
    super 
end 
Questions connexes