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.py
hace magia oscura usando el__new__
método para cargar las variables de su clase en última instanciaself.fields
en el orden definido en la clase.self.fields
es unaSortedDict
instancia 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_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
: Fuente en Githubfuente
PasswordChangeForm
cambiaba el orden de los campos, por lo que su ejemplo resultó ser muy útil para mí. Parece que es porquebase_fields
no se transfiere a la subclase. La solución parece estar diciendoPasswordChangeFormSubclass.base_fields = PasswordChangeForm.base_fields
despué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
..fields
El atributo es un diccionario, por lo que simplemente agregarlo al diccionario y cambiar loscreation_counter
atributos 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 order
Salida:
[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_fields
fuente
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 form
Además, hay un poco de documentación sobre SortedDict que se
base_fields
usa aquí: http://code.djangoproject.com/wiki/SortedDictfuente
Si cualquiera
fields = '__all__'
:class AuthorForm(ModelForm): class Meta: model = Author fields = '__all__'
o
exclude
se 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_order
en 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_order
dict.fuente
Usar
fields
en laMeta
clase 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.forms
no 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