@@ -1373,6 +1373,23 @@ def get_extra_kwargs(self):
13731373
13741374 return extra_kwargs
13751375
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+
13761393 def get_uniqueness_extra_kwargs (self , field_names , declared_fields , extra_kwargs ):
13771394 """
13781395 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
14011418
14021419 unique_constraint_names -= {None }
14031420
1404- # Include each of the `unique_together` field names,
1421+ # Include each of the `unique_together` and `UniqueConstraint` field names,
14051422 # 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 )
14101426
14111427 # Now we have all the field names that have uniqueness constraints
14121428 # applied, we can add the extra 'required=...' or 'default=...'
@@ -1503,11 +1519,6 @@ def get_unique_together_validators(self):
15031519 """
15041520 Determine a default set of validators for any unique_together constraints.
15051521 """
1506- model_class_inheritance_tree = (
1507- [self .Meta .model ] +
1508- list (self .Meta .model ._meta .parents )
1509- )
1510-
15111522 # The field names we're passing though here only include fields
15121523 # which may map onto a model field. Any dotted field name lookups
15131524 # cannot map to a field, and must be a traversal, so we're not
@@ -1533,34 +1544,33 @@ def get_unique_together_validators(self):
15331544 # Note that we make sure to check `unique_together` both on the
15341545 # base model class, but also on any parent classes.
15351546 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
15571551
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+ )
15621566 )
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 )
15641574 return validators
15651575
15661576 def get_unique_for_date_validators (self ):
0 commit comments