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