Как же Django знать для того, чтобы сделать поля форм?
Если у меня есть форма Django, например:
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
и я вызываю метод as_table () экземпляра этой формы, Django будет отображать поля в том же порядке, как указано выше.
мой вопрос в том, как Django знает порядок, в котором переменные класса определены?
(также как я могу переопределить этот порядок, например, когда я хочу добавить поле из класса init способ?)
14 ответов:
[Примечание: этот ответ теперь довольно полностью устарел - см. обсуждение ниже и более поздние ответы].
Если F представляет собой форму, ее поля Ф.поля, что является
django.utils.datastructures.SortedDict
(Он представляет элементы в порядке их добавления). После построения формы f. fields имеет атрибут keyOrder, который представляет собой список, содержащий имена полей в том порядке, в котором они должны быть представлены. Вы можете установить это в правильном порядке (хотя вам нужно проявлять осторожность, чтобы убедитесь, что вы не опускаете элементы или добавить дополнительно).вот пример, который я только что создал в своем текущем проекте:
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
New to Django 1.9 is форма.field_order и форма.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') fields_order = ['username', 'custom_field', 'password']
Я пошел вперед и ответил на свой собственный вопрос. Вот ответ для дальнейшего использования:
В Django
form.py
делает некоторые темные магии с помощью__new__
метод для загрузки переменных класса в конечном итоге вself.fields
в порядке, определенном в классе.self.fields
- это ДжангоSortedDict
экземпляр (определена вdatastructures.py
).Итак, чтобы переопределить это, скажем, в моем примере вы хотели, чтобы отправитель пришел первым, но ему нужно было добавить его в init метод, вы бы делать:
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())))
поля перечислены в том порядке, в котором они определены в классе ModelClass._мета.поля. Но если вы хотите изменить порядок в форме, вы можете сделать это с помощью функции keyOrder. Например :
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']
поля формы имеют атрибут для порядка создания, называемый
creation_counter
..fields
атрибут-это словарь, поэтому простое добавление в словарь и изменениеcreation_counter
атрибуты во всех полях, чтобы отразить новый порядок должно быть достаточно (никогда не пробовал это, хотя).
используйте счетчик в поле класса. Сортировка по этому счетчику:
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
выход:
[Field('b'), Field('a'), Field('c')]
С Django >= 1.7 ваш должен изменить
ContactForm.base_fields
, как показано ниже:from collections import OrderedDict ... class ContactForm(forms.Form): ... ContactForm.base_fields = OrderedDict( (k, ContactForm.base_fields[k]) for k in ['your', 'field', 'in', 'order'] )
этот трюк используется в Django Admin
PasswordChangeForm
:источник на Github
в формах Django 1.7 используется OrderedDict, который не поддерживает оператор append. Так что вам придется перестраивать словарь с нуля...
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
для дальнейшего использования: вещи немного изменились с newforms. Это один из способов переупорядочения полей из базовых классов форм, которые вы не контролируете:
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
кроме того, есть немного документации о SortedDict, что
base_fields
здесь: http://code.djangoproject.com/wiki/SortedDict
если
fields = '__all__'
:class AuthorForm(ModelForm): class Meta: model = Author fields = '__all__'
или
exclude
используется:class PartialAuthorForm(ModelForm): class Meta: model = Author exclude = ['title']
тогда Django ссылается на порядок полей как определено в модели. Это просто застало меня врасплох, так что я решил упомянуть об этом. На него ссылаются в ModelForm docs:
если используется любой из них, порядок отображения полей в форме будет соответствовать порядку, определенному в модели, с появлением экземпляров ManyToManyField последний.
Это связано с метаклассом, который используется при определении класса формы. Я думаю, что он хранит внутренний список полей, и если вы вставите его в середину списка, это может сработать. Прошло некоторое время с тех пор, как я смотрел на этот код.
используя
fields
внутреннейMeta
класс-это то, что работал для меня на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']
вот так просто.
я использовал это для перемещения полей:
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)
это работает в 1.5, и я уверен, что он по-прежнему работает в более поздних версиях.
самый простой способ заказать поля в формах django 1.9-использовать
field_order
в форме форма.field_orderвот небольшой пример
class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField() sender = forms.EmailField() field_order = ['sender','message','subject']
Это покажет все в порядке, указанном в
field_order
дикт.