'Using Django bulk_create gives UNIQUE constraint failed:solgeo_hourly.id

I have two models, namely Project and Hourly. Whenever a new project is created, I want to also generate 8760 new Hourly instances.

There are two methods, one is very costly since 8760 times a new row is written to the database, i.e.:

for i in range(0, 24):
    clock_time=i*3600
    hourly = Hourly.objects.create(project=project, clock_time=clock_time)
    hourly.save()

The other method is by using the Django bulk_create(hourlys) method, which works fine whenever I use the default Hourly() constructor. However I want to use my custom Hourly.objects.create() function. This however gives an UNIQUE constraint error for hourly.id, i.e.:

class HourlyManager(models.Manager):
    """"""
    def create(self, project, clock_time):
        hourly = super().create(project=project, clock_time=clock_time)
        hourly.local_civil_time = services.calc_local_civil_time(clock_time, 
                                                                 project.TMZ, 
                                                                 project.lon)
        hourly.save()
        return hourly


class ProjectManager(models.Manager):
    """"""
    def create(self, owner, project_name, TMZ, lat, lon):
        project = super().create(owner=owner, project_name=project_name, TMZ=TMZ, lat=lat, lon=lon)
        project.save()

        hourlys = []
        for i in range(0, 24):
            hourlys.append(Hourly.objects.create(project=project, clock_time=clock_time))
        Hourly.objects.bulk_create(hourlys)

        return project


class Project(models.Model):
    objects = ProjectManager()

    owner           = models.ForeignKey('auth.User', related_name='projects', on_delete=models.CASCADE)
    project_name    = models.CharField(max_length=200)
    TMZ             = models.FloatField(default=0)
    lat             = models.FloatField(default=0)  # Radians
    lon             = models.FloatField(default=0)  # Radians


class Hourly(models.Model):
    objects = HourlyManager()

    project             = models.ForeignKey(Project, related_name='hourlys', on_delete=models.CASCADE)
    clock_time          = models.FloatField(default=0) 
    local_civil_time    = models.FloatField(default=0)


Solution 1:[1]

When you are calling hourlys.append(Hourly.objects.create(project=project, clock_time=clock_time)), object of Hourly model is already created in the database, and you are appending that object in the hourlys list. Each object in that list already has an id associated with it. So when you are trying to do bulk_create in the next line, django tries to create new object with the same id and thus fails the unique constraint.

Solution 2:[2]

This is what I ended up with and it seems to work fine. I use bulk_create in the ProjectManager, than I create a new classmethod (@classmethod) called create() for the Hourly model. This solves the problem that the hourlys do not posess a unique ID. I'm not exactly why but it does seem to work fine.

class ProjectManager(models.Manager):
    """"""
    def create(self, owner, project_name, TMZ, lat, lon):
        project = super().create(owner=owner, project_name=project_name, TMZ=TMZ, lat=lat, lon=lon) # "CREATED BY PROJECTMANAGER"
        project.save()

        hourlys = []
        for i in range(0, 24):
            clock_time = i*3600
            hourlys.append(Hourly.create(project=project, clock_time=clock_time))
        Hourly.objects.bulk_create(hourlys)
        return project


class Project(models.Model):
    objects = ProjectManager()

    owner           = models.ForeignKey('auth.User', related_name='projects', on_delete=models.CASCADE)
    project_name    = models.CharField(max_length=200)
    TMZ             = models.FloatField(default=0)
    lat             = models.FloatField(default=0)  # Radians
    lon             = models.FloatField(default=0)  # Radians


class Hourly(models.Model):
    #objects = HourlyManager()

    project             = models.ForeignKey(Project, related_name='hourlys', on_delete=models.CASCADE)
    clock_time          = models.FloatField(default=0) # One year = 31557600 seconds primary_key=True, 
    declination         = models.FloatField(default=0)
    local_civil_time    = models.FloatField(default=0)
    equation_of_time    = models.FloatField(default=0)
    solar_time          = models.FloatField(default=0)
    solar_hour_angle    = models.FloatField(default=0)

    @classmethod
    def create(cls, project, clock_time):
        hourly = cls(project=project, clock_time=clock_time)
        hourly.declination      = services.calc_declination(clock_time)
        hourly.local_civil_time = services.calc_local_civil_time(clock_time, 
                                                                 project.TMZ, 
                                                                 project.lon)
        hourly.equation_of_time = services.calc_equation_of_time(clock_time)
        hourly.solar_time       = services.calc_solar_time(hourly.local_civil_time,
                                                           hourly.equation_of_time)
        hourly.solar_hour_angle = services.calc_solar_hour_angle(hourly.solar_time)
        return hourly

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ritesh Agrawal
Solution 2 arne