Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
# Generated by Django 4.2.6 on 2023-10-31 17:11

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
("tests", "0002_alter_profile_sites_setnullforeignkey_and_more"),
]

operations = [
migrations.CreateModel(
name="I49Product",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("number", models.CharField(max_length=50)),
("cost", models.FloatField()),
],
),
migrations.CreateModel(
name="I49Veterinary",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=50)),
],
),
migrations.AlterField(
model_name="accesskey",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="anotheravatar",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="anotherprofile",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="avatar",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="custompk",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="document",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="foreignkeychild",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="foreignkeyparent",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="i86genre",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="i86name",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="manytomanychild",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="manytomanyparent",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="onetoonechild",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="onetooneparent",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="page",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="profile",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="site",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="tag",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="taggeditem",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="team",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="ufmchild",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="ufmparent",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="user",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.CreateModel(
name="I49ProductDetail",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("cost", models.FloatField()),
(
"product",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="product_details",
to="tests.i49product",
),
),
(
"veterinary",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to="tests.i49veterinary",
),
),
],
options={
"unique_together": {("product", "veterinary")},
},
),
]
17 changes: 17 additions & 0 deletions tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,20 @@ class I86Name(models.Model):
class I86Genre(models.Model):
pass


class I49Veterinary(models.Model):
name = models.CharField(max_length=50)

class I49Product(models.Model):
number = models.CharField(max_length=50)
cost = models.FloatField()


class I49ProductDetail(models.Model):
product = models.ForeignKey("I49Product", on_delete=models.CASCADE, related_name='product_details')
veterinary = models.ForeignKey("I49Veterinary", on_delete=models.PROTECT)
cost = models.FloatField()

class Meta:
unique_together = ('product', 'veterinary')

19 changes: 19 additions & 0 deletions tests/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.validators import UniqueValidator
from rest_framework.relations import PrimaryKeyRelatedField

from drf_writable_nested.serializers import WritableNestedModelSerializer
from drf_writable_nested.mixins import UniqueFieldsMixin
Expand Down Expand Up @@ -375,3 +376,21 @@ class I86GenreSerializer(WritableNestedModelSerializer):
class Meta:
model = models.I86Genre
fields = ('id', 'names',)



class I49ProductDetailSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
veterinary = PrimaryKeyRelatedField(queryset=models.I49Veterinary.objects)

class Meta:
model = models.I49ProductDetail
fields = ('id', 'veterinary', 'cost')


class I49ProductSerializerWithPK(WritableNestedModelSerializer):
product_details = I49ProductDetailSerializer(many=True)

class Meta:
model = models.I49Product
fields = ('id', 'number', 'cost', 'product_details')
42 changes: 36 additions & 6 deletions tests/test_unique_fields_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,46 @@ def test_create_update_failed(self):
with self.assertRaises(ValidationError) as ctx:
serializer.save()
self.assertEqual(
ctx.exception.detail,
{'child': {'field': [unique_message_error_detail]}}
ctx.exception.detail, {"child": {"field": [unique_message_error_detail]}}
)

def test_unique_field_not_required_for_partial_updates(self):
child = models.UFMChild.objects.create(field='value')
child = models.UFMChild.objects.create(field="value")
serializer = serializers.UFMChildSerializer(
instance=child,
data={},
partial=True
instance=child, data={}, partial=True
)
self.assertTrue(serializer.is_valid())
serializer.save()


class I49Test(TestCase):
def test_issue_49(self):
veterinary = models.I49Veterinary.objects.create(name="Veterinary 1")
serializer = serializers.I49ProductSerializerWithPK(
data={
"number": "Product XX",
"cost": 20000,
"product_details": [{"veterinary": veterinary.id, "cost": 10000}],
}
)

self.assertTrue(serializer.is_valid())
instance = serializer.save()

assert len(models.I49Product.objects.all()) == 1
assert len(models.I49ProductDetail.objects.all()) == 1

serializer = serializers.I49ProductSerializerWithPK(
instance=instance,
data={
"number": "Product XX",
"cost": 20000,
"product_details": [{"veterinary": veterinary.id, "cost": 10000}],
},
)

self.assertTrue(serializer.is_valid())
serializer.save()

assert len(models.I49Product.objects.all()) == 1
assert len(models.I49ProductDetail.objects.all()) == 1
Loading