| 576 | return path, self.expressions, kwargs |
| 577 | |
| 578 | def validate(self, model, instance, exclude=None, using=DEFAULT_DB_ALIAS): |
| 579 | queryset = model._default_manager.using(using) |
| 580 | if self.fields: |
| 581 | lookup_kwargs = {} |
| 582 | generated_field_names = [] |
| 583 | for field_name in self.fields: |
| 584 | if exclude and field_name in exclude: |
| 585 | return |
| 586 | field = model._meta.get_field(field_name) |
| 587 | if field.generated: |
| 588 | if exclude and self._expression_refs_exclude( |
| 589 | model, field.expression, exclude |
| 590 | ): |
| 591 | return |
| 592 | generated_field_names.append(field.name) |
| 593 | else: |
| 594 | lookup_value = getattr(instance, field.attname) |
| 595 | if ( |
| 596 | self.nulls_distinct is not False |
| 597 | and lookup_value is None |
| 598 | or ( |
| 599 | lookup_value == "" |
| 600 | and connections[ |
| 601 | using |
| 602 | ].features.interprets_empty_strings_as_nulls |
| 603 | ) |
| 604 | ): |
| 605 | # A composite constraint containing NULL value cannot |
| 606 | # cause a violation since NULL != NULL in SQL. |
| 607 | return |
| 608 | lookup_kwargs[field.name] = lookup_value |
| 609 | lookup_args = [] |
| 610 | if generated_field_names: |
| 611 | field_expression_map = instance._get_field_expression_map( |
| 612 | meta=model._meta, exclude=exclude |
| 613 | ) |
| 614 | for field_name in generated_field_names: |
| 615 | expression = field_expression_map[field_name] |
| 616 | if self.nulls_distinct is False: |
| 617 | lhs = F(field_name) |
| 618 | condition = Q(Exact(lhs, expression)) | Q( |
| 619 | IsNull(lhs, True), IsNull(expression, True) |
| 620 | ) |
| 621 | lookup_args.append(condition) |
| 622 | else: |
| 623 | lookup_kwargs[field_name] = expression |
| 624 | queryset = queryset.filter(*lookup_args, **lookup_kwargs) |
| 625 | else: |
| 626 | # Ignore constraints with excluded fields. |
| 627 | if exclude and any( |
| 628 | self._expression_refs_exclude(model, expression, exclude) |
| 629 | for expression in self.expressions |
| 630 | ): |
| 631 | return |
| 632 | replacements = { |
| 633 | F(field): value |
| 634 | for field, value in instance._get_field_expression_map( |
| 635 | meta=model._meta, exclude=exclude |