如何在 Django 中修改 Bootstrap 4 表单验证信息呈现方式?

Posted

技术标签:

【中文标题】如何在 Django 中修改 Bootstrap 4 表单验证信息呈现方式?【英文标题】:How to modify Bootstrap 4 form validation information presentation in Django? 【发布时间】:2021-03-11 05:08:08 【问题描述】:

我正在使用 Django 和 Bootstrap 4。我想自定义表单验证信息在我正在使用的模板中的显示方式。当当前模板发生错误时,无效的输入元素有红色轮廓,错误消息显示为弹出窗口。另外,正确输入的输入元素没有绿色轮廓,它们只是没有轮廓。

我想做以下事情:

发生错误时在字段下方显示错误消息 为输入正确的字段添加绿色轮廓

我尝试了多种解决方案,包括:

this tutorial this GitHub gist the Stack Overflow answers from this question

我的情况没有任何效果。

我当前的代码:

submit_job_listing.html(模板):

% extends "base.html" %
% block title % Submit a job % endblock %
% block nav_item_post_a_job %active% endblock nav_item_post_a_job %
% block head %
     job_listing_form.media.css 
% endblock %
% block content %

    <h1>Submit a job listing</h1>
    <div class="container">
      <form method="post" enctype="multipart/form-data">
          % csrf_token %
          <div class="row text-center">
            <h2> <b>Job listing information</b> </h2>
          </div>
          <br/>
          % for field in job_listing_form.visible_fields %
            <div class="form-group">
              <div class="row">
                <p> field.label_tag </p>
              </div>
              <div class="row">
                % if field.help_text %
                  <p class="form-text text-muted"> field.help_text </p>
                % endif %
              </div>
              <div class="row">
                 field 
              </div>
            </div>
          % endfor %
          <br/>
          <div class="row text-center">
            <h2> <b>Employer information</b> </h2>
          </div>
          <br/>
          % for field in employer_form.visible_fields %
            <div class="form-group">
              <div class="row">
                <p> field.label_tag </p>
              </div>
              <div class="row">
                % if field.help_text %
                  <p class="form-text text-muted"> field.help_text </p>
                % endif %
              </div>
              <div class="row">
                 field 
              </div>
            </div>
          % endfor %
        <input type="submit" value="Submit" class="btn btn-primary" />
      </form>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
       job_listing_form.media.js 
    </div>
% endblock %

views.py(相关部分):

def submitJobListing(request):
    if request.method == "POST":
        employer_form = EmployerForm(request.POST, request.FILES)
        job_listing_form = JobListingForm(request.POST, request.FILES)

        #check if employer with that name already exists
        employer_name = str(request.POST.get("name", ""))
        try:
            employer_with_the_same_name = Employer.objects.get(name=employer_name)
        except ObjectDoesNotExist:
            employer_with_the_same_name = None

        employer = None
        
        if (employer_with_the_same_name != None):
            employer = employer_with_the_same_name

        if employer == None and employer_form.is_valid() and job_listing_form.is_valid():
            employer = employer_form.save()

        job_listing = job_listing_form.save(commit=False)
        job_listing.employer = employer
        job_listing.save()
        job_listing_form.save_m2m()

        return SimpleTemplateResponse("employers/successful_job_listing_submission.html")

    else:
        employer_form = EmployerForm()
        job_listing_form = JobListingForm()

    context = 
        "employer_form": employer_form,
        "job_listing_form": job_listing_form,
    

    return render(request, 'employers/submit_job_listing.html', context)

forms.py(相关部分):

class EmployerForm(forms.ModelForm):
    name = forms.CharField(max_length=100)
    #location = forms.CharField(widget=LocationWidget)
    short_bio = forms.CharField(widget=forms.Textarea)
    website = forms.URLField()
    profile_picture = forms.ImageField()

    def __init__(self, *args, **kwargs):
        super(EmployerForm, self).__init__(*args, **kwargs)
        for visible in self.visible_fields():
            if isinstance(visible.field.widget, (forms.widgets.TextInput, forms.widgets.Textarea, forms.widgets.URLInput, LocationWidget)):
                visible.field.widget.attrs['class'] = 'form-control'
            if isinstance(visible.field.widget, forms.widgets.ClearableFileInput):
                visible.field.widget.attrs['class'] = 'form-control-file'

    class Meta:
        model = Employer
        fields = ["name", "short_bio", "website", "profile_picture"]

class JobListingForm(forms.ModelForm):
        job_title = forms.CharField(max_length=100, help_text="What's the job title?")
        job_description = forms.CharField(widget=forms.Textarea)
        job_requirements = forms.CharField(widget=forms.Textarea)
        what_we_offer = forms.CharField(widget=forms.Textarea)
        location = forms.CharField(widget=LocationWidget)
        remote = forms.BooleanField()
        job_application_url = forms.URLField()
        point_of_contact = forms.EmailField()
        categories = forms.ModelMultipleChoiceField(
                     queryset=Category.objects.all(),
                     widget=forms.CheckboxSelectMultiple,
                     required=True)

        def __init__(self, *args, **kwargs):
            super(JobListingForm, self).__init__(*args, **kwargs)
            for visible in self.visible_fields():
                if isinstance(visible.field.widget, (forms.widgets.TextInput, forms.widgets.Textarea, forms.widgets.URLInput, forms.widgets.EmailInput, LocationWidget)):
                    visible.field.widget.attrs['class'] = 'form-control'
                if isinstance(visible.field.widget, (forms.widgets.CheckboxInput, forms.widgets.CheckboxSelectMultiple)):
                    visible.field.widget.attrs['class'] = 'form-check'

        class Meta:
            model = JobListing
            fields = ["job_title", "job_description", "job_requirements", "what_we_offer", "location", "remote", "job_application_url", "categories", "point_of_contact"]

谁能告诉我怎样才能达到我想要的效果?

更新 1:

我尝试添加检查field.errors 的代码,但它不起作用。

这是submit_job_listing.html 模板文件:

% extends "base.html" %
% load widget_tweaks %
% block title % Submit a job % endblock %
% comment % https://***.com/questions/50028673/changing-active-class-in-bootstrap-navbar-not-staying-in-inherited-django-templa % endcomment %
% block nav_item_post_a_job %active% endblock nav_item_post_a_job %
% block head %
     job_listing_form.media.css 
    <!---
    <style>
        input, select width: 100%
    </style>
    --->
% endblock %
% block content %

% comment %
https://simpleisbetterthancomplex.com/article/2017/08/19/how-to-render-django-form-manually.html
% endcomment %

    <h1>Submit a job listing</h1>
    <div class="container">
      <form method="post" enctype="multipart/form-data">
          % csrf_token %
          <div class="row text-center">
            <h2> <b>Job listing information</b> </h2>
          </div>
          <br />
          <div class="row text-center">
            <p> <b>Note: Fields marked with an asterisk (*) are required</b> </p>
          </div>
          <br/>
          % for field in job_listing_form.visible_fields %
            <div class="form-group">
              <div class="row">
                <p> field.label_tag  field.field.required|yesno:"*," </p>
              </div>
              <div class="row">
                % if field.help_text %
                  <p class="form-text text-muted"> field.help_text </p>
                % endif %
              </div>
              <div class="row">
                 field 
                <p>  field.errors  </p>
                % if field.errors %
                  <div class="border-bottom-danger">
                       field.errors 
                  </div>
                % endif %
              </div>
            </div>
          % endfor %
          <br/>
          <div class="row text-center">
            <h2> <b>Employer information</b> </h2>
          </div>
          <br />
          <div class="row text-center">
            <p> <b>Note: Fields marked with an asterisk (*) are required</b> </p>
          </div>
          <br/>
          % for field in employer_form.visible_fields %
            <div class="form-group">
              <div class="row">
                <p> field.label_tag  field.field.required|yesno:"*," </p>
              </div>
              <div class="row">
                % if field.help_text %
                  <p class="form-text text-muted"> field.help_text </p>
                % endif %
              </div>
              <div class="row">
                 field 
              </div>
            </div>
          % endfor %
        <input type="submit" value="Submit" class="btn btn-primary" />
      </form>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
       job_listing_form.media.js 
    </div>
% endblock %

当我只单击提交按钮而不输入任何内容时,所有输入都有一个红色轮廓,但没有任何内容打印为field.errors。我不知道为什么会这样。 为什么会这样?

【问题讨论】:

【参考方案1】:
% load widget_tweaks %

<div class="col-12 col-md-6">
                        <div class="form-group">
                            <label for=" form.field1.id_for_label " class="text-capitalize"> form.field1.label|title % if form.field1.field.required %<span class="text-danger ml-1">*</span>% endif %</label>

                            % if form.is_bound %
                                % if form.field1.errors %
                                    % render_field form.name class="form-control is-invalid" %
                                    % for error in form.field1.errors %
                                        <div class="invalid-feedback">
                                         error 
                                        </div>
                                    % endfor %
                                % else %
                                    % render_field form.field1 class="form-control is-valid" %
                                % endif %
                            % else %
                                % render_field form.field1 class="form-control" %
                            % endif %

                            % if form.field1.help_text %
                                <small class="form-text text-muted"> form.field1.help_text </small>
                            % endif %
                        </div>
                    </div>
</div>

【讨论】:

在您的代码中,您正在循环访问我提到的所有字段,以保留每个字段手册。因此,在您的情况下,您必须将 field1 替换为您拥有的正确字段【参考方案2】:

你试过在模板中调试吗?

我做了你想要实现的事情:

<form method="post"> % csrf_token %
    % for field in form %
        <div class="form-group">
             field 
        <label class="control-label" for=" field.id_for_label "> field.label </label>
            % if field.errors %
                <div class="border-bottom-danger">
                     field.errors 
                </div>
            % endif %
        </div>
    % endfor %
    <button class="btn btn-block" type="submit">Submit</button>
</form>

地点:

    field 是输入,您在其中正确添加 visible.field.widget.attrs['class'] = 'form-control' 然后,您想要(我猜)的错误存储在该字段中,因此如果有的话,您可以使用 field.errors 获取它。

你的代码和我的不同之处在于我使用的是field.errors而不是if field.help_text,希望对你有帮助。

我建议您在模板中放置断点,然后分析您的form 对象。然后你可以在你的前端做任何你想做的事情。

    如果没有错误: 将类 outline-green 添加到您的输入包装器(&lt;div class="row"如果出现错误:在输入下方的 div 中显示错误

PS : 如果你想概述输入,而不是行包装,你可以通过 css 来完成(或者如果你正在使用它,最好使用 Sass):

.row.outline-green > input
   border: 1px solid green;
<div class="row outline-green">
 field 
</div>

【讨论】:

对此我有两个问题:1) 如何在模板中放置断点?我正在使用 Atom 和终端 - 我需要为此使用其他东西吗? 2) 我更新了我的问题 - field.errors 似乎是空的。你能解释一下为什么会这样吗? 1) Check on this thread。就个人而言,我正在使用 pycharm pro。 2)你真的得到 field.help_text 的错误吗?尝试放置一些断点,以查看job_listing_formemployer_form 是如何填充的。如果您不能在模板中,请在返回上下文之前尝试在视图中。使用断点进行调试将为您节省大量时间。 当前模板将None 打印为field.errors。我不是通过 PyCharm 设置断点(因为我只有社区版),而是通过您提供的链接中的第二个答案。断点仅在第一次渲染模板时被激活。如果我单击提交按钮,则不会再次触发断点。这对我来说很奇怪 - 我认为 Django 会在每个 POST 请求上重新呈现模板,即使它是无效的?也许这个错误与我的观点有关? 删除条件% if field.errors %之前的&lt;p&gt; field.errors &lt;/p&gt;。然后检查您的job_listing_form.visible_fields 是如何填充断点的。是否有任何变量带有您所期望的任何类型的错误。 PS:如果您需要在模板中进行深度调试(逐步),请试用 pycharm pro 及其试用版 30 天(还有学生版 1 年)。 field.errors 为空,因为消息“请填写此字段”。由网络浏览器呈现。即使我填写了其他输入字段并单击提交按钮,它实际上并没有向服务器发送 POST 请求,但浏览器只是呈现“请填写此字段”。再次。如何解决此浏览器功能,以便测试我的错误报告功能?

以上是关于如何在 Django 中修改 Bootstrap 4 表单验证信息呈现方式?的主要内容,如果未能解决你的问题,请参考以下文章

django-bootstrap4|django 加载popper.min.js失败

python测试开发django-120.bootstrap-table表格添加操作按钮(查看/修改/删除)

如何在 Django 应用程序中使用 bootstrap-datepicker?

python测试开发django-126.bootstrap-table表格内操作按钮(修改/删除) 功能实现

如何在 django 中使用 bootstrap4 设置酥脆的表格?

如何在 twitter-bootstrap 模式窗口中插入 django 表单?