@@ -1373,6 +1373,23 @@ def get_extra_kwargs(self):
1373
1373
1374
1374
return extra_kwargs
1375
1375
1376
+ def get_unique_together_constraints (self , model ):
1377
+ """
1378
+ Returns iterator of (fields, queryset), each entry describe an unique together
1379
+ constraint on `fields` in `queryset`.
1380
+ """
1381
+ for parent_class in [model ] + list (model ._meta .parents ):
1382
+ for unique_together in parent_class ._meta .unique_together :
1383
+ yield unique_together , model ._default_manager
1384
+ for constraint in parent_class ._meta .constraints :
1385
+ if isinstance (constraint , models .UniqueConstraint ) and len (constraint .fields ) > 1 :
1386
+ yield (
1387
+ constraint .fields ,
1388
+ model ._default_manager
1389
+ if constraint .condition is None
1390
+ else model ._default_manager .filter (constraint .condition )
1391
+ )
1392
+
1376
1393
def get_uniqueness_extra_kwargs (self , field_names , declared_fields , extra_kwargs ):
1377
1394
"""
1378
1395
Return any additional field options that need to be included as a
@@ -1401,12 +1418,11 @@ def get_uniqueness_extra_kwargs(self, field_names, declared_fields, extra_kwargs
1401
1418
1402
1419
unique_constraint_names -= {None }
1403
1420
1404
- # Include each of the `unique_together` field names,
1421
+ # Include each of the `unique_together` and `UniqueConstraint` field names,
1405
1422
# so long as all the field names are included on the serializer.
1406
- for parent_class in [model ] + list (model ._meta .parents ):
1407
- for unique_together_list in parent_class ._meta .unique_together :
1408
- if set (field_names ).issuperset (set (unique_together_list )):
1409
- unique_constraint_names |= set (unique_together_list )
1423
+ for unique_together_list , queryset in self .get_unique_together_constraints (model ):
1424
+ if set (field_names ).issuperset (set (unique_together_list )):
1425
+ unique_constraint_names |= set (unique_together_list )
1410
1426
1411
1427
# Now we have all the field names that have uniqueness constraints
1412
1428
# applied, we can add the extra 'required=...' or 'default=...'
@@ -1503,11 +1519,6 @@ def get_unique_together_validators(self):
1503
1519
"""
1504
1520
Determine a default set of validators for any unique_together constraints.
1505
1521
"""
1506
- model_class_inheritance_tree = (
1507
- [self .Meta .model ] +
1508
- list (self .Meta .model ._meta .parents )
1509
- )
1510
-
1511
1522
# The field names we're passing though here only include fields
1512
1523
# which may map onto a model field. Any dotted field name lookups
1513
1524
# cannot map to a field, and must be a traversal, so we're not
@@ -1533,34 +1544,33 @@ def get_unique_together_validators(self):
1533
1544
# Note that we make sure to check `unique_together` both on the
1534
1545
# base model class, but also on any parent classes.
1535
1546
validators = []
1536
- for parent_class in model_class_inheritance_tree :
1537
- for unique_together in parent_class ._meta .unique_together :
1538
- # Skip if serializer does not map to all unique together sources
1539
- if not set (source_map ).issuperset (set (unique_together )):
1540
- continue
1541
-
1542
- for source in unique_together :
1543
- assert len (source_map [source ]) == 1 , (
1544
- "Unable to create `UniqueTogetherValidator` for "
1545
- "`{model}.{field}` as `{serializer}` has multiple "
1546
- "fields ({fields}) that map to this model field. "
1547
- "Either remove the extra fields, or override "
1548
- "`Meta.validators` with a `UniqueTogetherValidator` "
1549
- "using the desired field names."
1550
- .format (
1551
- model = self .Meta .model .__name__ ,
1552
- serializer = self .__class__ .__name__ ,
1553
- field = source ,
1554
- fields = ', ' .join (source_map [source ]),
1555
- )
1556
- )
1547
+ for unique_together , queryset in self .get_unique_together_constraints (self .Meta .model ):
1548
+ # Skip if serializer does not map to all unique together sources
1549
+ if not set (source_map ).issuperset (set (unique_together )):
1550
+ continue
1557
1551
1558
- field_names = tuple (source_map [f ][0 ] for f in unique_together )
1559
- validator = UniqueTogetherValidator (
1560
- queryset = parent_class ._default_manager ,
1561
- fields = field_names
1552
+ for source in unique_together :
1553
+ assert len (source_map [source ]) == 1 , (
1554
+ "Unable to create `UniqueTogetherValidator` for "
1555
+ "`{model}.{field}` as `{serializer}` has multiple "
1556
+ "fields ({fields}) that map to this model field. "
1557
+ "Either remove the extra fields, or override "
1558
+ "`Meta.validators` with a `UniqueTogetherValidator` "
1559
+ "using the desired field names."
1560
+ .format (
1561
+ model = self .Meta .model .__name__ ,
1562
+ serializer = self .__class__ .__name__ ,
1563
+ field = source ,
1564
+ fields = ', ' .join (source_map [source ]),
1565
+ )
1562
1566
)
1563
- validators .append (validator )
1567
+
1568
+ field_names = tuple (source_map [f ][0 ] for f in unique_together )
1569
+ validator = UniqueTogetherValidator (
1570
+ queryset = queryset ,
1571
+ fields = field_names
1572
+ )
1573
+ validators .append (validator )
1564
1574
return validators
1565
1575
1566
1576
def get_unique_for_date_validators (self ):
0 commit comments