Search code examples
pythondjangodjango-modelspython-datetime

Best way for allowing %d.%m.%Y-formated strings to be passed a model's DateField?


I have been trying to understand the effects of the DATE_INPUT_FORMATS settings and the way Django processes these datfields but whatever I find is either not understanable to me (being relatively new to DJango) or seems to relate to Forms, which doesn't seem to be applicable here.

My user model has a birthday field:

class LocalUser(AbstractUser):
    birthday = models.DateField(blank=True, null=True)
    

and I want to create users from a csv. I upload the csv and process its contents:

class InputFileForm(forms.Form):
    file = forms.FileField()

    def process_file(self):
        file = io.TextIOWrapper(self.cleaned_data['file'].file, encoding='utf-8-sig')
        reader = csv.DictReader(file, delimiter=";")
        l = LocalUser(**dict(line))
        l.save()

The csv contains dates in e. g. "01.01.1999" format. It works when I reformat dates e. g. via line["birthday"] = datetime.strptime(line["birthday"], '%d.%m.%Y') but I am sure there must be a nicer solution, one where I don't have to modify code wherever a birthday is passed to the user model as string.

In the settingy.py file I added:

#USE_L10N = False
from django.conf.global_settings import DATE_INPUT_FORMATS
DATE_INPUT_FORMATS += ('%d.%m.%Y',)

but that doesn't help.

The error message I get is:

ValidationError at /import_export/import_data

["'11.02.1993' value has an invalid date format. It must be in YYYY-MM-DD format."]

Do you have any recommendations on how to solve this?


Solution

  • The DATE_INPUT_FORMATS is related to the forms, so won't do the conversion while saving the model instance.

    You can override the save method of the model and add the necessary conversion code there. It would also be better to handle all the patterns mentioned in DATE_INPUT_FORMATS to keep a common source of truth:

    from datetime import datetime
    from django.conf import settings
    from django.core.exceptions import ValidationError
    
    
    class LocalUser(AbstractUser):
        birthday = models.DateField(blank=True, null=True)
    
        def save(self, *args, **kwargs):
            if self.birthday is not None:
                if isinstance(self.birthday, str):
                    for pattern in settings.DATE_INPUT_FORMATS:
                        try:
                            self.birthday = datetime.strptime(self.birthday, pattern)
                            break
                        except ValueError:
                            continue
                    else:
                        raise ValidationError('Not a valid date string')
            super().save(*args, **kwargs)
    

    Now, if you have your pattern(s) added to DATE_INPUT_FORMATS (like you do now), the automatic conversion would be taken place while saving.