Search code examples
pythondjangodjango-models

Will a dynamic list of choices in a Django model evaluate when the model is migrated or when a user tries to select a choice for a model?


Code

Let's say I have the following model:

class Course(models.Model):

    title = models.CharField(max_length=48)

    YEAR_CHOICES = [(r, r) for r in range(
            datetime.date.today().year-1, datetime.date.today().year+2
        )
    ]
    year = models.IntegerField(_('year'), choices=YEAR_CHOICES)

Question

Will the datetime.date.today() statements be evaluated right when the model is migrated, or will they be evaluated whenever the user accesses a form to set the year value for the Course model?

In other words, is my YEAR_CHOICES code above frozen to when I migrated my model or will it dynamically update as the years go by?


Solution

  • Test results

    I did some testing with minutes instead of years, and the results are pretty surprising:

    Given this function...

    from django.db import models
    from django.utils.timezone import now
    
    def minute_choices_range():
        now_minute = now().minute
        return [(r, r) for r in range(
                now_minute-1, now_minute+2
            )
        ]
    

    Constantly updated

    (Time values that kept in sync with time a page was accessed)

    class Course(models.Model):
        minute = models.IntegerField(choices=minute_choices_range)
    

    Stuck at migration time

    (Time values that were stuck at the time the model was migrated)

    class Course(models.Model):
        minute = models.IntegerField(choices=minute_choices_range())
    

    And given this function...

    from django.db import models
    from django.utils.timezone import now
    
    def current_min():
        return now().minute
    

    Stuck at migration time

    (Time values that were stuck at the time the model was migrated)

    class Course(models.Model):
        MINUTE_CHOICES = [(r, r) for r in range(
                current_min()-1, current_min()+2
            )
        ]
    
        minute = models.IntegerField(choices=MINUTE_CHOICES)
    

    And this one I didn't have a function with, but it resulted in a "stuck time" as well (a time that is stuck at migration time)

    from django.utils.timezone import now
    
    class Course(models.Model):
        MINUTE_CHOICES = [(r, r) for r in range(
                now().minute-1, now().minute+2
            )
        ]
    
        minute = models.IntegerField(choices=MINUTE_CHOICES)