0

---------------- EDIT --------------------

I could not succed doing it with Custom Model Field and I had to move on, so actually, I did it the alternative way specified at the end of this post.

Here is a link to a new post exposing the solution.

---------------- END EDIT --------------------

My app displays formsets where users can create objects. Once validated, the formset is displayed again and user can add new objects.

Dates should only be month and year ("%m/%Y").

I could work on the input field (though jquery) to add '01/' in front of the date entered. But after submitting, the field now displays "%d/%m/%Y" (normal).

So, I'm looking for a way to translate input string (ex : 09/2018) to dateField to store in the database, and then a translate this dateField to string when datas are retrieved from database.

I know, I could simply use a form charfield for months and another one for years.

But I would like to keep it as a date object, so, in templates, I could perform date formatting ({{ edu.start_date |date:"D, d M, Y" }})

Django custom model fields sound made for this : Django custom model fields.

I could do something like this in the custom field:

def string_to_date(value):
    value = '01/' + value
    la_date = datetime.strptime(value, "%d/%m/%Y").date()
    return la_date

def date_to_string(la_date_date):
    la_date_str = la_date_date.strftime('%m/%Y')
    return la_date_str

class DateYearMonth(models.DateField):
    def get_prep_value(self, value):
        if value is None:
            return value
        return string_to_date(value)

    def to_python(self, value):
        if value is None:
            return value
        return date_to_string(value)

The form associated (i commented widgets):

class EducationForm(forms.ModelForm):
    start_date =  forms.CharField()
    end_date = forms.CharField()

    class Meta:
        model = Education
        exclude = ("curriculum",)

        #     # les widgets :
       #       widgets = {
        #          'start_date': forms.TextInput(),
        #          'end_date': forms.TextInput(),
#     }

Well, this does not work so far. But I don't even know if I'm heading to the right direction...

EDIT

Actually, maybe I could use simple charfield for month and year and add a model method that would create a date out of it (maybe a @property)... This way, I would keep it simple on the form and be able to format on the templates...

stockersky
  • 1,531
  • 2
  • 20
  • 36
  • What exactly didn't work when you created the custom field? – Sachin Sep 03 '18 at 16:18
  • form validation fails. Message asks for a valid date. To be honest, this is a quick code try. I do not understand much about custom models... – stockersky Sep 03 '18 at 16:24
  • I tried to reproduce your code. It worked. Please share the error stacktrace. Also, the input you're giving while creating the model object. – Sachin Sep 03 '18 at 16:46
  • Thanks for your help. I updated my post with some infos. First, I added tests in case the date field are null/blank. Then, I realize that validation failure might come from some validation form, as when i input date, it raise a validation error and does not even reach model code... – stockersky Sep 03 '18 at 17:21
  • 1
    Right. For that custom field, you need to override the `forms.DateInput` as well. It will expect a date. Or, use a CharInput and add a custom validator on it. – Sachin Sep 03 '18 at 17:24
  • Thanks again Sachin! I updated my post. Indeed, it works at the db level. From django shell, I can create objects with string while the database field is a date. However, I cannot get rid of the validation error... – stockersky Sep 03 '18 at 19:53

1 Answers1

1

You can make the Year an IntegerField and Month (CharField or Integer) and store Year in Months individually is probably the best solution. Below is an example fore Year only(kind of DatetimeField to YearField)

import datetime
    YEAR_CHOICES = []
    for r in range(1980, (datetime.datetime.now().year+1)):
        YEAR_CHOICES.append((r,r))

    year = models.IntegerField(_('year'), choices=YEAR_CHOICES, 
           default=datetime.datetime.now().year)
Syed Faizan
  • 958
  • 9
  • 19
  • Thanks Syed! Actually I used your answer as a part of an alternative solution. See the update at the top of my post if you feel like it. – stockersky Sep 04 '18 at 16:37