Kristof Rado
Kristof Rado

Reputation: 881

Update multiple resource together in Django DRF

Lets say I have two models in Django:

class Inventory(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    added_by = models.ForeignKey(User, on_delete=models.SET("anonymous"),
                                 blank=True, null=True)
    name = models.CharField(max_length=100, unique=True)
    nickname = models.CharField(max_length=100, blank=True, null=True)
class InventoryProperties(models.Model):
    key = models.CharField(max_length=100)
    value = models.CharField(max_length=100)
    order = models.IntegerField()
    item = models.ForeignKey(Inventory, on_delete=models.CASCADE, related_name='properties')

What if I would like to add an Inventory and some properties to it from the frontend on the same page (form).

Then I would have to save the Inventory item first, then save the properties.

As I read in REST this should not be done with one resource (because this is a different resource, so now I have /inventory/:id/properties/): How to handle updates in a REST API?

What happens if something goes wrong during saving the properties? I can't rollback easily the saved Inventory item, so I end up with a half-saved object.

Also this is really hard to manage on the frontend, because if some error occurs during property saving, the frontend should not save the inventory again.

Upvotes: 1

Views: 369

Answers (3)

Kristof Rado
Kristof Rado

Reputation: 881

The solution for me was that I created a separate endpoint where I can update nested and multiple models at the same time, while providing typical RESTful endpoints also. This endpoint uses atomic transaction so that if anything goes wrong in the business logic, nothing gets committed.

Upvotes: 0

MSI Shafik
MSI Shafik

Reputation: 139

For this scenario, you can use django transaction which will rollback your database transaction if any error occur in your transaction block. Something Like this-

from django.db import transaction

with transaction.atomic():
    Inventory.objects.create(**kwargs)
    InventoryProperties.objects.create(**kwargs)

In the transaction block, if saving InventoryProperties gives any error then Inventory Table will be rollback.

Upvotes: 1

Yevhen Bondar
Yevhen Bondar

Reputation: 4707

Seems like InventoryProperties entity will never use without their Inventory relation. So, you can use JSONField Inventory.propetries instead of separate entity.

class Inventory(models.Model):
    props = models.JSONField(default=list)
    name = models.CharField(max_length=100, unique=True)


class InventorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Inventory
        fields = ['name', 'props']

This serializer would rewrite all props on every save in 1 SQL UPDATE query. As a bonus, you don't need to support InventoryProperties.order field.

Input example

{
   "name": "inv1",
   "props": [
      {"key": "size", "value": 30},
      {"key": "weight", "value": 16},
   ]
}

Also, you can add method to access your propetries by key

class Inventory(models.Model):
    def get_prop(self, key: str):
        for prop in self.props:
            if prop["key"] == key:
                return prop["value"]
        return None
       

If you want to add validation of JSON you can use this approach

Upvotes: 0

Related Questions