Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validation for datefield so it doesn't take future dates in django?

I'm creating a website for my project and i have a field called Purchase_Date:

class Member_Registration(models.Model):
    Purchase_Date=models.DateField(max_length=10, help_text="Enter the date of purchase")

I want to throw an error if the user selects a date that is in the future. How do i go about this?

like image 453
Tanmay Avatar asked Apr 17 '18 15:04

Tanmay


2 Answers

As of Django 2.2, you no longer have to create a custom validator for this logic because the limit_value can now be a callable.

from datetime import date

from django.core.validators import MaxValueValidator
from django.db import models
from django.utils.translation import gettext_lazy as _

class MemberRegistration(models.Model):
    purchase_date=models.DateField(
        help_text=_('Enter the date of purchase'),
        validators=[MaxValueValidator(limit_value=date.today)]
        verbose_name=_('purchase date')
    )

Note that the limit_value is set to date.today (a callable) and not date.today(). If we used date.today(), whenever the code is loaded for the first time, it will use that date from that point forward until the code is reloaded. Using date.today (a callable) makes this dynamic and it will retrieve today's date each time the validator is called. The error you would now receive on the front end will now be "Ensure this value is less than or equal to 2019-12-02" (replace 2019-12-02 with today's date if you are reading this in the future). If you want to keep your error message more specific to the field and have a custom message, the examples of writing your own validator would be needed.

For what it is worth, if you are super concerned about data integrity, you can add a CheckConstraint on this field to prevent the database from allowing future dates as well. This would insure that if validation is skipped for any reason, the value cannot be above what you expect:

from datetime import date

from django.core.validators import MaxValueValidator
from django.db import models
from django.db.models.functions import Now
from django.utils.translation import gettext_lazy as _

class MemberRegistration(models.Model):
    purchase_date=models.DateField(
        help_text=_('Enter the date of purchase'),
        validators=[MaxValueValidator(limit_value=date.today)]
        verbose_name=_('purchase date')
    )

    class Meta:
        constraints = [
            models.CheckConstraint(
                check=models.Q(purchase_date__lte=Now()),
                name='purchase_date_cannot_be_future_dated'
            )
        ]

If we tried to do MemberRegistration(purchase_date=date(2099, 1, 1)).save() we would get the error:

django.db.utils.IntegrityError: new row for relation "app_label_memberregistration" violates check constraint "purchase_date_cannot_be_future_dated" DETAIL: Failing row contains (2099-01-01).

like image 196
Michael B Avatar answered Oct 16 '22 14:10

Michael B


What you want is a validator:

from datetime import date
from django.core.exceptions import ValidationError
from django.db import models

def no_future(value):
    today = date.today()
    if value > today:
        raise ValidationError('Purchase_Date cannot be in the future.')

class Member_Registration(models.Model):
    Purchase_Date=models.DateField(help_text="Enter the date of purchase", validators=[no_future])
like image 8
Kal Avatar answered Oct 16 '22 13:10

Kal