Reputation: 881
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
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
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
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