2010-06-14 6 views
17

Quiero actualizar el Meta.fields dinámicamente. ¿Es posible hacerlo desde el constructor de Formulario? Intenté lo siguiente, pero year no aparece durante la generación del formulario. Solo se muestran name y title.¿Cómo se actualizan dinámicamente los campos de una clase Django Form Meta desde el constructor de formulario?

class Author(models.Model): 
    name = ... 
    title = ... 
    year = ... 

class PartialAuthorForm(ModelForm): 
    class Meta: 
     model = Author 
     fields = ('name', 'title') 

    def __init__(self, *args, **kwargs): 
     self.Meta.fields += ('year',) 

Respuesta

17

No, eso no funcionará. Meta es analizado, sorprendentemente, por la metaclase, incluso antes de llegar al __init__.

La manera de hacer esto es añadir el campo manualmente para self.fields:

def __init__(self, *args, **kwargs): 
    super(PartialAuthorForm, self).__init__(*args, **kwargs) 
    self.fields['year'] = forms.CharField(whatever) 
0

La clase interna Meta ya se procesa cuando se crea la clase ModelForm a través de una metaclase, que es antes de crear una instancia. ¡Hacer lo que intentas hacer en __init__ no funcionará así!
Puede acceder a los objetos de campo creados en un dictonary self.fields, por ejemplo. puedes hacer algo como self.fields['year'] = forms.MyFormField(...).

4

Las respuestas que dicen usar __init__ funcionarán pero perderá la conexión con el modelo (suponiendo que el campo que está agregando está en el modelo). En su lugar, puede crear una nueva forma de forma dinámica que herede de la forma existente.

def my_form_factory: 
    class ModifiedAuthorForm(PartialAuthorForm): 
     class Meta(PartialAuthorForm.Meta): 
      fields = PartialAuthorForm.Meta.fields 
      if hasattr(fields, 'append'): 
       fields.append('year') 
      else: 
       fields = fields + ('year',) 
return ModifiedAuthorForm 

Por supuesto, si quieren ser robusta probablemente debería comprobar que el campo va a añadir no está ya en fields y probablemente debería manejar exclude también.

14

Las respuestas de otros son geniales (especialmente @ BranHandley's), pero a veces, es más fácil condicionalmente eliminar algo en su lugar. Obviamente, esto no funciona con cosas que se crean dinámicamente, pero si solo tiene que incluir opcionalmente un campo o no, entonces inténtelo de esta manera.

class MyModelForm(ModelForm): 
    class Meta: 
    model = MyModel 
    fields = ['normalField', 'conditionalField', ] 

    def __init__(self, *args, **kwargs): 

    super(MyModelForm, self).__init__(*args, **kwargs) 

    if condition is True: 
     del self.fields['conditionalField'] 

Hacerlo a la inversa puede ser bastante complicado como se ha señalado anteriormente, pero se caiga de forma condicional es bastante sencillo. =)

+0

Creo que esa es la solución más elegante, ya que no va a perder la magia de la clase ModelForms. Pero no te olvides de llamar a super() en __init__. – bjunix

+0

¡Uy! Corregido, gracias! – mkoistinen

0

Agregar campo al formulario de modelo anulando el método __init__ del formulario no funcionará ya que los campos de formulario modelo se inicializan antes de la llamada al constructor de formulario. Por lo tanto, no obtendrá ningún valor del modelo real en el formulario y sus valores no se guardarán en db después de guardar la llamada.

Es necesario utilizar toda la belleza del pitón y utilizar decorador de clase como esta;)

def year_decorator(form_class): 
    class ModifiedForm(form_class): 
     class Meta(form_class.Meta): 
      if isinstance(form_class.Meta.model, Author): 
       fields = form_class.Meta.fields + ('year',) 
    return ModifiedForm 


@year_decorator 
class PartialAuthorForm(ModelForm): 
    class Meta: 
     model = Author 
     fields = ('name', 'title') 
Cuestiones relacionadas