How can I compare two dates in the database so that the next one starts with the previous one?

Viewed 35

I have model "Education" where the user must enter the years of study at the university. So I just wanna that when the user adds the year of the beginning of studing, the year of the end of studing cannot be lower than the year of the beginning of studing and must be within +10 years from the year of the beginning of studing.

Now my models looks like that:

My models:

def current_year():
    return datetime.date.today().year

def max_value_current_year(value):
    return MaxValueValidator(current_year())(value)

class Education(models.Model):
    name_university=models.CharField('University',max_length=300, blank=True)
    degree_of_edu=models.CharField('Degree of Education', max_length=25, blank=True)
    start_year=models.IntegerField('Start year',default=current_year(), validators=[MinValueValidator(1945), max_value_current_year])
    end_year=models.IntegerField('End year', default=current_year()+1,validators=[MinValueValidator(1945), MaxValueValidator(2100)])
    speciality=models.CharField('Speciality', default='Engineer', max_length=250, blank=True)

    def __str__(self):
        return '{}'.format(self.name_university)

I have understood that IntegerFields can't compere with each other, because i have gotten an error already like this:

'>' not supported between instances of 'int' and 'IntegerField'

So for example: if u choosed or wrote: 2021, u would able to choose the end of studing only 2022-2031

Or is it possible to do this at all only at the form stage?

1 Answers

You can work with clean() and Django's constraint framework to enforce this. Furthermore you made some errors regarding the default for the years: this should be a reference to a callable, not to the result of that callable, otherwise if you server keeps running between december 31st and january 1st, it will return still the old year.

from django.core.exceptions import ValidationError
from django.db.models import F, Q
from django.utils.timezone import now

def current_year():
    return now().year

def default_end_year():
    return current_year()+1

def max_value_current_year(value):
    if value > current_year():
        raise ValidationError('The year can not be greater than the current year.')

class Education(models.Model):
    # ⋮
    start_year=models.IntegerField(
        'Start year',
        default=current_year,
        validators=[MinValueValidator(1945), max_value_current_year]
    )
    end_year=models.IntegerField(
        'End year',
        default=default_end_year,
        validators=[MinValueValidator(1945), MaxValueValidator(2100)]
    )

    def clean(self, *args, **kwargs):
        if self.end_year < self.start_year:
            raise ValidationError('The end year can not be less than the start_year', code='end_year_too_low')
        if self.end_year > self.start_year+10:
            raise ValidationError('The end year should be at most ten years after the start year', code='study_too_long')
        return super().clean(*args, **kwargs)

    class Meta:
        constraints = [
            models.CheckConstraint(check=Q(end_year__gte=F('start_year')), name='end_year_too_low'),
            models.CheckConstraint(check=Q(end_year__lte=F('start_year')+10), name='study_too_long')
        ]

The constraints = [ … ] will enforce the constraints at the database level, given that the database supports this. The .clean(…) method [Django-doc] will run if you use a ModelForm, or you can manually clean the model object before trying to save it.

Related