Skip to content

Commit 6250f61

Browse files
committed
Add failing test for #49
1 parent 6b24fae commit 6250f61

File tree

4 files changed

+311
-6
lines changed

4 files changed

+311
-6
lines changed
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
# Generated by Django 4.2.6 on 2023-10-31 17:11
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("tests", "0002_alter_profile_sites_setnullforeignkey_and_more"),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name="I49Product",
16+
fields=[
17+
(
18+
"id",
19+
models.BigAutoField(
20+
auto_created=True,
21+
primary_key=True,
22+
serialize=False,
23+
verbose_name="ID",
24+
),
25+
),
26+
("number", models.CharField(max_length=50)),
27+
("cost", models.FloatField()),
28+
],
29+
),
30+
migrations.CreateModel(
31+
name="I49Veterinary",
32+
fields=[
33+
(
34+
"id",
35+
models.BigAutoField(
36+
auto_created=True,
37+
primary_key=True,
38+
serialize=False,
39+
verbose_name="ID",
40+
),
41+
),
42+
("name", models.CharField(max_length=50)),
43+
],
44+
),
45+
migrations.AlterField(
46+
model_name="accesskey",
47+
name="id",
48+
field=models.BigAutoField(
49+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
50+
),
51+
),
52+
migrations.AlterField(
53+
model_name="anotheravatar",
54+
name="id",
55+
field=models.BigAutoField(
56+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
57+
),
58+
),
59+
migrations.AlterField(
60+
model_name="anotherprofile",
61+
name="id",
62+
field=models.BigAutoField(
63+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
64+
),
65+
),
66+
migrations.AlterField(
67+
model_name="avatar",
68+
name="id",
69+
field=models.BigAutoField(
70+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
71+
),
72+
),
73+
migrations.AlterField(
74+
model_name="custompk",
75+
name="id",
76+
field=models.BigAutoField(
77+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
78+
),
79+
),
80+
migrations.AlterField(
81+
model_name="document",
82+
name="id",
83+
field=models.BigAutoField(
84+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
85+
),
86+
),
87+
migrations.AlterField(
88+
model_name="foreignkeychild",
89+
name="id",
90+
field=models.BigAutoField(
91+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
92+
),
93+
),
94+
migrations.AlterField(
95+
model_name="foreignkeyparent",
96+
name="id",
97+
field=models.BigAutoField(
98+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
99+
),
100+
),
101+
migrations.AlterField(
102+
model_name="i86genre",
103+
name="id",
104+
field=models.BigAutoField(
105+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
106+
),
107+
),
108+
migrations.AlterField(
109+
model_name="i86name",
110+
name="id",
111+
field=models.BigAutoField(
112+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
113+
),
114+
),
115+
migrations.AlterField(
116+
model_name="manytomanychild",
117+
name="id",
118+
field=models.BigAutoField(
119+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
120+
),
121+
),
122+
migrations.AlterField(
123+
model_name="manytomanyparent",
124+
name="id",
125+
field=models.BigAutoField(
126+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
127+
),
128+
),
129+
migrations.AlterField(
130+
model_name="onetoonechild",
131+
name="id",
132+
field=models.BigAutoField(
133+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
134+
),
135+
),
136+
migrations.AlterField(
137+
model_name="onetooneparent",
138+
name="id",
139+
field=models.BigAutoField(
140+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
141+
),
142+
),
143+
migrations.AlterField(
144+
model_name="page",
145+
name="id",
146+
field=models.BigAutoField(
147+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
148+
),
149+
),
150+
migrations.AlterField(
151+
model_name="profile",
152+
name="id",
153+
field=models.BigAutoField(
154+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
155+
),
156+
),
157+
migrations.AlterField(
158+
model_name="site",
159+
name="id",
160+
field=models.BigAutoField(
161+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
162+
),
163+
),
164+
migrations.AlterField(
165+
model_name="tag",
166+
name="id",
167+
field=models.BigAutoField(
168+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
169+
),
170+
),
171+
migrations.AlterField(
172+
model_name="taggeditem",
173+
name="id",
174+
field=models.BigAutoField(
175+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
176+
),
177+
),
178+
migrations.AlterField(
179+
model_name="team",
180+
name="id",
181+
field=models.BigAutoField(
182+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
183+
),
184+
),
185+
migrations.AlterField(
186+
model_name="ufmchild",
187+
name="id",
188+
field=models.BigAutoField(
189+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
190+
),
191+
),
192+
migrations.AlterField(
193+
model_name="ufmparent",
194+
name="id",
195+
field=models.BigAutoField(
196+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
197+
),
198+
),
199+
migrations.AlterField(
200+
model_name="user",
201+
name="id",
202+
field=models.BigAutoField(
203+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
204+
),
205+
),
206+
migrations.CreateModel(
207+
name="I49ProductDetail",
208+
fields=[
209+
(
210+
"id",
211+
models.BigAutoField(
212+
auto_created=True,
213+
primary_key=True,
214+
serialize=False,
215+
verbose_name="ID",
216+
),
217+
),
218+
("cost", models.FloatField()),
219+
(
220+
"product",
221+
models.ForeignKey(
222+
on_delete=django.db.models.deletion.CASCADE,
223+
related_name="product_details",
224+
to="tests.i49product",
225+
),
226+
),
227+
(
228+
"veterinary",
229+
models.ForeignKey(
230+
on_delete=django.db.models.deletion.PROTECT,
231+
to="tests.i49veterinary",
232+
),
233+
),
234+
],
235+
options={
236+
"unique_together": {("product", "veterinary")},
237+
},
238+
),
239+
]

tests/models.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,20 @@ class I86Name(models.Model):
168168
class I86Genre(models.Model):
169169
pass
170170

171+
172+
class I49Veterinary(models.Model):
173+
name = models.CharField(max_length=50)
174+
175+
class I49Product(models.Model):
176+
number = models.CharField(max_length=50)
177+
cost = models.FloatField()
178+
179+
180+
class I49ProductDetail(models.Model):
181+
product = models.ForeignKey("I49Product", on_delete=models.CASCADE, related_name='product_details')
182+
veterinary = models.ForeignKey("I49Veterinary", on_delete=models.PROTECT)
183+
cost = models.FloatField()
184+
185+
class Meta:
186+
unique_together = ('product', 'veterinary')
187+

tests/serializers.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from rest_framework import serializers
33
from rest_framework.exceptions import ValidationError
44
from rest_framework.validators import UniqueValidator
5+
from rest_framework.relations import PrimaryKeyRelatedField
56

67
from drf_writable_nested.serializers import WritableNestedModelSerializer
78
from drf_writable_nested.mixins import UniqueFieldsMixin
@@ -374,3 +375,21 @@ class I86GenreSerializer(WritableNestedModelSerializer):
374375
class Meta:
375376
model = models.I86Genre
376377
fields = ('id', 'names',)
378+
379+
380+
381+
class I49ProductDetailSerializer(serializers.ModelSerializer):
382+
id = serializers.IntegerField(required=False)
383+
veterinary = PrimaryKeyRelatedField(queryset=models.I49Veterinary.objects)
384+
385+
class Meta:
386+
model = models.I49ProductDetail
387+
fields = ('id', 'veterinary', 'cost')
388+
389+
390+
class I49ProductSerializerWithPK(WritableNestedModelSerializer):
391+
product_details = I49ProductDetailSerializer(many=True)
392+
393+
class Meta:
394+
model = models.I49Product
395+
fields = ('id', 'number', 'cost', 'product_details')

tests/test_unique_fields_mixin.py

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,46 @@ def test_create_update_failed(self):
8888
with self.assertRaises(ValidationError) as ctx:
8989
serializer.save()
9090
self.assertEqual(
91-
ctx.exception.detail,
92-
{'child': {'field': [unique_message_error_detail]}}
91+
ctx.exception.detail, {"child": {"field": [unique_message_error_detail]}}
9392
)
9493

9594
def test_unique_field_not_required_for_partial_updates(self):
96-
child = models.UFMChild.objects.create(field='value')
95+
child = models.UFMChild.objects.create(field="value")
9796
serializer = serializers.UFMChildSerializer(
98-
instance=child,
99-
data={},
100-
partial=True
97+
instance=child, data={}, partial=True
10198
)
10299
self.assertTrue(serializer.is_valid())
103100
serializer.save()
101+
102+
103+
class I49Test(TestCase):
104+
def test_issue_49(self):
105+
veterinary = models.I49Veterinary.objects.create(name="Veterinary 1")
106+
serializer = serializers.I49ProductSerializerWithPK(
107+
data={
108+
"number": "Product XX",
109+
"cost": 20000,
110+
"product_details": [{"veterinary": veterinary.id, "cost": 10000}],
111+
}
112+
)
113+
114+
self.assertTrue(serializer.is_valid())
115+
instance = serializer.save()
116+
117+
assert len(models.I49Product.objects.all()) == 1
118+
assert len(models.I49ProductDetail.objects.all()) == 1
119+
120+
serializer = serializers.I49ProductSerializerWithPK(
121+
instance=instance,
122+
data={
123+
"number": "Product XX",
124+
"cost": 20000,
125+
"product_details": [{"veterinary": veterinary.id, "cost": 10000}],
126+
},
127+
)
128+
129+
self.assertTrue(serializer.is_valid())
130+
serializer.save()
131+
132+
assert len(models.I49Product.objects.all()) == 1
133+
assert len(models.I49ProductDetail.objects.all()) == 1

0 commit comments

Comments
 (0)