Si j'ai une forme Django telle que:
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
Et j'appelle la méthode as_table () d'une instance de cette forme, Django rendra les champs dans le même ordre que spécifié ci-dessus.
Ma question est de savoir comment Django connaît l'ordre dans lequel les variables de classe sont définies?
(Comment puis-je remplacer cet ordre, par exemple lorsque je veux ajouter un champ à partir de la méthode init de la classe?)
Je suis allé de l'avant et j'ai répondu à ma propre question. Voici la réponse pour référence future:
Dans Django form.py
fait de la magie noire en utilisant le __new__
méthode pour charger vos variables de classe finalement dans self.fields
dans l'ordre défini dans la classe. self.fields
est une instance Django SortedDict
(définie dans datastructures.py
).
Donc, pour remplacer cela, disons dans mon exemple que vous vouliez que l'expéditeur soit le premier mais que vous deviez l'ajouter dans une méthode init, vous feriez:
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())))
[REMARQUE: cette réponse est maintenant complètement obsolète - veuillez consulter la discussion ci-dessous et les réponses plus récentes].
Si f
est un formulaire, ses champs sont f.fields
, qui est un Django.utils.datastructures.SortedDict
(il présente les éléments dans l'ordre où ils sont ajoutés). Après la construction du formulaire, f.fields a un attribut keyOrder, qui est une liste contenant les noms de champs dans l'ordre dans lequel ils doivent être présentés. Vous pouvez définir cela dans le bon ordre (bien que vous deviez faire preuve de prudence pour vous assurer de ne pas omettre d'articles ou d'ajouter des extras).
Voici un exemple que je viens de créer dans mon projet actuel:
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
Nouveau dans Django 1.9 est Form.field_order et 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']
Les champs sont répertoriés dans l'ordre dans lequel ils sont définis dans ModelClass._meta.fields. Mais si vous souhaitez modifier l'ordre dans le formulaire, vous pouvez le faire en utilisant la fonction keyOrder. Par exemple :
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']
Avec Django> = 1.7, vous devez modifier ContactForm.base_fields
comme ci-dessous:
from collections import OrderedDict
...
class ContactForm(forms.Form):
...
ContactForm.base_fields = OrderedDict(
(k, ContactForm.base_fields[k])
for k in ['your', 'field', 'in', 'order']
)
Cette astuce est utilisée dans Django Admin PasswordChangeForm
: Source sur Github
Les champs de formulaire ont un attribut pour l'ordre de création, appelé creation_counter
. .fields
l'attribut est un dictionnaire, donc simple à ajouter au dictionnaire et à modifier creation_counter
les attributs dans tous les champs pour refléter le nouvel ordre devraient suffire (jamais essayé cependant).
Utilisez un compteur dans la classe Field. Trier par ce compteur:
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
Production:
[Field('b'), Field('a'), Field('c')]
Depuis Django 1.7 utilisent OrderedDict qui ne prend pas en charge l'opérateur d'ajout. Vous devez donc reconstruire le dictionnaire à partir de zéro ...
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
Pour référence future: les choses ont un peu changé depuis les nouveaux formulaires. C'est une façon de réorganiser les champs des classes de base sur lesquelles vous n'avez aucun contrôle:
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
En outre, il existe un peu de documentation sur le SortedDict qui base_fields
utilise ici: http://code.djangoproject.com/wiki/SortedDict
Si soit fields = '__all__'
:
class AuthorForm(ModelForm):
class Meta:
model = Author
fields = '__all__'
ou exclude
sont utilisés:
class PartialAuthorForm(ModelForm):
class Meta:
model = Author
exclude = ['title']
Alors Django fait référence à l'ordre des champs tel que défini dans le modèle . Cela m'a juste pris au dépourvu, donc j'ai pensé Je le mentionnerais. Il est référencé dans les ModelForm docs :
Si l'un de ces éléments est utilisé, l'ordre dans lequel les champs apparaissent dans le formulaire sera l'ordre dans lequel les champs sont définis dans le modèle, les instances ManyToManyField apparaissant en dernier.
La façon la plus simple de classer les champs dans les formulaires Django 1.9 est d'utiliser field_order
dans votre formulaire Form.field_order
Voici un petit exemple
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
field_order = ['sender','message','subject']
Cela montrera tout dans l'ordre que vous avez spécifié dans field_order
dict.
L'utilisation de fields
dans la classe intérieure Meta
est ce qui a fonctionné pour moi sur 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']
Aussi simple que cela.
Je l'ai utilisé pour déplacer des champs sur:
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)
Cela fonctionne en 1.5 et je suis raisonnablement sûr que cela fonctionne toujours dans les versions plus récentes.
Cela concerne la méta-classe utilisée pour définir la classe de formulaire. Je pense qu'il conserve une liste interne des champs et si vous insérez au milieu de la liste, cela pourrait fonctionner. Cela fait un moment que je n'ai pas regardé ce code.