mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-27 20:14:01 +03:00
Two reproducible issues in DRF nested serializers with many to many through model
This commit is contained in:
parent
df584350b4
commit
6f9228ae59
|
@ -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
0
tests/issue/__init__.py
Normal file
42
tests/issue/migrations/0001_initial.py
Normal file
42
tests/issue/migrations/0001_initial.py
Normal 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'),
|
||||
),
|
||||
]
|
0
tests/issue/migrations/__init__.py
Normal file
0
tests/issue/migrations/__init__.py
Normal file
15
tests/issue/models.py
Normal file
15
tests/issue/models.py
Normal 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)
|
30
tests/issue/serializers.py
Normal file
30
tests/issue/serializers.py
Normal 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
66
tests/issue/test_issue.py
Normal 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
10
tests/issue/urls.py
Normal 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
13
tests/issue/views.py
Normal 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]
|
Loading…
Reference in New Issue
Block a user