Django Transaction Atomic Nedir¶
Bu yazımızda django'daki transaction atomic yapısını Django Rest Framework üzerindeki bir örnek ile anlamaya çalışacağız.
Django'nun varsayılan transaction davranışı otomatik işleme(autocommit) olarak çalışmaktır. Yani her bir sorgu(query) veritabanına anlık olarak işlenmektedir. Aşağıdaki örnek ile burayı anlamaya çalışalım.
- Şimdi Product model sınıfına ait verileri python manage.py shell diyerek ekleyelim.>>> p1 = Product(name="product 2021")
>>> p1.name
'product 2021'
>>> p1.id
# Henüz transaction gerçekleşmediği için
# herhangi bir cevap yok
>>> p1.save() # transaction işlemini başlatıyoruz.
>>> p1.id
28 # ve sonuç!
class ProductModelSerializer(serializers.ModelSerializer):
# ...
# .....
def create(self, validated_data):
productmaterial_set = validated_data.pop('productmaterial_set')
# Yeni product Product tablosuna ekleniyor
new_product = Product.objects.create(**validated_data)
# Yeni product ile beraber gönderilen material ve rate
# verileri ProductMaterial tablosuna ekleniyor.
for prod_material_data in productmaterial_set:
ProductMaterial.objects.create(
product=new_product,
material=prod_material_data.get('material'),
rate=prod_material_data.get('rate')
)
return new_product
from decimal import Decimal
class Product(models.Model):
name = models.CharField(max_length=60)
def is_material_rate_sum_valid(self):
rates = [pm.rate for pm in self.productmaterial_set.all()]
return sum(rates)==Decimal("100")
class ProductModelSerializer(serializers.ModelSerializer):
#...
def create(self, validated_data):
productmaterial_set = validated_data.pop('productmaterial_set')
new_product = Product.objects.create(**validated_data)
for prod_material_data in productmaterial_set:
ProductMaterial.objects.create(
product=new_product,
material=prod_material_data.get('material'),
rate=prod_material_data.get('rate')
)
# Eğer product material rate toplamı 100 değilse
# client'a hata mesajını göster
if not new_product.is_material_rate_sum_valid():
raise serializers.ValidationError("total material rate must be 100")
return new_product
{
"product_materials": [
{"material":3, "rate":"20"},
{"material":1, "rate":"60"}
],
"name": "test product"
}
HTTP 400 Bad Request
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
"total material rate must be 100"
]
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"id": 30,
"product_materials": [
{
"id": 13,
"product": 30,
"material": 3,
"rate": "20.00",
"material_name": "material3",
"product_name": "test product"
},
{
"id": 14,
"product": 30,
"material": 1,
"rate": "60.00",
"material_name": "material1",
"product_name": "test product"
}
],
"name": "test product"
}
]
from django.db import IntegrityError, transaction
class ProductModelSerializer(serializers.ModelSerializer):
# ...
def create(self, validated_data):
try:
with transaction.atomic():
productmaterial_set = validated_data.pop('productmaterial_set')
new_product = Product.objects.create(**validated_data)
for prod_material_data in productmaterial_set:
ProductMaterial.objects.create(
product=new_product,
material=prod_material_data.get('material'),
rate=prod_material_data.get('rate')
)
if not new_product.is_material_rate_sum_valid():
raise serializers.ValidationError("total material rate must be 100")
return new_product
except IntegrityError as ierr:
raise ierr
{
"product_materials": [
{"material":2, "rate":"20"},
{"material":3, "rate":"45"}
],
"name": "product 2023"
}
HTTP 400 Bad Request
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
"total material rate must be 100"
]
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"id": 30,
"product_materials": [
{
"id": 13,
"product": 30,
"material": 3,
"rate": "20.00",
"material_name": "material3",
"product_name": "test product"
},
{
"id": 14,
"product": 30,
"material": 1,
"rate": "60.00",
"material_name": "material1",
"product_name": "test product"
}
],
"name": "test product"
}
]