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

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).


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])

You could build your own validator for that.

from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone as tz

def validate_date_not_in_future(value):
    if value > tz.now():
        raise ValidationError('date is in the future')

class MemberRegistration(models.Model):
    purchase_date = models.DateField(
        validators=[validate_date_not_in_future])