@@ -1398,6 +1398,23 @@ def get_extra_kwargs(self):
1398
1398
1399
1399
return extra_kwargs
1400
1400
1401
+ def get_unique_together_constraints (self , model ):
1402
+ """
1403
+ Returns iterator of (fields, queryset), each entry describes an unique together
1404
+ constraint on `fields` in `queryset`.
1405
+ """
1406
+ for parent_class in [model ] + list (model ._meta .parents ):
1407
+ for unique_together in parent_class ._meta .unique_together :
1408
+ yield unique_together , model ._default_manager
1409
+ for constraint in parent_class ._meta .constraints :
1410
+ if isinstance (constraint , models .UniqueConstraint ) and len (constraint .fields ) > 1 :
1411
+ yield (
1412
+ constraint .fields ,
1413
+ model ._default_manager
1414
+ if constraint .condition is None
1415
+ else model ._default_manager .filter (constraint .condition )
1416
+ )
1417
+
1401
1418
def get_uniqueness_extra_kwargs (self , field_names , declared_fields , extra_kwargs ):
1402
1419
"""
1403
1420
Return any additional field options that need to be included as a
@@ -1426,12 +1443,11 @@ def get_uniqueness_extra_kwargs(self, field_names, declared_fields, extra_kwargs
1426
1443
1427
1444
unique_constraint_names -= {None }
1428
1445
1429
- # Include each of the `unique_together` field names,
1446
+ # Include each of the `unique_together` and `UniqueConstraint` field names,
1430
1447
# so long as all the field names are included on the serializer.
1431
- for parent_class in [model ] + list (model ._meta .parents ):
1432
- for unique_together_list in parent_class ._meta .unique_together :
1433
- if set (field_names ).issuperset (unique_together_list ):
1434
- unique_constraint_names |= set (unique_together_list )
1448
+ for unique_together_list , queryset in self .get_unique_together_constraints (model ):
1449
+ if set (field_names ).issuperset (unique_together_list ):
1450
+ unique_constraint_names |= set (unique_together_list )
1435
1451
1436
1452
# Now we have all the field names that have uniqueness constraints
1437
1453
# applied, we can add the extra 'required=...' or 'default=...'
@@ -1526,11 +1542,6 @@ def get_unique_together_validators(self):
1526
1542
"""
1527
1543
Determine a default set of validators for any unique_together constraints.
1528
1544
"""
1529
- model_class_inheritance_tree = (
1530
- [self .Meta .model ] +
1531
- list (self .Meta .model ._meta .parents )
1532
- )
1533
-
1534
1545
# The field names we're passing though here only include fields
1535
1546
# which may map onto a model field. Any dotted field name lookups
1536
1547
# cannot map to a field, and must be a traversal, so we're not
@@ -1556,34 +1567,33 @@ def get_unique_together_validators(self):
1556
1567
# Note that we make sure to check `unique_together` both on the
1557
1568
# base model class, but also on any parent classes.
1558
1569
validators = []
1559
- for parent_class in model_class_inheritance_tree :
1560
- for unique_together in parent_class ._meta .unique_together :
1561
- # Skip if serializer does not map to all unique together sources
1562
- if not set (source_map ).issuperset (unique_together ):
1563
- continue
1564
-
1565
- for source in unique_together :
1566
- assert len (source_map [source ]) == 1 , (
1567
- "Unable to create `UniqueTogetherValidator` for "
1568
- "`{model}.{field}` as `{serializer}` has multiple "
1569
- "fields ({fields}) that map to this model field. "
1570
- "Either remove the extra fields, or override "
1571
- "`Meta.validators` with a `UniqueTogetherValidator` "
1572
- "using the desired field names."
1573
- .format (
1574
- model = self .Meta .model .__name__ ,
1575
- serializer = self .__class__ .__name__ ,
1576
- field = source ,
1577
- fields = ', ' .join (source_map [source ]),
1578
- )
1579
- )
1570
+ for unique_together , queryset in self .get_unique_together_constraints (self .Meta .model ):
1571
+ # Skip if serializer does not map to all unique together sources
1572
+ if not set (source_map ).issuperset (unique_together ):
1573
+ continue
1580
1574
1581
- field_names = tuple (source_map [f ][0 ] for f in unique_together )
1582
- validator = UniqueTogetherValidator (
1583
- queryset = parent_class ._default_manager ,
1584
- fields = field_names
1575
+ for source in unique_together :
1576
+ assert len (source_map [source ]) == 1 , (
1577
+ "Unable to create `UniqueTogetherValidator` for "
1578
+ "`{model}.{field}` as `{serializer}` has multiple "
1579
+ "fields ({fields}) that map to this model field. "
1580
+ "Either remove the extra fields, or override "
1581
+ "`Meta.validators` with a `UniqueTogetherValidator` "
1582
+ "using the desired field names."
1583
+ .format (
1584
+ model = self .Meta .model .__name__ ,
1585
+ serializer = self .__class__ .__name__ ,
1586
+ field = source ,
1587
+ fields = ', ' .join (source_map [source ]),
1588
+ )
1585
1589
)
1586
- validators .append (validator )
1590
+
1591
+ field_names = tuple (source_map [f ][0 ] for f in unique_together )
1592
+ validator = UniqueTogetherValidator (
1593
+ queryset = queryset ,
1594
+ fields = field_names
1595
+ )
1596
+ validators .append (validator )
1587
1597
return validators
1588
1598
1589
1599
def get_unique_for_date_validators (self ):
0 commit comments