tipos personalizados guardar formularios formulario ejemplos datos boton avanzados python django django-forms

python - personalizados - ¿Cómo sabe Django el orden para procesar campos de formulario?



model forms django (14)

Si tengo un formulario de Django como:

class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField() sender = forms.EmailField()

Y llamo al método as_table () de una instancia de este formulario, Django representará los campos en el mismo orden que el especificado anteriormente.

Mi pregunta es ¿cómo sabe Django el orden en que se definieron las variables de clase?

(¿También cómo anulo este orden, por ejemplo cuando quiero agregar un campo del método init de la clase?)


A partir de Django 1.7, los formularios utilizan OrderedDict, que no es compatible con el operador anexar. Entonces debes reconstruir el diccionario desde cero ...

class ChecklistForm(forms.ModelForm): class Meta: model = Checklist fields = [''name'', ''email'', ''website''] def __init__(self, guide, *args, **kwargs): self.guide = guide super(ChecklistForm, self).__init__(*args, **kwargs) new_fields = OrderedDict() for tier, tasks in guide.tiers().items(): questions = [(t[''task''], t[''question'']) for t in tasks if ''question'' in t] new_fields[tier.lower()] = forms.MultipleChoiceField( label=tier, widget=forms.CheckboxSelectMultiple(), choices=questions, help_text=''desired set of site features'' ) new_fields[''name''] = self.fields[''name''] new_fields[''email''] = self.fields[''email''] new_fields[''website''] = self.fields[''website''] self.fields = new_fields


Con Django> = 1.7 debe modificar ContactForm.base_fields como se muestra a continuación:

from collections import OrderedDict ... class ContactForm(forms.Form): ... ContactForm.base_fields = OrderedDict( (k, ContactForm.base_fields[k]) for k in [''your'', ''field'', ''in'', ''order''] )

Este truco se usa en Django Admin PasswordChangeForm : Source en Github


La forma más fácil de pedir campos en formularios django 1.9 es usar field_order en su formulario Form.field_order

Aquí hay un pequeño ejemplo

class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField() sender = forms.EmailField() field_order = [''sender'',''message'',''subject'']

Esto mostrará todo en el orden especificado en field_order dict.


Lo he usado para mover campos sobre:

def move_field_before(frm, field_name, before_name): fld = frm.fields.pop(field_name) pos = frm.fields.keys().index(before_name) frm.fields.insert(pos, field_name, fld)

Esto funciona en 1.5 y estoy razonablemente seguro de que todavía funciona en versiones más recientes.


Los campos de formulario tienen un atributo para el orden de creación, llamado creation_counter . .fields atributo .fields es un diccionario, por lo que la simple adición al diccionario y el cambio de los atributos de creation_counter en todos los campos para reflejar el nuevo orden debería ser suficiente (aunque nunca lo intenté).


Los campos se enumeran en el orden en que se definen en ModelClass._meta.fields. Pero si desea cambiar el orden en el formulario, puede hacerlo utilizando la función keyOrder. Por ejemplo :

class ContestForm(ModelForm): class Meta: model = Contest exclude=(''create_date'', ''company'') def __init__(self, *args, **kwargs): super(ContestForm, self).__init__(*args, **kwargs) self.fields.keyOrder = [ ''name'', ''description'', ''image'', ''video_link'', ''category'']


Nuevo en Django 1.9 es Form.field_order y Form.order_fields() .

# example class SignupForm(forms.Form): password = ... email = ... username = ... field_order = [''username'', ''email'', ''password'']


Para referencia futura: las cosas han cambiado un poco desde newforms. Esta es una forma de reordenar campos de clases base sobre las que no tiene control:

def move_field_before(form, field, before_field): content = form.base_fields[field] del(form.base_fields[field]) insert_at = list(form.base_fields).index(before_field) form.base_fields.insert(insert_at, field, content) return form

Además, hay un poco de documentación sobre el SortedDict que base_fields usa aquí: http://code.djangoproject.com/wiki/SortedDict


Seguí adelante y respondí mi propia pregunta. Aquí está la respuesta para futuras referencias:

En Django, form.py usa magia oscura usando el método __new__ para cargar sus variables de clase en self.fields en el orden definido en la clase. self.fields es una instancia Django SortedDict (definida en datastructures.py ).

Entonces, para anular esto, digamos en mi ejemplo que quería que el remitente fuera primero pero que necesitara agregarlo en un método init , usted haría:

class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField() def __init__(self,*args,**kwargs): forms.Form.__init__(self,*args,**kwargs) #first argument, index is the position of the field you want it to come before self.fields.insert(0,''sender'',forms.EmailField(initial=str(time.time())))


Si cualquiera de los fields = ''__all__'' :

class AuthorForm(ModelForm): class Meta: model = Author fields = ''__all__''

o exclude se utilizan:

class PartialAuthorForm(ModelForm): class Meta: model = Author exclude = [''title'']

Entonces Django hace referencia al orden de los campos como se define en el modelo . Esto me sorprendió, así que pensé en mencionarlo. Se hace referencia en los documentos de ModelForm :

Si se usa cualquiera de estos, el orden en que aparecerán los campos en el formulario será el orden en que se definan los campos en el modelo, y las instancias de ManyToManyField aparecerán en último lugar.


Tiene que ver con la metaclase que se usa para definir la clase de formulario. Creo que mantiene una lista interna de los campos y si la inserta en el medio de la lista podría funcionar. Ha pasado un tiempo desde que miré ese código.


Usa un contador en la clase Field. Ordenar por ese contador:

import operator import itertools class Field(object): _counter = itertools.count() def __init__(self): self.count = Field._counter.next() self.name = '''' def __repr__(self): return "Field(%r)" % self.name class MyForm(object): b = Field() a = Field() c = Field() def __init__(self): self.fields = [] for field_name in dir(self): field = getattr(self, field_name) if isinstance(field, Field): field.name = field_name self.fields.append(field) self.fields.sort(key=operator.attrgetter(''count'')) m = MyForm() print m.fields # in defined order

Salida:

[Field(''b''), Field(''a''), Field(''c'')]


Usar fields en Meta Class interno es lo que funcionó para mí en Django==1.6.5 :

#!/usr/bin/env python # -*- coding: utf-8 -*- """ Example form declaration with custom field order. """ from django import forms from app.models import AppModel class ExampleModelForm(forms.ModelForm): """ An example model form for ``AppModel``. """ field1 = forms.CharField() field2 = forms.CharField() class Meta: model = AppModel fields = [''field2'', ''field1'']

Tan sencillo como eso.


[NOTA: esta respuesta ahora está bastante desactualizada. Consulte la discusión a continuación y las respuestas más recientes].

Si f es un formulario, sus campos son f.fields, que es un django.utils.datastructures.SortedDict (presenta los elementos en el orden en que se agregan). Después de la construcción de la forma, f.fields tiene un atributo keyOrder, que es una lista que contiene los nombres de los campos en el orden en que deben presentarse. Puede configurar esto para el orden correcto (aunque debe tener cuidado para asegurarse de no omitir elementos o agregar extras).

Aquí hay un ejemplo que acabo de crear en mi proyecto actual:

class PrivEdit(ModelForm): def __init__(self, *args, **kw): super(ModelForm, self).__init__(*args, **kw) self.fields.keyOrder = [ ''super_user'', ''all_districts'', ''multi_district'', ''all_schools'', ''manage_users'', ''direct_login'', ''student_detail'', ''license''] class Meta: model = Privilege