Django, Form Alanlarını Oluşturma Sırasını Nasıl Biliyor?


91

Aşağıdaki gibi bir Django formum varsa:

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

Ve bu formun bir örneğinin as_table () yöntemini çağırıyorum, Django alanları yukarıda belirtildiği gibi aynı sırada işleyecektir.

Sorum şu ki, Django sınıf değişkenlerinin tanımlandığı sırayı nasıl biliyor?

(Ayrıca bu sıralamayı nasıl geçersiz kılabilirim, örneğin classe'ın başlatma yönteminden bir alan eklemek istediğimde ?)

Yanıtlar:


89

[NOT: bu cevap artık tamamen güncel değil - lütfen aşağıdaki tartışmaya ve daha yeni cevaplara bakın].

Eğer fbir form onun alanlardır olan f.fieldsbir olduğunu django.utils.datastructures.SortedDict (onlar eklenir sırayla öğeler sunar). Form oluşturulduktan sonra f.fields, sunulmaları gereken sırayla alan adlarını içeren bir liste olan keyOrder özniteliğine sahiptir. Bunu doğru sıralamaya ayarlayabilirsiniz (ancak öğeleri atlamadığınızdan veya ekstra eklemediğinizden emin olmak için dikkatli olmanız gerekir).

İşte mevcut projemde yeni oluşturduğum bir örnek:

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

20
Bir süredir alanlar, bir ModelForm'un 'alanlar' özniteliği tarafından belirtildiği gibi sıralanacaktır. Gönderen docs.djangoproject.com/en/1.3/topics/forms/modelforms/... formu bunları oluşturulduğunda isimleri bu listede belirtilen alanlar saygı sıradır. Bu yalnızca ModelForms için geçerlidir - yaklaşımınız, özellikle ek alanlar ekleyen form alt sınıflarında, normal formlar için hala oldukça kullanışlıdır.
Chris Lawlor

3
Bu, bazı alanları geçersiz kılan ama diğerlerini geçersiz kılan ilginç şeyler yaptığınızda işe yarar. +1
Nathan Keller

"Bu" Chris Lawlor'un önerisi mi? Bu cevap, muhtemelen artık güncel olmayacak kadar eskidir (ancak muhtemelen 1.2 için iyiydi). Güvenilir olmayan bilgilerin işaretlendiğinden emin olmak önemlidir, aksi takdirde yeni gelenler neye güveneceklerini bilemezler. Girdiniz için teşekkürler.
holdenweb

72

Django 1.9'da yeni olan Form.field_order ve Form.order_fields () 'dir .

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

1
Aradığım cevap bu!
sivabudh

1
Bu yeni arama yapanlar bakmak gerekir cevap olmalıdır 1.9 olarak, belli ki hala 1.10 çalışır
Brian H.

2
Not: "field_order" değil, "field_order" ('s' yok)
Davy

1
Hatta sipariş edilmesini istediğiniz form alanlarının yalnızca bir alt kümesini bile belirtebilirsiniz
danleyb2

40

Devam ettim ve kendi sorumu cevapladım. İleride başvurmak için cevap şu şekildedir:

Django'da form.py, __new__sınıf değişkenlerinizi en sonunda sınıfta self.fieldstanımlanan sırayla yüklemek için yöntemi kullanarak bazı kara büyü yapar . self.fieldsbir Django SortedDictörneğidir (içinde tanımlanmıştır datastructures.py).

Öyleyse bunu geçersiz kılmak için, örneğimde gönderenin ilk gelmesini istediniz ancak bunu bir init yöntemine eklemeniz gerektiğini varsayalım :

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

12
En üstte 'gönderen' alanını normal olarak tanımlayabilir ve ardından ekleme satırınızda bir varyasyon kullanabilirsiniz:self.fields.insert( 0, 'sender', self.fields['sender'] )
EvdB

4
Bu, artık Django 1.7'de çalışmıyor çünkü form alanları, eklemeyi desteklemeyen OrderedDict'i kullanıyor. Aşağıdaki güncellenmiş
cevabıma

11

Alanlar, ModelClass._meta.fields içinde tanımlandıkları sırada listelenir. Ancak Formdaki sırayı değiştirmek istiyorsanız, keyOrder işlevini kullanarak yapabilirsiniz. Örneğin :

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

6

Django> = 1.7 ile ContactForm.base_fieldsaşağıdaki gibi değiştirmeniz gerekir :

from collections import OrderedDict

...

class ContactForm(forms.Form):
    ...

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

Bu numara Django Admin PasswordChangeForm: Github'da Kaynak'ta kullanılır


3
Komik, alt sınıfların PasswordChangeFormalanların sırasını neden değiştirdiğini anlamaya çalışırken alanların sıralanmasını aradım , bu yüzden örneğiniz benim için çok faydalı oldu. Görünüşe göre base_fieldsalt sınıfa taşınmadığı için. Çözüm PasswordChangeFormSubclass.base_fields = PasswordChangeForm.base_fields, `` PasswordChangeFormSubclass
orblivion

@orblivion: ContactForm.base_fields[k]sıralı dikteyi oluştururken kullanın .
Cevapta

5

Form alanlarında, oluşturma sırası için adı verilen bir öznitelik vardır creation_counter. .fieldsöznitelik bir sözlüktür, bu yüzden sözlüğe eklemek ve creation_counteryeni sıralamayı yansıtmak için tüm alanlardaki öznitelikleri değiştirmek yeterli olacaktır (bunu asla denemeyin).


5

Field sınıfında bir sayaç kullanın. Bu sayaca göre sırala:

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

Çıktı:

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


4

Django 1.7 formlarından itibaren, ekleme operatörünü desteklemeyen OrderedDict kullanılır. Yani sözlüğü sıfırdan yeniden oluşturmanız gerekiyor ...

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 

Python 3.2'den beri, öğelerin başına veya sonuna eklemek için OrderedDict.move_to_end () kullanabilirsiniz.
bparker

3

İleride referans olması için: Yeni biçimlerden bu yana işler biraz değişti. Bu, üzerinde kontrolünüz olmayan temel form sınıflarından alanları yeniden düzenlemenin bir yoludur:

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

Ayrıca, burada kullanılan SortedDict hakkında biraz belge base_fieldsvar: http://code.djangoproject.com/wiki/SortedDict


MyFormSet.form.base_fields kullanarak bir yabancı anahtar alanı için bir başlangıç ​​değeri ayarlamak için bir nedenim olana kadar Meta sınıfımdaki 'alanları' kullanmak benim için çalışıyordu (ModelForm ile), bu noktada 'alanlar'daki sıra hayırdı daha uzun süre saygı duyulur. Benim çözümüm aslında sadece temel modeldeki alanları yeniden sıralamaktı.
Paul J

2

İkisinden biri varsa fields = '__all__':

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

veya excludekullanılır:

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

Sonra Django , modelde tanımlanan alanların sırasına başvurur . Bu beni az önce yakaladı, ben de bundan bahsedeyim dedim. ModelForm belgelerinde başvurulmaktadır :

Bunlardan herhangi biri kullanılırsa, alanların formda görünme sırası, alanların modelde tanımlandığı sıra olur ve ManyToManyField örnekleri en son görünür.


2

Django 1.9 formlarında alan sipariş etmenin en kolay yolu Form.field_orderfield_order formunuzda kullanmaktır

İşte küçük bir örnek

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

Bu, her şeyi field_orderdiktede belirttiğiniz sırayla gösterecektir .


1

fieldsİç Metasınıfta kullanmak benim için işe yarayan şeydi 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']

Kadar basit.


1
Görünüşe göre bu yöntem sadece model formu için geçerli, normal form django.formsçalışmıyor
Pengfei. X

1

Bunu, alanları taşımak için kullandım:

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)

Bu 1.5'te çalışıyor ve hala daha yeni sürümlerde çalıştığından eminim.


0

Form sınıfını tanımlamada kullanılan meta sınıfı ile ilgilidir. Alanların dahili bir listesini tuttuğunu düşünüyorum ve listenin ortasına eklerseniz işe yarayabilir. O koda bakmayalı uzun zaman oldu.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.