9

I tried to validate my DateField to accept only dates from today and future, but I don't know why it's accepting every passed date anyway.

My models.py file:

def present_or_future_date(value):
    if value < datetime.date.today():
        raise models.ValidationError("The date cannot be in the past!")
    return value

class Event(models.Model):
    title = models.CharField(max_length=50)
    text = models.TextField()
    date = models.DateField(default=datetime.now, validators=[present_or_future_date])
    duration = models.TextField(default='0', blank='true')
    created_at = models.DateTimeField(default=datetime.now, blank='true')

def __str__(self):
    return self.title
Kaspazza
  • 303
  • 1
  • 3
  • 13

2 Answers2

12

As Benjamin said, validators are added implicitly only to ModelForms. More documentation about validators here.

If you want to be sure that no object can be created with your date condition, you should override its save method like below. Also takecare that though Django will handle naive date objects, its way better to use django.utils.timezone.now

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

class Event(models.Model):
    title = models.CharField(max_length=50)
    text = models.TextField()
    date = models.DateField(default=datetime.date.today())
    duration = models.TextField(default='0', blank='true')
    created_at = models.DateTimeField(default=datetime.datetime.now(), blank='true')
    def save(self, *args, **kwargs):
        if self.date < datetime.date.today():
            raise ValidationError("The date cannot be in the past!")
        super(Event, self).save(*args, **kwargs)
May.D
  • 1,832
  • 1
  • 18
  • 34
  • Thank you, but where should I paste that? – Kaspazza May 20 '18 at 21:52
  • In your Model defintion, like its __str__method (which is wrongly indented in your post btw). – May.D May 20 '18 at 21:54
  • AttributeError: 'method_descriptor' object has no attribute 'today' it's giving me this, and why it's wrongly intended? how should I change that? – Kaspazza May 20 '18 at 21:57
  • Which version of python and django do you use ? – May.D May 20 '18 at 22:07
  • python 3.6, django 2.0 – Kaspazza May 20 '18 at 22:14
  • I've tested your code and got some errors from your default fields. I've edited the answer with fixed code and its working fine for me. – May.D May 20 '18 at 22:25
  • I had to add str(datetime.date.today()) instead of datetime.date.today but besides that it is perfect, thank you – Kaspazza May 20 '18 at 22:40
  • Now when someone inputs bad date it breaks site and shows "django.db.models' has no attribute 'ValidationError'" – Kaspazza May 20 '18 at 22:42
  • It was because there was models.ValidationError() instead of forms.ValidationError() – Kaspazza May 20 '18 at 22:53
  • Copy paste mistake. Actually you should use django.core.exceptions.ValidationError – May.D May 20 '18 at 23:05
  • Thank you very much, I have one more question. Now when someone enters bad date it breaks site with error "['The date cannot be in the past!']" how can I make it to show up like in admin or redirect with some flash message instead of breaking site? – Kaspazza May 20 '18 at 23:12
  • 1
    Use https://docs.djangoproject.com/en/2.0/ref/contrib/messages/ `try: # object creation except ValidationError as e: # add error text to messages ` – May.D May 20 '18 at 23:41
1

To make sure the validation is performed, add constraints on the model in the Meta:

from django.db.models.functions import Now
from django.db import models

class Meta:
    constraints = [
        models.CheckConstraint(
            check=models.Q(date__gte=Now()),
            name='created_at_cannot_be_past_date'
        )
    ]
Sardar Faisal
  • 594
  • 5
  • 20