首页 > 解决方案 > Django:FormValidationError 时不保存复选框

问题描述

我正在使用 CreateView。当发送我的表单并正确填写所有字段时,字段票(ManyToMany 字段)也被保存。但是,如果我POST的表单有一些验证错误(例如必填字段为空),那么所有预填充的字段仍然是字段request.POST。但是,我的预先检查的字段不再被选中。你知道为什么吗?

示范

视图.py

class DiscountCreate(AdminPermissionRequiredMixin, SuccessMessageMixin,
                     FormValidationMixin, BaseDiscountView, CreateView):
    form_class = DiscountForm
    template_name = 'discounts/admin/create.html'
    success_message = _("Discount Code has been successfully created.")

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs['event'] = self.request.event
        return kwargs

    def get_success_url(self):
        return reverse('discounts:admin:detail', kwargs={
            'organizer': self.request.organizer.slug,
            'event': self.request.event.slug,
            'discount': self.instance.pk
        })

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['type_fixed'] = Discount.TYPE_FIXED
        context['type_percentage'] = Discount.TYPE_PERCENTAGE
        return context

    def form_valid(self, form):
        self.instance = form.save(commit=False)
        self.instance.event = self.request.event
        self.instance.status = Discount.STATUS_ACTIVE
        return super().form_valid(form)

模板

<div class="card">
  <div class="card-header">
    <h4 class="card-header-title">
      {% trans "Creating Discount Code" %}
    </h4>
  </div>
  <div class="card-body">

    <form method="post" autocomplete="off" novalidate>
      {% csrf_token %}

      <div class="form-group">
        <label for="{{ form.code.id_for_label }}">
          {{ form.code.label }}
        </label>
        {{ form.code }}
        {% if form.code.errors %}
          <div class="invalid-feedback d-block">
            {{ form.code.errors|first }}
          </div>
        {% endif %}
      </div>

      <div class="form-group">
        <label for="{{ form.type.id_for_label }}">
          {{ form.type.label }}
        </label>
        {{ form.type }}
        {% if form.type.errors %}
          <div class="invalid-feedback d-block">
            {{ form.type.errors|first }}
          </div>
        {% endif %}
      </div>

      <div class="form-group fixed{% if form.type.value != type_fixed %} d-none {% endif %}">
        {{ form.value.label_tag }}
        <div class="input-group">
          {{ form.value }}
          <div class="input-group-append">
            <span class="input-group-text text-dark">{{ request.event.currency }}</span>
          </div>
        </div>

        {% if form.value.errors %}
          <div class="invalid-feedback d-block">
            {{ form.value.errors|first }}
          </div>
        {% endif %}
      </div>

      <div class="form-group percentage{% if form.type.value != type_percentage %} d-none {% endif %}">
        {{ form.percentage.label_tag }}
        <div class="input-group">
          {{ form.percentage }}
          <div class="input-group-append">
            <span class="input-group-text text-dark">&#37;</span>
          </div>
        </div>
        {% if form.percentage.errors %}
          <div class="invalid-feedback d-block">
            {{ form.percentage.errors|first }}
          </div>
        {% endif %}
      </div>

      <div class="form-group">
        {{ form.available_amount.label_tag }}
        {{ form.available_amount }}
        {% if form.available_amount.errors %}
          <div class="invalid-feedback d-block">
            {{ form.available_amount.errors|first }}
          </div>
        {% endif %}
      </div>

      <div class="form-group">
        <label for="{{ form.tickets.id_for_label }}">
          {{ form.tickets.label }}
          <span class="badge badge-light">{% trans "Optional" %}</span>
        </label>
        <small class="form-text text-muted mt--2">{{ form.tickets.help_text }}</small>
        {% for ticket_id, ticket_label in form.tickets.field.choices %}
          <div class="custom-control custom-checkbox">
            <input
              type="checkbox"
              name="{{ form.tickets.html_name }}"
              value="{{ ticket_id }}"
              class="custom-control-input"
              id="id_{{ form.tickets.html_name }}_{{ forloop.counter }}"
              {% if ticket_id in form.tickets.value %}checked{% endif %}>
            <label class="custom-control-label" for="id_{{ form.tickets.html_name }}_{{ forloop.counter }}">{{ ticket_label }}</label>
          </div>
        {% endfor %}
        {% if form.tickets.errors %}
          <div class="invalid-feedback d-block">
            {{ form.tickets.errors|first }}
          </div>
        {% endif %}
      </div>

      <div class="form-group">
        <label for="{{ form.valid_from.id_for_label }}">
          {{ form.valid_from.label }}
          <span class="badge badge-light">{% trans "Optional" %}</span>
        </label>
        <small class="form-text text-muted mt--2">{{ form.valid_from.help_text }}</small>
        {{ form.valid_from }}
        {% if form.valid_from.errors %}
          <div class="invalid-feedback d-block">
            {{ form.valid_from.errors|first }}
          </div>
        {% endif %}
      </div>

      <div class="form-group">
        <label for="{{ form.valid_until.id_for_label }}">
          {{ form.valid_until.label }}
          <span class="badge badge-light">{% trans "Optional" %}</span>
        </label>
        <small class="form-text text-muted mt--2">{{ form.valid_until.help_text }}</small>
        {{ form.valid_until }}
        {% if form.valid_until.errors %}
          <div class="invalid-feedback d-block">
            {{ form.valid_until.errors|first }}
          </div>
        {% endif %}
      </div>

      <div class="form-group">
        <label for="{{ form.comment.id_for_label }}">
          {{ form.comment.label }}
          <span class="badge badge-light">{% trans "Optional" %}</span>
        </label>
        <small class="form-text text-muted mt--2">{{ form.comment.help_text }}</small>
        {{ form.comment }}
        {% if form.comment.errors %}
          <div class="invalid-feedback d-block">
            {{ form.comment.errors|first }}
          </div>
        {% endif %}
      </div>

      <button type="submit" class="btn btn-primary btn-block">{% trans "Create Discount Code" %}</button>
    </form>

  </div>
</div> {# / .card #}

<a class="btn btn-block btn-link text-muted mb-4" href="{% url 'discounts:admin:index' request.organizer.slug request.event.slug %}">
  {% trans "Cancel discount code creation" %}
</a>

表格.py

class DiscountForm(forms.ModelForm):
    # Remove required attribute from HTML elements
    use_required_attribute = False
    value = forms.DecimalField(decimal_places=2, required=False)
    percentage = forms.DecimalField(max_digits=4, decimal_places=2, required=False)

    class Meta:
        model = Discount
        fields = (
            'code',
            'type',
            'value',
            'percentage',
            'available_amount',
            'tickets',
            'valid_from',
            'valid_until',
            'comment',
        )

    def __init__(self, *args, **kwargs):
        self.event = kwargs.pop('event')
        super().__init__(*args, **kwargs)

        for visible_field in self.visible_fields():
            visible_field.field.widget.attrs['class'] = 'form-control'

        self.fields['tickets'].queryset = self.event.tickets.all()

        self.fields['code'].widget.attrs['autofocus'] = True
        self.fields['valid_from'].widget.attrs['class'] = 'form-control start-date picker'
        self.fields['valid_until'].widget.attrs['class'] = 'form-control end-date picker'
        self.fields['valid_from'].widget.format = settings.DATETIME_INPUT_FORMATS[0]
        self.fields['valid_until'].widget.format = settings.DATETIME_INPUT_FORMATS[0]
        self.fields['valid_from'].widget.attrs['data-lang'] = get_lang_code()
        self.fields['valid_until'].widget.attrs['data-lang'] = get_lang_code()

    def clean(self):
        cleaned_data = super().clean()
        discount_type = cleaned_data.get('type')

        if discount_type:
            if discount_type == Discount.TYPE_FIXED:
                value = cleaned_data.get('value')
                cleaned_data['percentage'] = None
                if not value:
                    message = _("Please enter how much discount you want to give.")
                    self.add_error('value', forms.ValidationError(message))

            if discount_type == Discount.TYPE_PERCENTAGE:
                percentage = cleaned_data.get('percentage')
                cleaned_data['value'] = None
                if not percentage:
                    message = _("Please enter how much discount you want to give.")
                    self.add_error('percentage', forms.ValidationError(message))

    def clean_value(self):
        value = self.cleaned_data['value']
        if value:
            value = smallest_currency_unit_converter(
                value,
                self.event.currency,
            )
        return value

    def clean_percentage(self):
        percentage = self.cleaned_data['percentage']
        if percentage:
            percentage /= 100  # convert 19 to 0.19
        return percentage

    def clean_valid_until(self):
        valid_until = self.cleaned_data['valid_until']
        if valid_until and valid_until > self.event.end_date:
            valid_until = self.event.end_date
        return valid_until

    def clean_valid_from(self):
        valid_from = self.cleaned_data['valid_from']
        if valid_from and valid_from > self.event.end_date:
            raise forms.ValidationError(_("Discount code should become valid \
                before the event starts."), code='valid_from')
        return valid_from

    def clean_code(self):
        code = self.cleaned_data['code']
        code_check = self.event.discounts.filter(
            code=code
        ).exclude(pk=self.instance.pk).exists()

        if code_check:
            raise forms.ValidationError(_("The code you chose as your discount code \
                    already exists for this event. Please change it."), code='code_exists')
        return code

标签: pythondjango

解决方案


在我解决问题后,可以在这里找到帮助我的最终解决方案:Django template: {% if 5 in ['4', '3', '5'] %} doesn't work


推荐阅读