代码之家  ›  专栏  ›  技术社区  ›  hannson

Django窗体、窗体字段的继承和顺序

  •  61
  • hannson  · 技术社区  · 16 年前

    我正在我的网站上使用django表单,希望控制字段的顺序。

    以下是我如何定义表单:

    class edit_form(forms.Form):
        summary = forms.CharField()
        description = forms.CharField(widget=forms.TextArea)
    
    
    class create_form(edit_form):
        name = forms.CharField()
    

    名称是不可变的,仅应在创建实体时列出。我使用继承来添加一致性和干涸的原则。事实上,完全可以预料,所发生的并不是错误的,是名称字段在视图/html中最后一个列出,但我希望名称字段位于摘要和描述的顶部。我确实意识到,通过将摘要和描述复制到创建表单中并释放继承,我可以很容易地修复它,但我想知道这是否可行。

    为什么? 想象一下,在编辑表单中有100个字段,在创建表单的顶部添加10个字段,那么复制和维护这两个表单看起来就不那么性感了。(这是 我的情况,我只是举个例子)

    那么,我如何才能覆盖这个行为呢?

    编辑:

    显然,要做到这一点,不经过讨厌的黑客攻击(摆弄.field属性)是没有合适的方法的。.field属性是sorteddict(Django的内部数据结构之一),它不提供任何重新排序键:值对的方法。它提供了一种在给定索引中插入项的方法,但这会将项从类成员移到构造函数中。此方法可以工作,但会降低代码的可读性。另一种我认为合适的方法是修改框架本身,这在大多数情况下都不太理想。

    简而言之,代码会变成这样:

    class edit_form(forms.Form):
        summary = forms.CharField()
        description = forms.CharField(widget=forms.TextArea)
    
    
    class create_form(edit_form):
        def __init__(self,*args,**kwargs):
            forms.Form.__init__(self,*args,**kwargs)
    
            self.fields.insert(0,'name',forms.CharField())
    

    让我闭嘴:)

    10 回复  |  直到 16 年前
        1
  •  97
  •   Maxime Lorant Ashana.Jackol    7 年前

    自Django 1.9+

    Django 1.9添加了一个新的 Form 属性, field_order ,允许对字段排序,而不管它们在类中的声明顺序如何。

    class MyForm(forms.Form):
        summary = forms.CharField()
        description = forms.CharField(widget=forms.TextArea)
        author = forms.CharField()
        notes = form.CharField()
    
        field_order = ['author', 'summary']
    

    中缺少字段 场序 在类中保持它们的顺序,并在列表中指定的顺序之后追加。上面的示例将按以下顺序生成字段: ['author', 'summary', 'description', 'notes']

    请参阅文档: https://docs.djangoproject.com/en/stable/ref/forms/api/#notes-on-field-ordering

    至Django 1.6

    我也遇到了同样的问题,我发现了另一种在 Django CookBook :

    class EditForm(forms.Form):
        summary = forms.CharField()
        description = forms.CharField(widget=forms.TextArea)
    
    
    class CreateForm(EditForm):
        name = forms.CharField()
    
        def __init__(self, *args, **kwargs):
            super(CreateForm, self).__init__(*args, **kwargs)
            self.fields.keyOrder = ['name', 'summary', 'description']
    
        2
  •  16
  •   François Constant    8 年前

    从Django 1.9: https://docs.djangoproject.com/en/1.10/ref/forms/api/#notes-on-field-ordering


    原始答案: django 1.9默认情况下将在表单上支持此功能 field_order :

    class MyForm(forms.Form):
        ...
        field_order = ['field_1', 'field_2']
        ...
    

    https://github.com/django/django/commit/28986da4ca167ae257abcaf7caea230eca2bcd80

        3
  •  11
  •   Joshua    15 年前

    我使用了Selene发布的解决方案,但发现它删除了所有未分配给keyorder的字段。我要子类化的表单有很多字段,所以这对我来说不是很好。我用Akaihola的答案编写了这个函数来解决这个问题,但是如果你想让它像Selene那样工作,你需要做的就是设置 throw_away True .

    def order_fields(form, field_list, throw_away=False):
        """
        Accepts a form and a list of dictionary keys which map to the
        form's fields. After running the form's fields list will begin
        with the fields in field_list. If throw_away is set to true only
        the fields in the field_list will remain in the form.
    
        example use:
        field_list = ['first_name', 'last_name']
        order_fields(self, field_list)
        """
        if throw_away:
            form.fields.keyOrder = field_list
        else:
            for field in field_list[::-1]:
                form.fields.insert(0, field, form.fields.pop(field))
    

    这就是我在自己的代码中使用它的方式:

    class NestableCommentForm(ExtendedCommentSecurityForm):
        # TODO: Have min and max length be determined through settings.
        comment = forms.CharField(widget=forms.Textarea, max_length=100)
        parent_id = forms.IntegerField(widget=forms.HiddenInput, required=False)
    
        def __init__(self, *args, **kwargs):
            super(NestableCommentForm, self).__init__(*args, **kwargs)
            order_fields(self, ['comment', 'captcha'])
    
        4
  •  10
  •   Ted    11 年前

    似乎在某一点上,字段顺序的底层结构从特定于Django的 SordedDict 以python标准 OrderedDict

    因此,在1.7中,我必须做以下工作:

    from collections import OrderedDict
    
    class MyForm(forms.Form):
    
        def __init__(self, *args, **kwargs):
            super(MyForm, self).__init__(*args, **kwargs)
            original_fields = self.fields
            new_order = OrderedDict()
            for key in ['first', 'second', ... 'last']:
                new_order[key] = original_fields[key]
            self.fields = new_order
    

    我相信有人可以把它打成两到三条线,但为了S.O.的目的,我认为清楚地展示它的工作原理比劈刀更好。

        5
  •  7
  •   Jan Pöschko    14 年前

    您还可以创建一个装饰器来订购字段(受Joshua解决方案的启发):

    def order_fields(*field_list):
        def decorator(form):
            original_init = form.__init__
            def init(self, *args, **kwargs):
                original_init(self, *args, **kwargs)        
                for field in field_list[::-1]:
                    self.fields.insert(0, field, self.fields.pop(field))
            form.__init__ = init
            return form            
        return decorator
    

    这将确保传递给装饰器的所有字段都位于第一位。 您可以这样使用它:

    @order_fields('name')
    class CreateForm(EditForm):
        name = forms.CharField()
    
        6
  •  6
  •   Thom Wiggers    11 年前

    接受答案的方法使用了Django 1.7中更改的内部Django Forms API。 The project team's opinion is that it should never have been used in the first place. 现在,我使用此函数对表单重新排序。此代码使用 OrderedDict :

    def reorder_fields(fields, order):
        """Reorder form fields by order, removing items not in order.
    
        >>> reorder_fields(
        ...     OrderedDict([('a', 1), ('b', 2), ('c', 3)]),
        ...     ['b', 'c', 'a'])
        OrderedDict([('b', 2), ('c', 3), ('a', 1)])
        """
        for key, v in fields.items():
            if key not in order:
                del fields[key]
    
        return OrderedDict(sorted(fields.items(), key=lambda k: order.index(k[0])))
    

    我在这类课程中用到:

    class ChangeOpenQuestion(ChangeMultipleChoice):
    
        def __init__(self, *args, **kwargs):
            super(ChangeOpenQuestion, self).__init__(*args, **kwargs)
            key_order = ['title',
                         'question',
                         'answer',
                         'correct_answer',
                         'incorrect_answer']
    
            self.fields = reorder_fields(self.fields, key_order)
    
        7
  •  3
  •   Community Mohan Dere    8 年前

    请参阅中的注释 this SO question 在Django的内部结构跟踪字段顺序的过程中,答案包括如何根据您的喜好“重新排序”字段的建议(最终归结为与 .fields 属性)。

        8
  •  2
  •   akaihola dnlcrl    16 年前

    更改字段顺序的其他方法:

    弹出式和插入式:

    self.fields.insert(0, 'name', self.fields.pop('name'))
    

    流行和追加:

    self.fields['summary'] = self.fields.pop('summary')
    self.fields['description'] = self.fields.pop('description')
    

    全部弹出并追加:

    for key in ('name', 'summary', 'description'):
        self.fields[key] = self.fields.pop(key)
    

    有序副本:

    self.fields = SortedDict( [ (key, self.fields[key])
                                for key in ('name', 'summary' ,'description') ] )
    

    但塞莱恩在《姜戈食谱》中的做法仍然让人感觉最清楚。

        9
  •  1
  •   Daniel Sokolowski    13 年前

    基于@akaihola的回答,并更新为与最新的django 1.5合作 self.fields.insert 正在被 depreciated .

    from easycontactus.forms import *
    from django import forms
    class  CustomEasyContactUsForm(EasyContactUsForm):
        ### form settings and configuration
        CAPTHCA_PHRASE = 'igolf'
    
        ### native methods
        def __init__(self, *args, **kwargs):
            super(CustomEasyContactUsForm, self).__init__(*args, **kwargs)
            # re-order placement of added attachment field 
            self.fields.keyOrder.insert(self.fields.keyOrder.index('captcha'),
                                        self.fields.keyOrder.pop(self.fields.keyOrder.index('attachment'))
                                        )
    
        ### field defintitions
        attachment = forms.FileField()
    

    在上面,我们正在扩展easycontactusForm基类,正如在中定义的那样。 django-easycontactus 包裹。

        10
  •  0
  •   Utkarsh Sinha    10 年前

    我构建了一个从django registration redux的“registration form”继承的表单“exregistrationform”。我面临两个问题,其中一个是在创建新表单后重新排序HTML输出页上的字段。

    我解决了如下问题:

    1。问题1:从注册表中删除用户名:在my_app.forms.py中

        class ExRegistrationForm(RegistrationForm):
              #Below 2 lines extend the RegistrationForm with 2 new fields firstname & lastname
              first_name = forms.CharField(label=(u'First Name'))
              last_name = forms.CharField(label=(u'Last Name'))
    
              #Below 3 lines removes the username from the fields shown in the output of the this form
              def __init__(self, *args, **kwargs):
              super(ExRegistrationForm, self).__init__(*args, **kwargs)
              self.fields.pop('username')
    

    2。问题2:使firstname和firstname出现在最上面:在templates/registration/registration.html中

    可以按所需的顺序单独显示字段。如果字段的数量较少,这将有所帮助,但如果您有大量字段,而实际上不可能在表单中实际写入它们,则情况并非如此。

         {% extends "base.html" %}
         {% load i18n %}
    
         {% block content %}
         <form method="post" action=".">
              {% csrf_token %}
    
              #The Default html is: {{ form.as_p }} , which can be broken down into individual elements as below for re-ordering.
              <p>First Name: {{ form.first_name }}</p>
              <p>Last Name:  {{ form.last_name }}</p>
              <p>Email: {{ form.email }}</p>
              <p>Password: {{ form.password1 }}</p>
              <p>Confirm Password: {{ form.password2 }}</p>
    
              <input type="submit" value="{% trans 'Submit' %}" />
          </form>
          {% endblock %}