【问题标题】:Django Crispy Forms with custom field template is not adding the value of the field to request带有自定义字段模板的 Django Crispy Forms 未添加要请求的字段值
【发布时间】:2017-09-10 12:27:22
【问题描述】:

我在我的项目中使用 Django 1.10 和 django-crispy-forms 1.6.1。我添加了一个似乎正常运行的自定义 datetimepicker 下拉模板。我现在遇到的问题是,每次我尝试提交表单时,都会在必填字段上闪现错误。好像是提交表单后,字段被清空,字段中添加了错误类。

After submit, form shows errors

当我检查控制台时,我没有收到任何错误,但是当我检查 Chrome DevTools 时,我查看了服务器端发送的请求负载,我发现该字段根本没有添加到请求中。这是来自开发工具的请求负载的图片。

DevTools request payload without "time_stamp"

当输入获得焦点时,我可以让 datetimepicker 下拉,这似乎工作得很好。这是我的表单/视图的相关代码,以及自定义 template.html:

models.py

class Call(models.Model):
    history = AuditlogHistoryField()
    analyst = models.ForeignKey(settings.AUTH_USER_MODEL, models.SET_NULL, blank=True, null=True, limit_choices_to={'is_staff': True}, related_name='call_analyst')
    contact = models.CharField(max_length=50)
    time_stamp = models.DateTimeField(default=datetime.now)
    description = models.CharField(max_length=512)
    ticket = models.PositiveIntegerField(blank=True, null=True)
    CALL_TYPE = (
        ('M', 'Made'),
        ('R', 'Received'),
    )
    call_type = models.CharField(max_length=1, choices=CALL_TYPE)
    created_at = models.DateTimeField(auto_now_add=True)
    modified_at = models.DateTimeField(auto_now=True)

    @python_2_unicode_compatible
    def __str__(self):
        return "Call instance {}".format(self.pk)

forms.py

class CallForm(forms.ModelForm):
    description = forms.CharField(widget=forms.Textarea)

    class Meta:
        model = Call
        fields = ['analyst', 'contact', 'time_stamp', 'description', 'ticket', 'call_type']

    def __init__(self, *args, **kwargs):
        super(CallForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.form_tag = True
        self.helper.form_class = 'form-horizontal'
        self.helper.label_class = 'col-sm-3'
        self.helper.field_class = 'col-sm-9'
        self.helper.layout = Layout(
            Field('analyst'),
            Field('contact'),
            Field('time_stamp', template="datetimepicker.html"),
            Field('description'),
            Field('ticket'),
            Field('call_type'),
        )

    def is_valid(self):
        return super(CallForm, self).is_valid()

    def full_clean(self):
        return super(CallForm, self).full_clean()

    def clean_analyst(self):
        analyst = self.cleaned_data.get("analyst", None)
        return analyst

    def clean_contact(self):
        contact = self.cleaned_data.get("contact", None)
        return contact

    def clean_time_stamp(self):
        time_stamp = self.cleaned_data.get("time_stamp", None)
        return time_stamp

    def clean_description(self):
        description = self.cleaned_data.get("description", None)
        return description

    def clean_ticket(self):
        ticket = self.cleaned_data.get("ticket", None)
        return ticket

    def clean_call_type(self):
        call_type = self.cleaned_data.get("call_type", None)
        return call_type

    def clean(self):
        return super(CallForm, self).clean()

    def validate_unique(self):
        return super(CallForm, self).validate_unique()

    def save(self, commit=True):
        return super(CallForm, self).save(commit)

views.py

class CallCreateView(AjaxCreateView):
    model = Call
    form_class = CallForm
    # fields = ['analyst', 'contact', 'call_timestamp', 'description', 'ticket', 'call_type']
    template_name = "reports/../_templates/create_update_template.html"
    success_url = reverse_lazy("call_list")

    def __init__(self, **kwargs):
        super(CallCreateView, self).__init__(**kwargs)

    def dispatch(self, request, *args, **kwargs):
        return super(CallCreateView, self).dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        return super(CallCreateView, self).get(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return super(CallCreateView, self).post(request, *args, **kwargs)

    def get_form_class(self):
        return super(CallCreateView, self).get_form_class()

    def get_form(self, form_class=None):
        return super(CallCreateView, self).get_form(form_class)

    def get_form_kwargs(self, **kwargs):
        return super(CallCreateView, self).get_form_kwargs()

    def get_initial(self):
        return super(CallCreateView, self).get_initial()

    def form_invalid(self, form):
        return super(CallCreateView, self).form_invalid(form)

    def form_valid(self, form):
        obj = form.save(commit=False)
        obj.save()
        return super(CallCreateView, self).form_valid(form)

    def get_context_data(self, **kwargs):
        ret = super(CallCreateView, self).get_context_data(**kwargs)
        return ret

    def render_to_response(self, context, **response_kwargs):
        return super(CallCreateView, self).render_to_response(context, **response_kwargs)

    def get_template_names(self):
        return super(CallCreateView, self).get_template_names()

    def get_success_url(self):
        return reverse("call_detail", args=(self.object.pk,))

datetimepicker.html

{% load crispy_forms_field %}

<div{% if div.css_id %} id="{{ div.css_id }}"{% endif %}
                        class="form-group{% if form_show_errors and field.errors %} has-error{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}{% if div.css_class %} {{ div.css_class }}{% endif %}">
    {% if field.label and form_show_labels %}
        <label for="{{ field.id_for_label }}"
               class="control-label {{ label_class }}{% if field.field.required %} requiredField{% endif %}">
            {{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
        </label>
    {% endif %}

    <div class="controls date datetimepicker col-sm-9" id="{{ field.id_for_label }}"
         data-link-field="{{ field.id_for_label }}" {{ flat_attrs|safe }}>
        <input class="form-control" id="{{ field.id_for_label }}" type="text">
    </div>
    <input type="hidden" id="{{ field.id_for_label }}" value=""/><br/>
    {% include 'bootstrap3/layout/help_text_and_errors.html' %}
</div>

{% block datetimepicker %}
    <script type="text/javascript">
        $(function () {
            $('#{{ field.id_for_label }}').datetimepicker({
                sideBySide: true,
                allowInputToggle: true,
                showTodayButton: true,
                showClear: true,
                showClose: true,
                toolbarPlacement: "top",
                format: "dddd, MMMM Do YYYY, h:mm A"
            })
        })
    </script>
{% endblock %}

我认为我的模板一定有问题,或者 django-crispy-forms 如何处理使用自定义模板的字段,因为当我从表单助手中删除模板时,会使用常规输入框和日期时间字符串提交给 django 就好了。感谢大家的帮助。抱歉,我必须链接图像,我没有足够高的代表来嵌入它们。

【问题讨论】:

  • 您在带有字段的divhidden input 上都设置了id="{{ field.id_for_label }}"
  • 我知道,但是crispy-forms 会将其转换为字符串“tag_id_for_label”,因此当它位于 div 上时,该 div 具有 id="div_id_time_stamp",而输入具有 id="input_id_time_stamp"。我确保这也不是错误。
  • 啊,很酷,我不知道 =D 当我包装字段时,我会这样做:self.helper['active'].wrap(Field, template='field_templates/checkbox_input.html') 但我不确定它是否会有所作为。在我这样做之前,我在处理个别领域时遇到了很多麻烦。
  • ['active'] 是您调用 wrap() 方法的字段的名称吗?如果是这样,那么我在 forms.py 中的声明应该遵循self.helper['time_stamp'].wrap(...)?到目前为止,我感谢您的帮助!
  • 没关系,我刚刚在crispy-forms 文档中看到这正是您所做的。我现在正要试一试。

标签: python django django-templates django-views django-crispy-forms


【解决方案1】:

我认为time_stamp没有被发送到服务器的原因是因为日期选择器input标签没有name属性:

<input class="form-control" id="{{ field.id_for_label }}" type="text">

看看notes in w3schools

注意:只有具有name 属性的表单元素才会在提交表单时传递其值。

【讨论】:

  • 感谢您的帮助。忽略这么简单的事情,我觉得自己像个白痴。然而,问题并没有完全消失。通过在输入中添加 name="time_stamp" 属性,该字段被添加到发送到服务器的请求中,但它仍然返回一个错误,要求输入有效的日期/时间,并且正在发布的数据是空的。
  • 所以除了您的建议之外,我唯一做的就是将标签的for="{{ field.id_for_label }}" 属性更改为for="input_{{ id_for_label }}",现在突然之间它起作用了。我会将您的答案标记为已回答,因为我认为您的回答最能帮助我取得成功。非常感谢@AKS 的帮助!
猜你喜欢
  • 2019-12-31
  • 2017-05-04
  • 1970-01-01
  • 2017-03-20
  • 2014-12-21
  • 2013-12-30
  • 2021-10-19
  • 2014-02-22
  • 1970-01-01
相关资源
最近更新 更多