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 especificado anteriormente.
Mi pregunta es ¿cómo sabe Django el orden en que se definieron las variables de clase?
(Además, ¿cómo anulo este orden, por ejemplo, cuando quiero agregar un campo desde el método init de la clase ?)
fuente

Nuevo en Django 1.9 es Form.field_order y Form.order_fields () .
# forms.Form example class SignupForm(forms.Form): password = ... email = ... username = ... field_order = ['username', 'email', 'password'] # forms.ModelForm example class UserAccount(forms.ModelForm): custom_field = models.CharField(max_length=254) def Meta: model = User fields = ('username', 'email') field_order = ['username', 'custom_field', 'password']fuente
Seguí adelante y respondí mi propia pregunta. Aquí está la respuesta para referencia futura:
En Django se
form.pyhace magia oscura usando el__new__método para cargar las variables de su clase en última instanciaself.fieldsen el orden definido en la clase.self.fieldses unaSortedDictinstancia de Django (definida endatastructures.py).Entonces, para anular esto, digamos en mi ejemplo que deseaba que el remitente fuera primero pero necesitara agregarlo en un método de inicio , 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())))fuente
self.fields.insert( 0, 'sender', self.fields['sender'] )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']fuente
Con Django> = 1.7 debe modificar
ContactForm.base_fieldscomo 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: Fuente en Githubfuente
PasswordChangeFormcambiaba el orden de los campos, por lo que su ejemplo resultó ser muy útil para mí. Parece que es porquebase_fieldsno se transfiere a la subclase. La solución parece estar diciendoPasswordChangeFormSubclass.base_fields = PasswordChangeForm.base_fieldsdespués de la definición de `PasswordChangeFormSubclassContactForm.base_fields[k]al construir el dictado ordenado. Hay un error tipográfico en la respuesta, laLos campos de formulario tienen un atributo para el orden de creación, llamado
creation_counter..fieldsEl atributo es un diccionario, por lo que simplemente agregarlo al diccionario y cambiar loscreation_counteratributos en todos los campos para reflejar el nuevo orden debería ser suficiente (aunque nunca lo intenté).fuente
Utilice 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 orderSalida:
[Field('b'), Field('a'), Field('c')]fuente
A partir de Django 1.7, los formularios utilizan OrderedDict, que no admite el operador append. Entonces tienes que 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_fieldsfuente
Para referencia futura: las cosas han cambiado un poco desde las nuevas formas. Esta es una forma de reordenar los campos de las clases de formulario 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 formAdemás, hay un poco de documentación sobre SortedDict que se
base_fieldsusa aquí: http://code.djangoproject.com/wiki/SortedDictfuente
Si cualquiera
fields = '__all__':class AuthorForm(ModelForm): class Meta: model = Author fields = '__all__'o
excludese utilizan:class PartialAuthorForm(ModelForm): class Meta: model = Author exclude = ['title']Luego, Django hace referencia al orden de los campos tal como se define en el modelo . Esto me sorprendió, así que pensé en mencionarlo. Se hace referencia en los documentos de ModelForm :
fuente
La forma más fácil de ordenar campos en formularios de django 1.9 es usar
field_orderen su formulario Form.field_orderAquí 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 que especificó en
field_orderdict.fuente
Usar
fieldsen laMetaclase interna es lo que funcionó para mí enDjango==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.
fuente
django.formsno funcionaHe usado esto 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.
fuente
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 inserta en el medio de la lista podría funcionar. Ha pasado un tiempo desde que miré ese código.
fuente