Two reproducible issues in DRF nested serializers with many to many through model

This commit is contained in:
enrico 2022-08-30 09:55:27 +08:00
parent df584350b4
commit 6f9228ae59
9 changed files with 177 additions and 0 deletions

View File

@ -63,6 +63,7 @@ def pytest_configure(config):
'rest_framework.authtoken',
'tests.authentication',
'tests.generic_relations',
'tests.issue',
'tests.importable',
'tests',
),

0
tests/issue/__init__.py Normal file
View File

View File

@ -0,0 +1,42 @@
# Generated by Django 3.1.13 on 2022-01-23 09:00
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Item',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=10)),
],
),
migrations.CreateModel(
name='ItemAmount',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('amount', models.IntegerField()),
('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='issue.item')),
],
),
migrations.CreateModel(
name='Summary',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('items', models.ManyToManyField(through='issue.ItemAmount', to='issue.Item')),
],
),
migrations.AddField(
model_name='itemamount',
name='summary',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='issue.summary'),
),
]

View File

15
tests/issue/models.py Normal file
View File

@ -0,0 +1,15 @@
from django.db import models
class Item(models.Model):
name = models.CharField(max_length=10)
class ItemAmount(models.Model):
summary = models.ForeignKey('Summary', on_delete=models.CASCADE)
item = models.ForeignKey(Item, on_delete=models.CASCADE)
amount = models.IntegerField()
class Summary(models.Model):
items = models.ManyToManyField(Item, through=ItemAmount)

View File

@ -0,0 +1,30 @@
from rest_framework import serializers
from . import models
class ItemAmountSerializer(serializers.ModelSerializer):
item = serializers.PrimaryKeyRelatedField(queryset=models.Item.objects.all())
class Meta:
model = models.ItemAmount
fields = ('item', 'amount')
class SummarySerializer(serializers.ModelSerializer):
items = ItemAmountSerializer(many=True)
def create(self, validated_data):
items = validated_data.pop('items')
instance = super().create(validated_data)
for item in items:
instance.items.add(
item['item'], through_defaults=dict(
amount=item['amount']
)
)
return instance
class Meta:
model = models.Summary
fields = ('items', )

66
tests/issue/test_issue.py Normal file
View File

@ -0,0 +1,66 @@
from django.test import TestCase, override_settings
from django.urls import reverse
from rest_framework.test import APIClient
from rest_framework.utils import json
from . import models, serializers
class TestSerializer(TestCase):
def test_create(self):
item = models.Item.objects.create(name='test')
data = {
"items": [
{
"item": item.id,
"amount": 100,
}
],
}
serializer = serializers.SummarySerializer(data=data)
serializer.is_valid(raise_exception=True)
expected_data = {
"items": [
{
"item": item,
"amount": 100,
}
],
}
assert serializer.validated_data == expected_data
serializer.save()
assert models.Summary.objects.count() == 1
@override_settings(ROOT_URLCONF='tests.issue.urls')
class TestIssueViewSet(TestCase):
def test_create(self):
api_client = APIClient()
item = models.Item.objects.create(name='test')
data = {
"items": [
{
"item": item.id,
"amount": 100,
}
],
}
response = api_client.post(reverse('summary-list'), data)
print(response.content)
assert response.status_code == 201
def test_create_with_json(self):
api_client = APIClient()
item = models.Item.objects.create(name='test')
data = {
"items": [
{
"item": item.id,
"amount": 100,
}
],
}
response = api_client.post(reverse('summary-list'), json.dumps(data), content_type='application/json')
print(response.content)
assert response.status_code == 201

10
tests/issue/urls.py Normal file
View File

@ -0,0 +1,10 @@
from rest_framework.routers import DefaultRouter
from . import views
app_name = "issue"
router = DefaultRouter()
router.register(r'summary', views.SummaryViewSet, basename='summary')
urlpatterns = router.urls

13
tests/issue/views.py Normal file
View File

@ -0,0 +1,13 @@
from rest_framework import viewsets
from rest_framework.permissions import AllowAny
from . import models, serializers
class SummaryViewSet(viewsets.ModelViewSet):
"""
A simple ViewSet for viewing and editing accounts.
"""
queryset = models.Summary.objects.all()
serializer_class = serializers.SummarySerializer
permission_classes = [AllowAny]