Create one object and bulk-create many related objects

Question:

Am new to Django (and Python) and have a problem initializing my model. For every school year created in the database, I want the corresponding days (not exactly 365/366, but > 300) to be created with a default value of 0 (a one-to-many relationship). I learned that I should not try to do this in a constructor, but after the constructor has run (and the object and its attributes have become accessible). So is this a case for using signals or should I override pre- or post-save(). And why? Or is there a third option which I have missed so far?

from django.db import models
import pandas as pd


class BaseModel(models.Model):
    objects = models.Manager()

    class Meta:
        abstract = True


class SchoolYear(BaseModel):
    start = models.DateField()
    end = models.DateField()

    def init_days(self):
        current_date = self.start
        delta = timedelta(days=1)
        while current_date <= self.end:
            self.days.add(schoolyear=self, date=current_date, value=0)
            current_date += delta


class Day(BaseModel):
    schoolyear = models.ForeignKey(SchoolYear, on_delete=models.CASCADE)
    date = models.DateField()
    value = models.IntegerField()
Asked By: kalabalik

||

Answers:

Signals tend to be considered an anti-pattern in all but a few cases. You can accomplish the above in a readable fashion by overriding the save function for your year model, and using bulk_create to create a large number of similar objects, giving you something like:

class SchoolYear(BaseModel):
    start = models.DateField()
    end = models.DateField()

    def save(self):
        #see if this is a new year being created by checking for an ID
        being_created = self.pk is None
        #save it anyway (after this it does have an ID)
        super().save(*args, **kwargs)
        #test if this is a newly created year and create days if so
        if being_created:
            current_date = self.start
            delta = timedelta(days=1)
            days = []
            #create a list of days for creation
            while current_date <= self.end:
                days.append(Day(schoolyear=self, date=current_date, value=0))
                current_date += delta
            #use bulk_create to create the list of days efficiently.
            Day.objects.bulk_create(days)
Answered By: SamSparx