Django 表单处理样板的替代方案?

发布于 2024-07-27 19:45:12 字数 749 浏览 12 评论 0 原文

建议的模式在我看来,在视图中处理表单似乎过于复杂且不干燥:

def contact(request):
    if request.method == 'POST': # If the form has been submitted...
        form = ContactForm(request.POST) # A form bound to the POST data
        if form.is_valid(): # All validation rules pass
            # Process the data in form.cleaned_data
            # ...
            return HttpResponseRedirect('/thanks/') # Redirect after POST
    else:
        form = ContactForm() # An unbound form

    return render_to_response('contact.html', {
        'form': form,
    })

有很多条件,它重复 ContactForm() 构造,并且在视图需要处理表单的任何地方都会重复整个块。 难道没有更好的方法吗?

The suggested pattern for processing a form in a view seems overly complex and non-DRY to me:

def contact(request):
    if request.method == 'POST': # If the form has been submitted...
        form = ContactForm(request.POST) # A form bound to the POST data
        if form.is_valid(): # All validation rules pass
            # Process the data in form.cleaned_data
            # ...
            return HttpResponseRedirect('/thanks/') # Redirect after POST
    else:
        form = ContactForm() # An unbound form

    return render_to_response('contact.html', {
        'form': form,
    })

That's a lot of conditionals, it repeats the ContactForm() construction, and the whole block is repeated everywhere a view needs to process a form. Isn't there a better way of doing it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

歌入人心 2024-08-03 19:45:12

当然,您可以避免重复。 大多数情况下,您需要传入要使用的表单类和模板名称作为参数,在提交有效表单时处理清理后的数据的可调用函数,以及处理后重定向的目标; 另外,您需要一些额外的代码来调用表单类一次,以生成绑定或未绑定的表单,并正确处理它。 IE:

def process_any_form(request, 
                     form_class, template_file_name,
                     process_data_callable, redirect_destination):

    form = form_class(request.POST if request.method == 'POST' else None)

    if form.is_bound and form.is_valid():
        process_data_callable(form.cleaned_data)
        return HttpResponseRedirect(redirect_destination)

    return render_to_response(template_file_name, {'form': form})

You can avoid the repetition, of course. Mostly, you need to pass in as arguments the class of form and template name to use, a callable to process the cleaned data when a valid form is submitted, and a destination for the redirect after such processing; plus, you need a little extra code to call the form class just once, to produce either a bound or unbound form, and deal with it properly. I.e.:

def process_any_form(request, 
                     form_class, template_file_name,
                     process_data_callable, redirect_destination):

    form = form_class(request.POST if request.method == 'POST' else None)

    if form.is_bound and form.is_valid():
        process_data_callable(form.cleaned_data)
        return HttpResponseRedirect(redirect_destination)

    return render_to_response(template_file_name, {'form': form})
情域 2024-08-03 19:45:12

你是对的,它可能会更好,这是一个更好的选择(但请继续阅读):

def contact(request):
     form = ContactForm(request.POST or None) # A form bound to the POST data
     if form.is_valid(): # All validation rules pass
        # Process the data in form.cleaned_data
        # ...
        return HttpResponseRedirect('/thanks/') # Redirect after POST

     return render_to_response('contact.html', {
        'form': form,
     })

此片段来自名为 高级 Django 表单使用

请注意,如果所有字段都是可选的并且您不使用 CSRF 保护,这会将空表单处理为有效(甚至在提交之前)。 因此,为了消除这种风险,您最好使用以下方法:

    def contact(request):
     form = ContactForm(request.POST or None) # A form bound to the POST data
     if request.method == 'POST' and form.is_valid(): # All validation rules pass
        # Process the data in form.cleaned_data
        # ...
        return HttpResponseRedirect('/thanks/') # Redirect after POST

     return render_to_response('contact.html', {
        'form': form,
     })

You are right it could be better, here is a better alternative (but keep reading):

def contact(request):
     form = ContactForm(request.POST or None) # A form bound to the POST data
     if form.is_valid(): # All validation rules pass
        # Process the data in form.cleaned_data
        # ...
        return HttpResponseRedirect('/thanks/') # Redirect after POST

     return render_to_response('contact.html', {
        'form': form,
     })

This snippet comes from a talk called Advanced Django Form Usage from DjangoCon11.

Note that this will process an empty form as valid (even before submission) if all the fields are optional and you don't use CSRF protection. So to eliminate that risk, you better use this one:

    def contact(request):
     form = ContactForm(request.POST or None) # A form bound to the POST data
     if request.method == 'POST' and form.is_valid(): # All validation rules pass
        # Process the data in form.cleaned_data
        # ...
        return HttpResponseRedirect('/thanks/') # Redirect after POST

     return render_to_response('contact.html', {
        'form': form,
     })
好久不见√ 2024-08-03 19:45:12

处理表单的样板方式混合了两个问题:呈现表单以进行编辑和处理结果。 您可以将其分为两种方法,这会以相同的 render_to_response() 调用的形式引入一些重复。 当您重构它时,您可能会得到比上面的单一方法形式可读性更差的东西。

当我查看样板方法时,我没有看到重复。 ContactForm() 的两种用法截然不同。 在我看来,这两个条件相当清楚地显示了处理表单所涉及的状态转换(呈现空白表单、接受提交直到表单有效、处理并重定向)。

The boilerplate way of processing forms mixes two concerns: presenting a form to edit and processing the results. You could break this into two methods, which would introduce some duplication in the form of identical render_to_response() calls. By the time you refactored that, you might end up with something that's less readable than the single-method form above.

When I look at the boilerplate method, I don't see duplication. The two uses of ContactForm() are distinctly different. The two conditionals seem to me to fairly cleanly show the state transitions involved in processing a form (present a blank form, accept submissions until one is valid, process-and-redirect).

我很坚强 2024-08-03 19:45:12

Alex 的通用处理程序打败了我,但 FWIW 我们倾向于他的建议的不太通用的版本:

def contact(request):
    post_data = request.POST if request.method == 'POST' else None
    form = ContactForm(post_data)
    if request.method == 'POST':
        # perform normal validation checking, etc

    return render_to_response('contact.html', {
        'form': form,
         })

如果 post_data 为 None,则表单被实例化为无界。 否则,绑定处理将照常继续。 它避免了 ContactForm 的重复构造,但我同意 Dave 的回答,即重复构造不会因为构造参数不同而让我因为重复构造而烦恼。

Alex's generic handler beat me to it, but FWIW we tend toward a less-generic version of his suggestion:

def contact(request):
    post_data = request.POST if request.method == 'POST' else None
    form = ContactForm(post_data)
    if request.method == 'POST':
        # perform normal validation checking, etc

    return render_to_response('contact.html', {
        'form': form,
         })

If post_data is None, then the form is instantiated as being unbounded. Otherwise, bound processing continues as normal. It avoids a duplicated construction of ContactForm, but I agree with Dave's answer that the duplicate construction doesn't bother me as being a duplicate precisely because the construction parameters are different.

楠木可依 2024-08-03 19:45:12

我对此感到非常厌倦,因此我编写了自己的通用视图来处理它。 在此过程中,我发现 django 已经有文档不足的泛型用于表单处理的视图。 它们与记录的通用视图相当直接类似,但接受表单,并且基本上遵循您在示例中使用的相同模板。 最终,我发现它们对于我的使用来说太不灵活和愚蠢(我不需要 create_or_update 视图,我不想单独处理这两个操作。)

编辑:你不喜欢 Fragsworth 的答案,它指向同一件事我说的是,我想你也不会喜欢我的。 以下是其工作原理的示例。

# in urls.py
urlpatterns += patterns("", 
    (u'^...

ContactForm 必须有一个 save() 方法,这就是表单处理逻辑所在的位置。

, 'django.views.generic.create_update.update', { 'form_class': ContactForm }) )

ContactForm 必须有一个 save() 方法,这就是表单处理逻辑所在的位置。

I got so tired of this that i wrote my own generic views to handle it. In the process, I discovered that django already has underdocumented generic views for forms processing. They are fairly direct analogues of the documented generic views, but accept forms, and basically follow the same template you used in your example. Ultimately, I found them too inflexible and stupid for my use (I don't want a create_or_update view, I wan't to treat those two actions seperately.)

Edit: You didn't like Fragsworth's answer, which points to the same thing i'm talking about, I assume you wont' like mine either. Here's an example for how it works.

# in urls.py
urlpatterns += patterns("", 
    (u'^...

ContactForm must have a save() method, and thats where your form processing logic goes.

, 'django.views.generic.create_update.update', { 'form_class': ContactForm }) )

ContactForm must have a save() method, and thats where your form processing logic goes.

2024-08-03 19:45:12

人们可以编写一个函数来处理所有形式的条件。 您可以通过在“is_valid”之后传入特定于该表单的函数来完成此操作,例如:

def FormHandler(request, CleaningFunction, redirecturl):
    if request.method = 'POST':
        if request.method == 'POST': # If the form has been submitted...
            form = ContactForm(request.POST) # A form bound to the POST data
            if form.is_valid(): # All validation rules pass
                CleaningFunction(form) # Process the data in form.cleaned_data
                return HttpResponseRedirect('/thanks/') # Redirect after POST
     else:
         form = ContactForm() # An unbound form
     return form

然后您可以从您的视图中调用 FormHandler。 请注意,这未经测试,可能有错误。

One could write a function that handles the conditionals for all forms. You could do this by passing in a function specific to that form after "is_valid", such as:

def FormHandler(request, CleaningFunction, redirecturl):
    if request.method = 'POST':
        if request.method == 'POST': # If the form has been submitted...
            form = ContactForm(request.POST) # A form bound to the POST data
            if form.is_valid(): # All validation rules pass
                CleaningFunction(form) # Process the data in form.cleaned_data
                return HttpResponseRedirect('/thanks/') # Redirect after POST
     else:
         form = ContactForm() # An unbound form
     return form

Then you would call FormHandler from your view. Note this isn't tested and may have errors.

恰似旧人归 2024-08-03 19:45:12

你可以绕过 django 的表单模块,只用老式的方式来做,你可以获得更大的灵活性,而不会造成太多损失,恕我直言。

上次我查看 django 表单已经是很久以前的事情了,我不知道事情是否发生了变化,但是,例如,它实际上不允许您构建 ajax 风格的表单; 至少不容易。

You can bypass django's forms module and just do it the old fashion way, you get more flexibility without too much loss IMHO.

Last time I looked at django forms was quite a while ago, I don't know if things have changed, but for instance, it doesn't really allow you build an ajax-style form; at least not easily.

山人契 2024-08-03 19:45:12

Django 提供了几个通用视图来创建、编辑和删除对象。 也许你可以尝试这些。

Django provides several generic views for creating, editing, and deleting objects. Perhaps you could try these.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文