ModelForm 上的 Django 和字段集

Posted

技术标签:

【中文标题】ModelForm 上的 Django 和字段集【英文标题】:Django and fieldsets on ModelForm 【发布时间】:2010-10-05 20:38:54 【问题描述】:

我知道您可以在 django 中为管理员助手指定字段集。但是,我找不到任何对 ModelForms 有用的东西。只是一些我不能使用的补丁。我错过了什么吗?有没有一种方法可以实现类似字段集的功能,而无需手动在模板上的相应标签中写出每个字段。

理想情况下,我希望遍历一组 BoundField。但是,在我的 ModelForm 末尾做这样的事情:

    fieldsets = []
    fieldsets.append(('Personal Information',
                      [username,password,password2,first_name,last_name,email]),) # add a 2 element tuple of string and list of fields
    fieldsets.append(('Terms & Conditions',
                      [acceptterms,acceptprivacy]),) # add a 2 element tuple of string and list of fields

失败,因为我的数据结构中包含的项目是原始字段,而不是 BoundFields。 t 看起来 BoundFields 是动态生成的……这让我很难过。我可以创建我自己的 forms.Form 子类,其中包含字段集的概念(即使是一个不向后兼容的粗略的...这仅适用于我自己的项目),如果是这样,你能指点一下吗?我不想弄乱 django 代码。

【问题讨论】:

【参考方案1】:

我认为this snippet 完全符合您的要求。它为您提供了一个 Form 子类,允许您以声明方式将表单细分为字段集并在模板中遍历它们。

更新:sn-p 已成为 django-form-utils 的一部分

【讨论】:

好极了,它正在我当前的项目中进行! 感谢 django-form-utils!像魅力一样工作。 我也在使用 form-utils,我还为 form-utils 编写了一个扩展,它允许字段在字段集中按行排列......就像管理员一样。我打算联系 form-utils 的作者,看看他是否会接受它作为补丁。同时,如果您愿意,尽管问...【参考方案2】:

模型表单中的字段集仍处于“设计”阶段。在 Django trac 中有一个 ticket 活动度低。

这是我在不久的将来有兴趣自己研究的东西,但由于我还没有这样做,所以我能提供的最好的就是这些 sn-ps:

Form splitting/Fieldset templatetag Sectioned Form Forms splitted in fieldsets

编辑:我刚刚再次注意到这个问题,我意识到需要编辑来指出 Carl 的项目 django-form-utils,其中包含一个 BetterForm 类,该类可以包含字段集。如果您喜欢这个项目,请在下面为他的回答给他 +1 :)

【讨论】:

第二个链接为我提供了实现基本字段集支持所需的线索。谢谢。 十一月2010 年,在 1.2.3 中成功使用了 django-form-utils。感谢您的提示。【参考方案3】:

您可以做的一件事是将逻辑字段集分解为单独的模型表单类。

class PersonalInfoForm (forms.ModelForm):
    class Meta:
        model=MyModel
        fields=('field1', 'field2', ...)

class TermsForm (forms.ModelForm):
    class Meta:
        model=MyModel
        fields=('fieldX', 'fieldY', ...)

将它们以不同的变量传递给您的模板并分解表单集:

<form ...>
   <fieldset><legend>Personal Information</legend>
        personal_info_form 
   </fieldset>
   <fieldset><legend>Terms and Conditions</legend>
        terms_form 
   </fieldset>
</form>

从这个意义上说,您的每个表单类只是实际 html 表单的一个片段。

当您在表单上调用 save 时,它​​会引入一些复杂性。您可能想要传递 commit=False 然后合并结果对象。或者完全避免使用 ModelForm.save 并使用“cleaned_data”手动填充模型对象

【讨论】:

这太费劲了,尤其是在视图层。 @Greg,否决票不会打扰我,但您的评论令人困惑。过度努力是什么意思?将一个单一的表单分解为可以彼此独立操作的单独表单片段是解决此问题的惯用方法。自从我提供此答案以来,Django 可能在两年内添加了更多“字段集”实用程序,但该方法仍然有效。 格雷格的观点是可怕的无效!..这个选项适用且灵活。唯一一次它不起作用,是在“干净”方法中,可能需要来自两个或多个表单字段集的一些数据! 这是一个很好的方法,特别是因为处理许多表单段通常会引入额外的复杂性,例如“如果其他部分未填写,则不要验证该部分”等等......例如逻辑通常以单独的形式更好地处理。【参考方案4】:

Daniel Greenfelds django-uni-form 使用 Layout 助手类解决了这个问题。我现在正在尝试它,它对我来说看起来很干净。

Uniform helpers can use layout objects. A layout can consist of fieldsets, rows, columns, HTML and fields.

我最初选择 Django-uni-form 是因为它符合 section 508。

【讨论】:

【参考方案5】:

你可以使用这个包:https://pypi.org/project/django-forms-fieldset/

pip install django-forms-fieldset

将 forms_fieldset 添加到您的 INSTALLED_APPS 设置中,如下所示:

INSTALLED_APPS = [
    ...
    'forms_fieldset',
]

在您的表单中添加fieldsets

from django.forms import ModelForm

from .models import Student

class StudentForm(ModelForm):
    fieldsets = [
        ("Student Information", 'fields': [
            ('first_name', 'last_name'),
            ('email', 'adress'),
        ]),
        ("Parent Information", 'fields': [
            'mother_name',
            'father_name',
        ]),
    ]
    class Meta:
        model = Student
        fields = '__all__'

在你看来

def home(request):
    form = StudentForm()
    if request.method == 'POST':
        form = Form(request.POST, request.FILES)
        #save...
    context = 
        'form': form,
    
    return render(request, 'home.html', context)

在您的模板中

% load forms_fieldset static %
<link rel="stylesheet" type="text/css" href="% static 'forms_fieldset/css/main.css' %">

<form>
     form|fieldset:'#42945c' 
</form>

【讨论】:

【参考方案6】:

这是我为了理解自定义标签(带有链接)而开发的代码。我应用它来创建一个字段集。

免责声明:我鼓励使用上述任何答案,这只是为了学习。

templatetags/myextras.py:

from django import template
from django.template import Context

register = template.Library()


class FieldsetNode(template.Node):
    """ Fieldset renderer for 'fieldset' tag """
    def __init__(self, nodelist, fieldset_name):
        """ Initialize renderer class
        https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/#writing-the-renderer
        :param nodelist: a list of the template nodes inside a block of 'fieldset'
        :param fieldset_name: the name of the fieldset
        :return: None
        """
        self.nodelist = nodelist
        self.fieldset_name = fieldset_name

    def render(self, context):
        """ Render the inside of a fieldset block based on template file
        https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/#auto-escaping-considerations
        :param context: the previous template context
        :return: HTML string
        """
        t = context.template.engine.get_template('myapp/fieldset.html')
        return t.render(Context(
            'var': self.nodelist.render(context),
            'name': self.fieldset_name,
        , autoescape=context.autoescape))


@register.tag
def fieldset(parser, token):
    """ Compilation function for fieldset block tag
    Render a form fieldset
    https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/#writing-the-compilation-function
    https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/#parsing-until-another-block-tag
    :param parser: template parser
    :param token: tag name and variables
    :return: HTML string
    """
    try:
        tag_name, fieldset_name = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError("%r tag requires a single argument" % token.contents.split()[0])
    if not (fieldset_name[0] == fieldset_name[-1] and fieldset_name[0] in ('"', "'")):
        raise template.TemplateSyntaxError("%r tag's argument should be in quotes" % tag_name)
    nodelist = parser.parse(('endfieldset',))
    parser.delete_first_token()
    return FieldsetNode(nodelist, fieldset_name[1:-1])

templates/myapp/fieldset.html:

<div class="fieldset panel panel-default">
    <div class="panel-heading"> name </div>
    <div class="panel-body"> var </div>
</div>

templates/myapp/myform.html:

<form action="% url 'myapp:myurl' %" method="post">
    % csrf_token %
    % fieldset 'General' %
        form.myfield1 
    % endfieldset %
    # my submit button #
</form>

【讨论】:

以上是关于ModelForm 上的 Django 和字段集的主要内容,如果未能解决你的问题,请参考以下文章

Django - 带有 ModelForm 的属性和重新定义的字段?

Django如何将外键传递给ModelForm字段

Django之ModelForm使用

Django之ModelForm

django form和ModelForm组件

django form 和modelform