简体   繁体   中英

Django Crispy Forms with custom field template is not adding the value of the field to request

I am using Django 1.10 and django-crispy-forms 1.6.1 in my project. I added a custom datetimepicker dropdown template that seems to be functioning correctly. The problem I am now encountering, is that every time I attempt to submit a form, I am flashed with the error on the field that it is required. It seems that after submitting the form, the field is cleared out, and the error class is added to the field.

After submit, form shows errors

When I checked the console, I receive no errors, but when I check Chrome DevTools, I looked at the request payload sent server-side and I see that the field is not added at all to the request. Here is a picture of the request payload from the dev tools.

DevTools request payload without "time_stamp"

I am able to get the datetimepicker to dropdown when the input receives focus, and that seems to work just fine. Here is the relevant code for my forms/views, and the custom 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 %}

I think there must be a problem either with my template, or how django-crispy-forms handles fields that use a custom template, because when I remove the template from the form helper, a regular input box is used and the datetime string is submitted just fine to django. Thank you all for your help. Sorry that I had to link the images, I don't have a high enough rep to embed them.

I think the reason why the time_stamp is not being sent to the server because the datepicker input tag doesn't have a name attribute:

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

Have a look at the notes in w3schools :

Note: Only form elements with a name attribute will have their values passed when submitting a form.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM