web-dev-qa-db-fra.com

Comment Django connaît-il l'ordre de rendre les champs du formulaire?

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

84
Greg

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

[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
89
holdenweb

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']
50
Steve Tjoa

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']
11
Hugh Saunders

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

6
Zulu

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

5
zgoda

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

5
nosklo

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 
4
Paul Kenjora

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

3
Stijn Debrouwere

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.

2
Paul J

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.

2
Tahir Fazal

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.

1
ariel17

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.

0
Ian Rolfe

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.

0
Sam Corder