Repository: django/django
File: django/db/models/base.py (Python)

Code before:
                    generated_field.expression.replace_expressions(replacements),
                    generated_field.output_field,
                )

        return field_map

    def prepare_database_save(self, field):
        if not self._is_pk_set():
            raise ValueError(
                "Unsaved model instance %r cannot be used in an ORM query." % self
            )
        return getattr(self, field.remote_field.get_related_field().attname)

    def clean(self):
        """
        Hook for doing any extra model-wide validation after clean() has been
        called on every field by self.clean_fields. Any ValidationError raised
        by this method will not be associated with a particular field; it will
        have a special-case association with the field defined by
        NON_FIELD_ERRORS.
        """
        pass

    def validate_unique(self, exclude=None):
        """
        Check unique constraints on the model and raise ValidationError if any
        failed.
        """
        unique_checks, date_checks = self._get_unique_checks(exclude=exclude)

        errors = self._perform_unique_checks(unique_checks)
        date_errors = self._perform_date_checks(date_checks)

        for k, v in date_errors.items():
            errors.setdefault(k, []).extend(v)

        if errors:
            raise ValidationError(errors)

    def _get_unique_checks(self, exclude=None, include_meta_constraints=False):
        """
        Return a list of checks to perform. Since validate_unique() could be
        called from a ModelForm, some fields may have been excluded; we can't
        perform a unique check on a model that is missing fields involved
        in that check. Fields that did not validate should also be excluded,
        but they need to be passed in via the exclude argument.
        """
        if exclude is None:
            exclude = set()
        unique_checks = []


Review comment (performance):
I think this introduces a performance regression: both `patron` and `patron_id` will be checked against the db even though they're backed by the same column. There might be a different solution making use of `field.local_related_fields`. 🤔 

Code after:
            order = "_order" if is_next else "-_order"
            order_field = self._meta.order_with_respect_to
            filter_args = order_field.get_filter_kwargs_for_object(self)
            obj = (
                self.__class__._default_manager.filter(**filter_args)
                .filter(
                    **{
                        "_order__%s"
                        % op: self.__class__._default_manager.values("_order").filter(
                            **{self._meta.pk.name: self.pk}
                        )
                    }
                )
                .order_by(order)[:1]
                .get()
            )
            setattr(self, cachename, obj)
        return getattr(self, cachename)

    def _get_field_expression_map(self, meta, exclude=None):
        if exclude is None:
            exclude = set()
        meta = meta or self._meta
        field_map = {}
        generated_fields = []
        for field in meta.local_fields:
            if field.name in exclude:
                continue
            if field.generated:
                if any(
                    ref[0] in exclude
                    for ref in self._get_expr_references(field.expression)
                ):
                    continue
                generated_fields.append(field)
                continue
            if (
                isinstance(field.remote_field, ForeignObjectRel)
                and field not in meta.local_concrete_fields
            ):
                value = tuple(
                    getattr(self, from_field) for from_field in field.from_fields
                )
                if len(value) == 1:
                    value = value[0]
            elif field.concrete:
                value = getattr(self, field.attname)
            else:
                continue
            if not value or not hasattr(value, "resolve_expression"):
                value = Value(value, field)