【问题标题】:Displaying validation errors at the right position in a Django page containing multiple form fields在包含多个表单字段的 Django 页面中的正确位置显示验证错误
【发布时间】:2017-02-21 05:32:40
【问题描述】:

在我搭建的一个 Django 社交网站中,用户可以在普通房间里聊天,或者创建私人群组。

每个用户都有一个主仪表板,他们参与的所有对话都显示在一起,彼此堆叠(按 20 个对象进行分页)。我称之为unseen activity 页面。此页面上的每个看不见的对话都有一个文本框,用户可以直接在其中输入回复。此类回复通过 <form> 内的 POST 请求提交。

每个<form>action 属性指向不同的网址,具体取决于提交的回复类型(例如home_commentgroup_reply)。这是因为它们有不同的验证和处理要求等。

问题是这样的:如果引发了 ValidationError(例如,用户输入了带有禁止字符的回复),它会显示在unseen_activity 中的多个表单上页面,而不仅仅是生成它的特定表单。如何确保所有 ValidationErrors 仅出现在它们起源的表单上?一个说明性的例子会很棒!


附加到所有这些的表单类称为UnseenActivityForm,并定义如下:

class UnseenActivityForm(forms.Form):
    comment = forms.CharField(max_length=250)
    group_reply = forms.CharField(max_length=500)
    class Meta:
        fields = ("comment", "group_reply", )

    def __init__(self,*args,**kwargs):
        self.request = kwargs.pop('request', None)
        super(UnseenActivityForm, self).__init__(*args, **kwargs)

    def clean_comment(self):
        # perform some validation checks
        return comment

    def clean_group_reply(self):
        # perform some validation checks
        return group_reply

模板如下所示:

{% for unseen_obj in object_list %}

    {% if unseen_obj.type == '1' %}

    {% if form.comment.errors %}{{ form.comment.errors.0 }}{% endif %}
    <form method="POST" action="{% url 'process_comment' pk %}">
    {% csrf_token %}
    {{ form.comment }}
    <button type="submit">Submit</button>
    </form>

    {% if unseen_obj.type == '2' %}

    {% if form.group_reply.errors %}{{ form.group_reply.errors.0 }}{% endif %}
    <form method="POST" action="{% url 'process_group_reply' pk %}">
    {% csrf_token %}
    {{ form.group_reply }}
    <button type="submit">Submit</button>
    </form>

    {% endif %}

{% endfor %}

现在是观点。我不会一次性处理所有内容。一个函数负责为 GET 请求生成内容,其他函数负责处理 POST 数据处理。如下:

def unseen_activity(request, slug=None, *args, **kwargs):
        form = UnseenActivityForm()
        notifications = retrieve_unseen_notifications(request.user.id)
        page_num = request.GET.get('page', '1')
        page_obj = get_page_obj(page_num, notifications, ITEMS_PER_PAGE)
        if page_obj.object_list:
            oblist = retrieve_unseen_activity(page_obj.object_list)
        else:
            oblist = []
        context = {'object_list': oblist, 'form':form, 'page':page_obj,'nickname':request.user.username}
        return render(request, 'user_unseen_activity.html', context)

def unseen_reply(request, pk=None, *args, **kwargs):
        if request.method == 'POST':
            form = UnseenActivityForm(request.POST,request=request)
            if form.is_valid():
                # process cleaned data
            else:
                notifications = retrieve_unseen_notifications(request.user.id)
                page_num = request.GET.get('page', '1')
                page_obj = get_page_obj(page_num, notifications, ITEMS_PER_PAGE)
                if page_obj.object_list:
                    oblist = retrieve_unseen_activity(page_obj.object_list)
                else:
                    oblist = []
                context = {'object_list': oblist, 'form':form, 'page':page_obj,'nickname':request.user.username}
                return render(request, 'user_unseen_activity.html', context)

def unseen_group_reply(group_reply, pk=None, *args, **kwargs):
            #similar processing as unseen_reply

注意:代码是我实际代码的简化版本。如果需要,请询问更多详细信息。

【问题讨论】:

  • 请同时添加您正在创建表单和看不见的对象的视图。看起来您对所有看不见的对象使用相同的表单。查看模板,似乎所有表单都使用了相同的 URL。
  • @AKS:确实,你是对的。我也添加了视图,看看。这里有什么解决方法?
  • 您需要为每个看不见的活动创建单独的表单,以确保只有与特定活动相关的表单才会显示错误。此外,您在每个表单中使用的 action 根本没有使用 pk url 参数。
  • @AKS:抱歉,action 被错误地显示了 - 我已经修改了它(在我的真实代码中,我确实包含了 pk)。回复:为每个 unseen_activity 制作单独的表格,这是让我感到困惑的部分。我在 for 循环中生成表单 - 可能有许多看不见的活动(所以我将其分页 20)。我将如何在这里手动创建具有唯一身份的它们?似乎无法绕过它。可以举个例子吗?
  • 您在 for 循环中使用的 form 在视图中创建并通过上下文传递给模板。因此,您对所有看不见的活动使用相同的表单实例。您需要做的是为视图上下文本身中的每个看不见的活动创建一个单独的表单。并且,在表单和活动之间有一个映射,您可以使用它稍后呈现表单。

标签: django django-forms


【解决方案1】:

根据上述 cmets 中的讨论:

我的建议是为视图中的每个实例创建一个表单。我已经重构了您的代码,使其具有返回对象列表的函数,您可以在 unseen_replygroup_reply 函数中使用它:

def get_object_list_and_forms(request):
    notifications = retrieve_unseen_notifications(request.user.id)
    page_num = request.GET.get('page', '1')
    page_obj = get_page_obj(page_num, notifications, ITEMS_PER_PAGE)
    if page_obj.object_list:
        oblist = retrieve_unseen_activity(page_obj.object_list)
    else:
        oblist = []

    # here create a forms dict which holds form for each object 
    forms = {}
    for obj in oblist:
        forms[obj.pk] = UnseenActivityForm()

    return page_obj, oblist, forms


def unseen_activity(request, slug=None, *args, **kwargs):
    page_obj, oblist, forms = get_object_list_and_forms(request)

    context = {
        'object_list': oblist,
        'forms':forms,
        'page':page_obj,
        'nickname':request.user.username
    }
    return render(request, 'user_unseen_activity.html', context)

现在,您需要使用对象 ID from forms dict 访问模板中的表单。

{% for unseen_obj in object_list %}
    <!-- use the template tag in the linked post to get the form using obj pk -->
    {% with forms|get_item:unseen_obj.pk as form %}
        {% if unseen_obj.type == '1' %}

            {% if form.comment.errors %}{{ form.comment.errors.0 }}{% endif %}
            <form method="POST" action="{% url 'process_comment' pk %}">
                {% csrf_token %}
                {{ form.comment }}
                <button type="submit">Submit</button>
            </form>

        {% elif unseen_obj.type == '2' %}

            {% if form.group_reply.errors %}{{ form.group_reply.errors.0 }}{% endif %}
            <form method="POST" action="{% url 'process_group_reply' pk %}">
                {% csrf_token %}
                {{ form.group_reply }}
                <button type="submit">Submit</button>
            </form>

        {% endif %}
    {% endwith %}
{% endfor %}

在处理回复时,您再次需要附加与特定对象 pk 引发错误的表单:

def unseen_reply(request, pk=None, *args, **kwargs):
    if request.method == 'POST':
        form = UnseenActivityForm(request.POST,request=request)
        if form.is_valid():
        # process cleaned data
        else:
            page_obj, oblist, forms = get_object_list_and_forms(request)

            # explicitly set the form which threw error for this pk
            forms[pk] = form

            context = {
                'object_list': oblist,
                'forms':forms,
                'page':page_obj,
                'nickname':request.user.username
            }
            return render(request, 'user_unseen_activity.html', context)

【讨论】:

  • 我明白了。非常细致入微的答案,令人兴奋的是现在尝试一下。稍后我会回复你,谢谢大家的插话 :-)
  • 顺便说一句,我在思考这个问题时很好奇:formsets 是否可以解决我的特定问题?
猜你喜欢
  • 2020-11-22
  • 2021-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-14
  • 1970-01-01
  • 1970-01-01
  • 2018-11-23
相关资源
最近更新 更多