2008-12-08 15 views
79

Wenn ich eine Django Form haben, wie:Wie kennt Django die Reihenfolge, um Formularfelder zu rendern?

class ContactForm(forms.Form): 
    subject = forms.CharField(max_length=100) 
    message = forms.CharField() 
    sender = forms.EmailField() 

Und ich rufe die as_table() -Methode eine Instanz dieser Form, wird Django die Felder wie derselben Reihenfolge übertragen, wie oben angegeben.

Meine Frage ist, wie Django die Reihenfolge kennt, in der Klassenvariablen definiert wurden?

(auch wie kann ich diese Ordnung außer Kraft setzen, zum Beispiel, wenn ich ein Feld aus dem classe des init Methode hinzufügen?)

Antwort

38

Ich ging weiter und beantwortete meine eigene Frage. Hier ist die Antwort für die Zukunft:

In Django form.py hat einige dunkele Magie die __new__ Methode mit Ihren Klassenvariablen laden schließlich in self.fields in der Reihenfolge, in der Klasse definiert. self.fields ist eine Instanz von Django SortedDict (definiert in datastructures.py).

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()))) 
+10

Sie können das Feld 'sender' oben normal definieren und dann eine Variante Ihrer Einfügezeile verwenden: 'self.fields.insert (0, 'sender', self.fields ['sender'])' – EvdB

+3

Dies funktioniert nicht mehr in Django 1.7, da Formularfelder OrderedDict verwenden, das append nicht unterstützt. Siehe meine aktualisierte Antwort unten (zu lang für einen Kommentar) ... –

0

Es hat mit der Meta-Klasse zu tun, die bei der Festlegung der Form verwendet wird, Klasse. Ich denke, es hält eine interne Liste der Felder und wenn Sie in die Mitte der Liste einfügen, könnte es funktionieren. Es ist eine Weile her, seit ich diesen Code angeschaut habe.

5

Formularfelder haben ein Attribut für die Erstellungsreihenfolge, genannt creation_counter. Attribut ist ein Wörterbuch, so einfach zum Wörterbuch hinzufügen und ändern creation_counter Attribute in allen Feldern, um neue Ordnung widerspiegeln sollte ausreichen (nie versucht, dies jedoch).

5

Verwenden Sie einen Zähler in der Klasse Field: hinzufügen, um es in einem init Methode, würden Sie tun

Also diese außer Kraft zu setzen, sagen in meinem Beispiel Sie Absender zunächst aber benötigt kommen wollte. Sortieren nach diesen Zählern:

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 

Ausgang:

[Field('b'), Field('a'), Field('c')] 

+0

Einfach und es funktioniert perfekt. Vielen Dank! –

87

[HINWEIS: Diese Antwort ist jetzt ziemlich völlig veraltet - sehen Sie bitte die Diskussion darunter, und neuere Antworten].

Wenn f ein Formular ist, sind seine Felder f.fields, das ist ein django.utils.datastructures.SortedDict (es präsentiert die Elemente in der Reihenfolge, in der sie hinzugefügt werden). Nach der Formularkonstruktion hat f.fields ein keyOrder-Attribut, das eine Liste mit den Feldnamen in der Reihenfolge darstellt, in der sie angezeigt werden sollen. Sie können dies auf die richtige Reihenfolge einstellen (obwohl Sie sorgfältig darauf achten müssen, dass Sie keine Elemente weglassen oder Extras hinzufügen).

Hier ist ein Beispiel, das ich gerade in meinem aktuellen Projekt erstellt:

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 
+18

Seit einiger Zeit werden die Felder wie im Attribut 'fields' einer ModelForm angegeben angeordnet. Von https://docs.djangoproject.com/en/1.3/topics/forms/modelforms/#using-a-subset-of-fields-on-the-form: Die Reihenfolge, in der die Feldnamen in dieser Liste angegeben sind wird respektiert, wenn das Formular sie rendert. Dies funktioniert nur für ModelForms - Ihr Ansatz ist immer noch sehr nützlich für reguläre Formulare, besonders in Form-Unterklassen, die zusätzliche Felder hinzufügen. –

+3

Das funktioniert, wenn Sie funky Sachen machen, die einige Felder aber nicht andere überschreiben. +1 –

+0

"Dies" ist Chris Lawlor Vorschlag? Diese Antwort ist alt genug, dass es wahrscheinlich nicht mehr aktuell ist (aber wahrscheinlich gut für 1.2). Es ist wichtig sicherzustellen, dass unzuverlässige Informationen gekennzeichnet werden, da Neulinge sonst nicht wissen, was sie erwarten. Danke für deinen Beitrag. – holdenweb

3

Für die Zukunft: die Dinge ein wenig, da newforms geändert haben.Dies ist eine Möglichkeit der Nachbestellung Felder aus Basis formclasses Sie keine Kontrolle haben:

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 

Außerdem gibt es ein wenig Dokumentation über die SortedDict dass base_fields hier verwendet: http://code.djangoproject.com/wiki/SortedDict

+0

Verwenden von 'Felder' in meiner Klasse Meta arbeitete für mich (mit einem ModelForm), bis ich einen Grund hatte, einen Anfangswert für ein Fremdschlüsselfeld mit MyFormSet.form.base_fields festzulegen, zu welchem ​​Zeitpunkt die Reihenfolge in 'Felder' war nicht mehr respektiert. Meine Lösung bestand lediglich darin, die Felder im zugrunde liegenden Modell neu zu ordnen. –

11

Felder in der Reihenfolge aufgeführt werden Sie sind in ModelClass._meta.fields definiert. Wenn Sie jedoch die Reihenfolge in Form ändern möchten, können Sie dies mit der Funktion keysOrder tun. Zum Beispiel:

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'] 
0

Mit fields in innerer Meta Klasse ist das, was für mich auf Django==1.6.5 gearbeitet:

#!/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'] 

So einfach ist das.

+1

scheint, wie diese Methode nur gültig für Modelform ist, die normale Form 'django.forms' nicht –

0

Ich habe diese verwendet, um Felder zu bewegen:

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) 

Dies funktioniert in 1.5 und ich bin ziemlich sicher, es funktioniert immer noch in neueren Versionen.

2

Wenn entweder fields = '__all__':

class AuthorForm(ModelForm): 
    class Meta: 
     model = Author 
     fields = '__all__' 

oder exclude verwendet:

class PartialAuthorForm(ModelForm): 
    class Meta: 
     model = Author 
     exclude = ['title'] 

Django Dann verweist die Reihenfolge der Felder als im Modell definiert. Das hat mich nur erwischt, also dachte ich, ich würde es erwähnen. Es ist in den ModelForm docs verwiesen:

Wenn eine dieser verwendet werden, die Reihenfolge der Felder im Formular erscheinen wird die Reihenfolge der Felder im Modell definiert sind, mit ManyToManyField Fällen als letzten.

5

Mit Django> = 1.7 muss auf Ihrem Computer ContactForm.base_fields ändern, wie unten:

from collections import OrderedDict 

... 

class ContactForm(forms.Form): 
    ... 

ContactForm.base_fields = OrderedDict(
    (k, ContactForm.base_fields[k]) 
    for k in ['your', 'field', 'in', 'order'] 
) 

Dieser Trick in Django Admin verwendet wird PasswordChangeForm: Source on Github

+3

Lustig funktioniert, seeked ich Reihenfolge der Felder, wenn, warum, um herauszufinden, Subklassen' PasswordChangeForm' die Reihenfolge der Felder geändert, so dass Ihre Beispiel ist mir sehr nützlich gewesen. Es scheint, dass dies daran liegt, dass 'base_fields' nicht in die Unterklasse übertragen wird. Die Lösung scheint zu sagen 'PasswordChangeFormSubclass.base_fields = PasswordChangeForm.base_fields' nach der Definition von' PasswordChangeFormSubclass – orblivion

+0

@orblivion: verwenden 'ContactForm.base_fields [k]' beim Erstellen des geordneten dict. Es gibt einen Tippfehler in der Antwort, ich werde bearbeiten – dangonfast

4

Ab Django 1.7 Formen OrderedDict verwenden, die nicht unterstützt der Append-Operator Also muss man von Grund auf neu erstellen, das Wörterbuch ...

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 
28

New Django 1.9 ist Form.field_order und Form.order_fields().

+1

Dies ist die Antwort, die ich suche! – sivabudh

+1

ab 1.9 sollte dies die Antwort sein, die neue Sucher betrachten sollten, funktioniert natürlich noch in 1.10 –

1

Der einfachste Weg, Felder in django 1.9 Formen zu bestellen ist field_order in Ihrer Form ist

class ContactForm(forms.Form): 
    subject = forms.CharField(max_length=100) 
    message = forms.CharField() 
    sender = forms.EmailField() 
    field_order = ['sender','message','subject'] 

ein kleines Beispiel hier Form.field_order

verwenden Dies alles in der Reihenfolge zeigt, werden Sie in field_order dict angegeben .