Search code examples
djangobulk-create

Django bulk_create() with inexistent ForeignKey


I have 2 models. For simplicity, I deleted all unnecessary fields.

class User(models.Model):
    id = models.CharField(max_length=36, primary_key=True, null=False)
    age = models.IntegerFiels(default=-1)

and

class Device(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)

Through the API, I receive information about devices and create a list with devices.

devices = []
for device_data in data:
    devices.append(
        Device(
            user_id=device_data("user_id")
        )
    )

Once I have the list of devices, I add it to the database.

Device.objects.bulk_create(devices, ignore_conflicts=True)

The problem is that if, when creating a Device, I specified a user_id that is not in the database, I will get a crash because I am trying to link with a non-existent id.

Now to get around this I do the following:

devices = []
users = []
for device_data in data:
    users.append(
        User(
            id=device_data("user_id", age = -1)
        )
    )
    devices.append(
        Device(
            user_id=device_data("user_id")
        )
    )

Then, when saving:

User.objects.bulk_create(users, ignore_conflicts=True)
Device.objects.bulk_create(devices, ignore_conflicts=True)
User.objects.filter(age=-1).delete()

How can I make sure that when saving, if the specified user_id does not exist, this device is not added?

UPDATED:

Another solution is to change the field

User.age = models.IntegerFiels(null=True) 

and

 Device.user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)

After this, I can save devices without users.

Device.objects.bulk_create(devices, ignore_conflicts=True)

After which, do: Device.objects.filter(user__age__isnull=True).delete()

This works, but I don’t like the fact that you need to check for age, and not for the user itself.


Solution

  • The problem is that if, when creating a Device, I specified a user_id that is not in the database, I will get a crash because I am trying to link with a non-existent id.

    If you don't want your devices to require a User, then make the ForeignKey nullable:

    class Device(models.Model):
        user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=true)
    

    UPDATE
    Oh I misunderstood. You want to bulk create only those Devices that have an existing user id? Why not just filter out the device data with usable user_id?

    devices = []
    user_ids = User.objects.values_list("id", flat=True)
    # assuming that device_data is a dictionary
    for device_data in data:
        if "user_id" in device_data and device_data["user_id"] in user_ids:
            devices.append(Device(**device_data))