Jeśli mam formularz Django taki jak:
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
Wywołuję metodę as_table () instancji tego formularza, Django wyrenderuje pola w takiej samej kolejności, jak określono powyżej.
Moje pytanie brzmi: skąd Django zna kolejność definiowania zmiennych klas?
(Również jak zmienić tę kolejność, na przykład, gdy chcę dodać pole z metody init klasy ?)
źródło
Nowością w Django 1.9 są Form.field_order i 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']
źródło
Poszedłem dalej i odpowiedziałem na własne pytanie. Oto odpowiedź na przyszłość:
W Django
form.py
robi trochę mrocznej magii, używając__new__
metody do załadowania zmiennych klasowych ostatecznieself.fields
w kolejności zdefiniowanej w klasie.self.fields
jestSortedDict
instancją Django (zdefiniowaną wdatastructures.py
).Aby to zmienić, powiedzmy w moim przykładzie, że chcesz, aby nadawca był pierwszy, ale musisz go dodać w metodzie init , zrobiłbyś:
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())))
źródło
self.fields.insert( 0, 'sender', self.fields['sender'] )
Pola są wymienione w kolejności, w jakiej są zdefiniowane w ModelClass._meta.fields. Ale jeśli chcesz zmienić kolejność w formularzu, możesz to zrobić za pomocą funkcji keyOrder. Na przykład :
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']
źródło
W Django> = 1.7 musisz zmodyfikować
ContactForm.base_fields
jak poniżej:from collections import OrderedDict ... class ContactForm(forms.Form): ... ContactForm.base_fields = OrderedDict( (k, ContactForm.base_fields[k]) for k in ['your', 'field', 'in', 'order'] )
Ta sztuczka jest używana w Django Admin
PasswordChangeForm
: Source na Githubźródło
PasswordChangeForm
zmieniają kolejność pól, więc twój przykład był dla mnie bardzo przydatny. Wygląda na to, żebase_fields
dzieje się tak, ponieważ nie jest przenoszony do podklasy. Rozwiązanie wydaje się mówićPasswordChangeFormSubclass.base_fields = PasswordChangeForm.base_fields
po definicji `` PasswordChangeFormSubclassContactForm.base_fields[k]
podczas budowania zamówionego dyktu. W odpowiedzi jest literówka,Pola formularza mają atrybut kolejności tworzenia o nazwie
creation_counter
..fields
atrybut jest słownikiem, więc proste dodanie do słownika i zmianacreation_counter
atrybutów we wszystkich polach w celu odzwierciedlenia nowej kolejności powinno wystarczyć (nigdy tego nie próbowałem).źródło
Użyj licznika w klasie Field. Sortuj według tego licznika:
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
Wynik:
[Field('b'), Field('a'), Field('c')]
źródło
Od wersji Django 1.7 formularze używają OrderedDict, która nie obsługuje operatora dołączania. Musisz więc odbudować słownik od podstaw ...
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
źródło
Na przyszłość: od czasu Newforms sytuacja nieco się zmieniła. Jest to jeden ze sposobów zmiany kolejności pól z podstawowych klas formularzy, nad którymi nie masz kontroli:
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
Istnieje również trochę dokumentacji dotyczącej SortedDict, która
base_fields
używa tutaj: http://code.djangoproject.com/wiki/SortedDictźródło
Jeśli
fields = '__all__'
:class AuthorForm(ModelForm): class Meta: model = Author fields = '__all__'
lub
exclude
są używane:class PartialAuthorForm(ModelForm): class Meta: model = Author exclude = ['title']
Następnie Django odwołuje się do kolejności pól zdefiniowanej w modelu . Właśnie to mnie zaskoczyło, więc pomyślałem, że o tym wspomnę. Jest przywoływany w dokumentacji ModelForm :
źródło
Najłatwiejszym sposobem uporządkowania pól w formularzach django 1.9 jest użycie
field_order
w formularzu Form.field_orderOto mały przykład
class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField() sender = forms.EmailField() field_order = ['sender','message','subject']
Spowoduje to wyświetlenie wszystkiego w kolejności określonej w
field_order
dict.źródło
Użycie
fields
wMeta
klasie wewnętrznej zadziałało w przypadku mnieDjango==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']
Tak proste jak to.
źródło
django.forms
nie działaUżyłem tego do przenoszenia pól o:
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)
Działa to w wersji 1.5 i jestem pewien, że nadal działa w nowszych wersjach.
źródło
Ma to związek z klasą meta używaną do definiowania klasy formularza. Myślę, że przechowuje wewnętrzną listę pól i jeśli wstawisz je na środku listy, może to zadziałać. Minęło trochę czasu, odkąd przyjrzałem się temu kodowi.
źródło