2011-11-04 36 views
11

Quiero tener campos adicionales en relación con el valor de un campo. Por lo tanto, creo un formulario de administrador personalizado para agregar algunos campos nuevos.campos dinámicos en Django administración

relacionados con la entrada de blog de jacobian 1 Esto es lo que ocurrió:

class ProductAdminForm(forms.ModelForm): 
    class Meta: 
     model = Product 

    def __init__(self, *args, **kwargs): 
     super(ProductAdminForm, self).__init__(*args, **kwargs) 
     self.fields['foo'] = forms.IntegerField(label="foo") 

class ProductAdmin(admin.ModelAdmin): 
    form = ProductAdminForm 

admin.site.register(Product, ProductAdmin) 

Pero el campo 'foo' adicional no aparece en el admin. Si añado el campo como éste, todo funciona bien, pero no es tan dinámica como sea necesario, para agregar los campos en relación con el valor de otro campo del modelo

class ProductAdminForm(forms.ModelForm): 

    foo = forms.IntegerField(label="foo") 

    class Meta: 
     model = Product 

class ProductAdmin(admin.ModelAdmin): 
    form = ProductAdminForm 

admin.site.register(Product, ProductAdmin) 

Entonces, ¿hay algún método que tengo que gatillo inicializar de nuevo para hacer que el nuevo campo funcione? ¿O hay algún otro intento?

Respuesta

14

Aquí es una solución al problema. Gracias a koniiiik i trató de resolver este extendiendo los get_fieldsets * * método

class ProductAdmin(admin.ModelAdmin): 
    def get_fieldsets(self, request, obj=None): 
     fieldsets = super(ProductAdmin, self).get_fieldsets(request, obj) 
     fieldsets[0][1]['fields'] += ['foo'] 
     return fieldsets 

Si utiliza varios conjuntos de campos asegúrese de añadir el de la derecha FIELDSET utilizando el índice apropiado.

+1

'Unknown field (s) (foo) especificado para GlobalLabel. Verifique los campos/conjuntos de campo/excluya los atributos de la clase GlobalLabelAdmin. "Estoy recibiendo este error, no sé por qué ... ¿pueden ayudarme? – bhushya

+0

@bhushya: ¿fueron capaces de resolver esto? Tampoco puedo hacer que funcione en django 1.9.3, por ejemplo: 'django.core.exceptions.FieldError: Campo (s) desconocido (dynamicfield1, dynamicfield2) especificado para MyModel' – tehfink

+0

@tehfink Parece que no has definido el campo en su modelo ... ¿puede publicar su estructura de modelo en pastebin.com y compartir el enlace? – bhushya

5

Mientras que la publicación de Jacob podría funcionar bien para ModelForm s regulares (a pesar de que es más de un año y medio de edad), el administrador es un asunto algo diferente.

Durante todo el camino declarativa de modelos que definen, forma ModelAdmins y todo eso hace un uso intensivo de metaclases y la introspección de clases. Lo mismo con el administrador - cuando se le dice a un ModelAdmin utilizar una forma Istead específico de crear un defecto, es una introspección la clase. Obtiene la lista de campos y otras cosas de la clase misma sin instanciarlo.

la clase personalizada, sin embargo, no define el campo de formulario adicional a nivel de clase, sino que agrega dinámicamente una después de que se ha creado una instancia - que es demasiado tarde para el ModelAdmin para reconocer este cambio.

Una manera de ir sobre su problema podría ser la subclase ModelAdmin y anular su método get_fieldsets para crear instancias de la clase realidad ModelForm y obtener la lista de campos de la instancia en lugar de la clase. Sin embargo, deberá tener en cuenta que esto puede ser algo más lento que la implementación predeterminada.

0

no estoy seguro de por qué eso no funciona, pero ¿podría una posible solución definir el campo estáticamente (en el formulario) y luego anularlo en el __init__?

2

respuesta de Stephan es elegante, pero cuando utilicé en dj1.6 en que requiere el campo para ser una tupla. La solución completa era la siguiente:

class ProductForm(ModelForm): 
    foo = CharField(label='foo') 


class ProductAdmin(admin.ModelAdmin): 
    form = ProductForm 
    def get_fieldsets(self, request, obj=None): 
     fieldsets = super(ProductAdmin, self).get_fieldsets(request, obj) 
     fieldsets[0][1]['fields'] += ('foo',) 
     return fieldsets 
+0

¿Le importaría, si incluyo esto en mi respuesta;) –

3

Puede crear campos dinámicos y de campos utilizando la clase de formulario meta. El código de muestra se proporciona a continuación. Agregue la lógica de bucle según sus requisitos.

class CustomAdminFormMetaClass(ModelFormMetaclass): 
    """ 
    Metaclass for custom admin form with dynamic field 
    """ 
    def __new__(cls, name, bases, attrs): 
     for field in get_dynamic_fields: #add logic to get the fields 
      attrs[field] = forms.CharField(max_length=30) #add logic to the form field 
     return super(CustomAdminFormMetaClass, cls).__new__(cls, name, bases, attrs) 


class CustomAdminForm(six.with_metaclass(CustomAdminFormMetaClass, forms.ModelForm)): 
    """ 
    Custom admin form 
    """ 

    class Meta: 
     model = ModelName 
     fields = "__all__" 


class CustomAdmin(admin.ModelAdmin): 
    """ 
    Custom admin 
    """ 

    fieldsets = None 
    form = CustomAdminForm 

    def get_fieldsets(self, request, obj=None): 
     """ 
     Different fieldset for the admin form 
     """ 
     self.fieldsets = self.dynamic_fieldset(). #add logic to add the dynamic fieldset with fields 
     return super(CustomAdmin, self).get_fieldsets(request, obj) 

    def dynamic_fieldset(self): 
     """ 
     get the dynamic field sets 
     """ 
     fieldsets = [] 
     for group in get_field_set_groups: #logic to get the field set group 
      fields = [] 
      for field in get_group_fields: #logic to get the group fields 
       fields.append(field) 

      fieldset_values = {"fields": tuple(fields), "classes": ['collapse']} 
      fieldsets.append((group, fieldset_values)) 

     fieldsets = tuple(fieldsets) 

     return fieldsets 
3

La respuesta aceptada anteriormente funcionaba en versiones anteriores de django, y así es como lo estaba haciendo. Esto ahora se ha roto en versiones posteriores de django (estoy en 1.68 en este momento, pero incluso eso es viejo ahora).

El motivo por el que ahora está roto es porque los campos dentro de los conjuntos de campo que usted devuelve de ModelAdmin.get_fieldsets() se pasan finalmente como los campos = parámetro a modelform_factory(), lo que le dará un error porque los campos en su lista no existe (y no existirá hasta que tu instancia sea instanciada y se llame su __ init __).

Para solucionar esto, debemos anular ModelAdmin.get_form() y proporcionar una lista de campos que no incluya ningún campo adicional que se agregará más adelante. El comportamiento predeterminado de get_form es llamar get_fieldsets() para esta información, y hay que evitar que esto ocurra:

# CHOOSE ONE 
# newer versions of django use this 
from django.contrib.admin.utils import flatten_fieldsets 
# if above does not work, use this 
from django.contrib.admin.util import flatten_fieldsets 

class MyModelForm(ModelForm): 
    def __init__(self, *args, **kwargs): 
     super(MyModelForm, self).__init__(*args, **kwargs) 
     # add your dynamic fields here.. 
     for fieldname in ('foo', 'bar', 'baz',): 
      self.fields[fieldname] = form.CharField() 

class MyAdmin(ModelAdmin): 
    form = MyModelForm 

    fieldsets = [ 
     # here you put the list of fieldsets you want displayed.. only 
     # including the ones that are not dynamic 
    ] 

    def get_form(self, request, obj=None, **kwargs): 
     # By passing 'fields', we prevent ModelAdmin.get_form from 
     # looking up the fields itself by calling self.get_fieldsets() 
     # If you do not do this you will get an error from 
     # modelform_factory complaining about non-existent fields. 

     # use this line only for django before 1.9 (but after 1.5??) 
     kwargs['fields'] = flatten_fieldsets(self.declared_fieldsets) 
     # use this line only for django 1.9 and later 
     kwargs['fields'] = flatten_fieldsets(self.fieldsets) 

     return super(MyAdmin, self).get_form(request, obj, **kwargs) 

    def get_fieldsets(self, request, obj=None): 
     fieldsets = super(MyAdmin, self).get_fieldsets(request, obj) 

     newfieldsets = list(fieldsets) 
     fields = ['foo', 'bar', 'baz'] 
     newfieldsets.append(['Dynamic Fields', { 'fields': fields }]) 

     return newfieldsets 
+0

Desafortunadamente, 'ModelAdmin.declared_fieldsets' [ha sido eliminado] (https://docs.djangoproject.com/en/1.9/internals/deprecation/) en Django 1.9 – tehfink

+0

Hmm ... bueno, supongo que cuando actualice mis servidores a 1.9 voy a tener algo de trabajo que hacer;) pero por suerte he replicado la mayoría de la funcionalidad de administrador en otra parte de mi aplicación ... –

+0

También 'django.contrib.admin.util' ahora es' django.contrib.admin.utils' –

4

Esto funciona para añadir campos dinámicos en Django 1.9.3, usando sólo una clase ModelAdmin (sin ModelForm) y anulando get_fields. No sé todavía lo robusto que es:

class MyModelAdmin(admin.ModelAdmin): 

    fields = [('title','status',), 'description', 'contact_person',] 
    exclude = ['material'] 

    def get_fields(self, request, obj=None): 
     gf = super(MyModelAdmin, self).get_fields(request, obj) 

     new_dynamic_fields = [ 
      ('test1', forms.CharField()), 
      ('test2', forms.ModelMultipleChoiceField(MyModel.objects.all(), widget=forms.CheckboxSelectMultiple)), 
     ] 

     #without updating get_fields, the admin form will display w/o any new fields 
     #without updating base_fields or declared_fields, django will throw an error: django.core.exceptions.FieldError: Unknown field(s) (test) specified for MyModel. Check fields/fieldsets/exclude attributes of class MyModelAdmin. 

     for f in new_dynamic_fields: 
      #`gf.append(f[0])` results in multiple instances of the new fields 
      gf = gf + [f[0]] 
      #updating base_fields seems to have the same effect 
      self.form.declared_fields.update({f[0]:f[1]}) 
     return gf 
+0

esto parece funcionar en django 1.10 – nidhin

0

que durante mucho tiempo no se podía resolver un problema con la adición dinámica de campos. La solución "little_birdie" realmente funciona. Gracias Birdie)) El único matiz es: "Self.declared_fieldsets" se debe reemplazar por "self.fieldsets".

#kwargs['fields'] = flatten_fieldsets(self.declared_fieldsets) 
kwargs['fields'] = flatten_fieldsets(self.fieldsets) 

He usado la versión 1.10. Quizás algo ha cambiado.

Si alguien encuentra una solución aún más simple y elegante, muéstrela aquí.

Gracias a todos)))

+0

. De nada. –

Cuestiones relacionadas