2017-10-09 2 views
0

Je veux définir des attributs dynamiquement à un FlaskForm comme celui-ciPython: Impossible de définir dynamiquement des attributs sur un FlaskForm?

form = ModelForm(request.form) 
fichier

form.py

class ModelForm(FlaskForm): 
    def __init__(self, postData): 
     super(ModelForm, self).__init__() 
     for p in postData: 
      setattr(ModelForm, p, StringField(p, validators=[InputRequired()])) 

Mais il ne fonctionne que pour la deuxième fois consécutive, la première fois en cours d'exécution, il ne travail. Je ne comprends vraiment pas comment fonctionne le constructeur Python. Comme ce post, il a dit que

class A is not fully initialized when you do your setattr(A, p, v) there.

Mais dans d'autres langues, l'objet doivent être créés après le constructeur fini, et il a des variables pleine de classe, les propriétés déclarées dans le constructeur? Par exemple, cela fonctionne et peut imprimer une touche. Alors, quelle différence y a-t-il dans le constructeur de flacons?

class A(object): 
    def __init__(self): 
     self.a = 'i am a accessor' 
     setattr(self, 'key', 'value') 

a = A() 
print a.a 
print a.key 

Répondre

0

class A is not fully initialized

moyens, lorsque vous créez votre première instance de votre classe avec form=ModelForm(), vous commencez à ajouter des attributs à la classe que vous instanciation actuellement de. Je ne sais pas, comment cela fonctionne en interne en Python. Mais puisque vous dites que cela ne fonctionne que dans la deuxième exécution, je suppose que l'objet est d'abord créé avec tous les attributs définis pour la classe, alors __init__ est exécuté. Donc, les nouveaux attributs sont ajoutés à la fin. En d'autres termes: Vous essayez de changer la définition de la classe après vous en avez déjà créé une instance. Vous devez ajouter tous vos attributs, avant d' instancier.

Maintenant, ce que vous devez faire est: d'abord définir la classe sans champs dynamiques. Ensuite, après la définition de classe, vous ajoutez la boucle avec vos champs dynamiques.

ModelForm = FlaskForm 
for p in postData: 
    setattr(ModelForm, p, StringField(p, validators=[InputRequired()])) 

Ou si vous avez besoin d'ajouter d'autres choses dans la définition de la classe:

class ModelForm(FlaskForm): 
    def foo(self): 
     return 'bar' 

for p in postData: 
    setattr(ModelForm, p, StringField(p, validators=[InputRequired()])) 

Et puis, quelque part dans votre fonction de vue, vous pouvez utiliser form = ModelForm(request.form) comme d'habitude.

Habituellement, vous savez de quels champs vous avez besoin au préalable. Le formulaire répond simplement sur ce qu'il a reçu de la requête GET. Donc ça devrait aller. Mais peut-être avez-vous ajouté d'autres champs avec du JS côté client, que le serveur ne connaît pas (encore). Dans ce cas, vous pouvez essayer de placer la définition de classe dans la portée locale de la fonction d'affichage qui gère la requête POST.

+0

J'ai mis à jour la question avec un exemple de constructeur pour clarifier la différence. Puisque 'je ne sais pas, comment cela fonctionne en interne en Python' a été dit, cela signifie que le constructeur en Python ne fonctionne pas correctement ou que Flask form a fait quelque chose avec leur bibliothèque pour empêcher l'initialisation de l'objet. Ofcouse, nous pouvons contourner le problème avec 'setattr' en retard, mais cela ne m'aide pas à comprendre ce qui se passe avec Flask ou le constructeur Python – TomSawyer

+0

Que se passe-t-il si vous essayez de mettre l'appel' super() .__ init__' après 'setattr '? Mais pourquoi voulez-vous même utiliser 'setattr' dans' __init__'?Vous essayez de construire un objet qui change ses propres plans pendant les constructions. Je ne sais même pas si cela est supposé fonctionner. – Feodoran

+0

Mettre 'super()' après que setattr ne fonctionne pas, cela donne une erreur 'TypeError: 'NoneType' object is iterable'. Setattr inside __init__ est une bonne pratique et fonctionne dans toutes les langues, il permet d'éviter d'écrire plus de lignes pour déclarer l'objet formulaire, j'ai seulement besoin: 'form = MyForm (attrs)' – TomSawyer