11

I am building an app using Django 1.10 as backend. Is it possible to set a model field's default relative to another model from the same instance?

I specifically need to set second_visit's default to be 3 weeks after the first_visit

class SomeModel(models.Model): 
    first_visit = models.DateField()
    second_visit = models.DateField(default= second_visit_default)

    def second_visit_default(self):
        # Set second_visit to 3 weeks after first_visit
Krish Munot
  • 1,093
  • 2
  • 18
  • 29
Vingtoft
  • 13,368
  • 23
  • 86
  • 135

3 Answers3

12

You cannot assign a default value on a field dependent on another before having a model instance. To achieve the same you can override the save() method of the model:

class SomeModel(models.Model):

    ...

    def save(self, *args, **kwargs):
        self.second_visit = self.first_visit + datetime.timedelta(weeks=3)
        super().save(*args, **kwargs)
Evans Murithi
  • 3,197
  • 1
  • 21
  • 26
  • 5
    Warning: this is not the same as having a default. This will break any subsequent saves of the model where the user will actually want to set an explicit value in that field. – BjornW Aug 06 '20 at 09:28
2

You can override save or usepre_save

from django.db.models.signals import pre_save
from django.dispatch import receiver


@receiver(pre_save, sender=SomeModel)
def my_handler(sender, instance, **kwargs):
    instance.second_visit = # Set second_visit to 3 weeks after instance.first_visit
itzMEonTV
  • 19,851
  • 4
  • 39
  • 49
1

This is a late answer, but @BjornW addresses a valid concern in the comment on the accepted answer that is not addressed in the 2 provided answers (at the time of writing): Overwriting save is not the same thing as setting a default value; a default value only takes effect once, at the first instance creation, while modifying save affects every subsequent modification of an existing instance as well. The 2nd answer suffers from the same deviation of the principle of a default value.

Providing a default value relative to other attributes of self is not (yet?) possible, however you can use the pre_save signal to set a default value for a field that is relative to other fields.

Here would be an example:

# signals.py
from django.dispatch import receiver
from django.db.models.signals import pre_save
# do all necessary imports e.g.:
from .models import SomeModel

@receiver(pre_save, sender=SomeModel)
def set_default_second_visit(sender, instance, raw, **kwargs):
    """
    Set default value for `second_visit`
    """
    if instance.pk is None:
        instance.second_visit = instance.second_visit or instance.first_visit + datetime.timedelta(weeks=3)

A note might be at hand:

It is not that the pre_save signal is sent only once. Actually, it is sent whenever an instance of SomeModel is saved, i.e. also if an existing entry from the db is modified. The line if instance.pk is None: is present and necessary exactly for this reason: it makes sure that that the value is set only if the object does not yet exist in the database (i.e has no primary key yet).

j-i-l
  • 10,281
  • 3
  • 53
  • 70
  • Problem is, bulk methods (e.g. `bulk_create()` or `bulk_update()`) don't send pre_save signals or call the `.save()` method, rendering this soltion inadequate :( – Gabriel Tkacz Aug 17 '23 at 18:39