Question

I have a form which allows users to edit their profiles. One of the fields is 'date of birth'. While editing, the form picks up the date coming from database (mySQL) and shows it in the format 'June 10, 1982'. When I hit 'submit', it throws the error : 'Enter a valid date.' The field is 'read-only', so that date is picked using datepicker. I'm using the datepicker from eternicode (https://github.com/eternicode/bootstrap-datepicker/)

To render the form, I'm using crispy-forms (v1.2.3) and twitter-bootstrap.

When a date is picked using the datepicker, it's in the format mm/dd/yyyy. This gets saved properly when the form is submitted.

I could not find anything on SO or elsewhere which could solve my problem. I feel it should be a minor tweak somewhere, but not able to pinpoint-it despite many various approaches.

By the way, if I specify value="{{ field.value|date:'m/d/Y' }}" in the 'input' tag, it displays the date in this format and saves it successfully, but then disappears from the widget till the form is reloaded (it can be reloaded by refreshing the page)

In settings.py: USE_I18N, USE_L10N and USE_TZ are all set to 'True', TIME_ZONE = 'Asia/Kolkata'

If possible, I'd like to keep the display date in format 'June 10, 1982' and still be able to save the form. Otherwise, I don't mind changing the display date format in the form's widget. Your help would be highly appreciated.

Here are my relevant code snippets:
models.py

class Profile(models.Model):

    class Meta:
        verbose_name_plural = _('Profiles')

    user = models.OneToOneField(User, verbose_name=_('User'), related_name='enduser',     unique=True)
    dob = models.DateField(_('Date of Birth'), help_text=_(u"Age: 18yrs - 90yrs"))
    :
    :  

forms.py

class ExtendedProfileForm(forms.ModelForm):

class Meta:
    model = Profile
    exclude = ('user',)

# some crispy forms related code
def __init__(self, *args, **kwargs):

    super(ExtendedProfileForm, self).__init__(*args, **kwargs)

    self.helper = FormHelper()
    self.helper.form_class = 'form-inline'
    self.helper.form_tag = False
    self.helper.html5_required = True
    self.helper.help_text_inline = True
    self.helper.attrs = {'accept-charset' : 'utf-8'}

    fieldlist = self.fields.keys()
    newlist = []
    for key in fieldlist:
        if key == 'dob':
            newlist.append(Field("dob", css_class="input-small", readonly="readonly", id="id_dob", template="datepick.html", input_formats=(settings.DATE_INPUT_FORMATS)))
        else:
            newlist.append(key)

    self.helper.layout = Layout(
        Fieldset("",
                 *newlist
        ),
    )  

datepick.html

{% comment %}
  Ref: https://github.com/maraujop/django-crispy-forms/issues/134
{% endcomment %}
{% load crispy_forms_field %}
<div id="div_{{ field.auto_id }}" class="clearfix control-group{% if field.errors %}     error{% endif %}">
    {% if field.label %}
        <label for="id_{{ field.id_for_label }}" class="control-label {% if   field.field.required %}requiredField{% endif %}"> {{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %} </label>
    {% endif %}
    <div class="controls">
        <div id="{{ field.auto_id }}" class="input-append date" data-date-format="%m/%d/%Y">
            <input size="40" type="text" readonly="readonly" {% if field.value %} value="{{ field.value }}"{% endif %} name="{% if form.prefix %}{{form.prefix}}-{% endif %}{{ field.name }}"/><span class="add-on"><i class="icon-calendar">&nbsp;</i></span>
        </div>
        {% include 'bootstrap/layout/help_text_and_errors.html' %}
    </div>
</div>

settings.py

DATE_INPUT_FORMATS = (
    '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
    '%b %d %Y', '%b %d, %Y',            # 'Oct 25 2006', 'Oct 25, 2006'
    '%d %b %Y', '%d %b, %Y',            # '25 Oct 2006', '25 Oct, 2006'
    '%B %d %Y', '%B %d, %Y',            # 'October 25 2006', 'October 25, 2006'
    '%d %B %Y', '%d %B, %Y',            # '25 October 2006', '25 October, 2006'
)  

The JS call to datepicker

    $('#id_dob').datepicker({
        format : 'mm/dd/yyyy',
        endDate : '-18y', // 18 years in the past
        startDate : '-90y', // we assume a person to be max 90 years old to use this site
        startView : 2,
        autoclose : true,
    }); 
Was it helpful?

Solution 2

Thanks to Aamir Adnan and kartheek, for giving me the right pointers!

The thing that finally worked was adding following in the init of the ExtendedProfileForm

        self.fields['dob'].input_formats=(settings.DATE_INPUT_FORMATS)  

So now my form is thus, which works perfectly!

class ExtendedProfileForm(forms.ModelForm):

class Meta:
    model = Profile
    exclude = ('user',)

# some crispy forms related code
def __init__(self, *args, **kwargs):

    super(ExtendedProfileForm, self).__init__(*args, **kwargs)

    self.helper = FormHelper()
    self.helper.form_class = 'form-inline'
    self.helper.form_tag = False
    self.helper.html5_required = True
    self.helper.help_text_inline = True
    self.helper.attrs = {'accept-charset' : 'utf-8'}
    self.fields['dob'].input_formats=(settings.DATE_INPUT_FORMATS)

    fieldlist = self.fields.keys()
    newlist = []
    for key in fieldlist:
        if key == 'dob':
            newlist.append(Field("dob", css_class="input-small", readonly="readonly", id="id_dob", template="datepick.html"))
        else:
            newlist.append(key)

    self.helper.layout = Layout(
        Fieldset("",
                 *newlist
        ),
    )

OTHER TIPS

You need to define date format similar to the datepicker format on the form field:

def ExtendedProfileModel(forms.ModelForm):
    dob = forms.DateField(label=_('Date of birth'), input_formats=['%m/%d/%y'])

    def __init__(self, *args, **kwargs):
        super(ExtendedProfileModel, self).__init__(*args, **kwargs)
        self.fields['dob'].widget.format = '%m/%d/%y'
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top