Пример #1
0
    def _make_query(self):
        table = Table(self.model._meta.table)
        self.query = self._db.query_class.update(table)
        self.resolve_filters(
            model=self.model,
            q_objects=self.q_objects,
            annotations=self.annotations,
            custom_filters=self.custom_filters,
        )

        for key, value in self.update_kwargs.items():
            field_object = self.model._meta.fields_map.get(key)
            if not field_object:
                raise FieldError(
                    "Unknown keyword argument {} for model {}".format(
                        key, self.model))
            if field_object.generated:
                raise IntegrityError(
                    "Field {} is generated and can not be updated")
            if isinstance(field_object, fields.ForeignKeyField):
                db_field = "{}_id".format(key)
                value = value.id
            else:
                db_field = self.model._meta.fields_db_projection[key]
            self.query = self.query.set(db_field, value)
Пример #2
0
    def order_by(self, *orderings: str) -> "QuerySet[MODEL]":
        """
        Accept args to filter by in format like this:

        .. code-block:: python3

            .order_by('name', '-tournament__name')

        Supports ordering by related models too.

        :raises FieldError: If unknown field has been provided.
        """
        queryset = self._clone()
        new_ordering = []
        for ordering in orderings:
            field_name, order_type = self._resolve_ordering_string(ordering)

            if not (field_name.split("__")[0] in self.model._meta.fields
                    or field_name in self._annotations):
                raise FieldError(
                    f"Unknown field {field_name} for model {self.model.__name__}"
                )
            new_ordering.append((field_name, order_type))
        queryset._orderings = new_ordering
        return queryset
Пример #3
0
    def add_field_to_select_query(self, field, return_as) -> None:
        table = Table(self.model._meta.table)
        if field in self.model._meta.fields_db_projection:
            db_field = self.model._meta.fields_db_projection[field]
            self.query = self.query.select(
                getattr(table, db_field).as_(return_as))
            return

        if field in self.model._meta.fetch_fields:
            raise ValueError(
                'Selecting relation "{}" is not possible, select concrete field on related model'
                .format(field))

        field_split = field.split('__')
        if field_split[0] in self.model._meta.fetch_fields:
            related_table, related_db_field = self._join_table_with_forwarded_fields(
                model=self.model,
                field=field_split[0],
                forwarded_fields='__'.join(field_split[1:]),
            )
            self.query = self.query.select(
                getattr(related_table, related_db_field).as_(return_as))
            return

        raise FieldError('Unknown field "{}" for model "{}"'.format(
            field,
            self.model.__name__,
        ))
Пример #4
0
 def _get_actual_filter_params(self, model, key, value) -> Tuple[str, Any]:
     if key in model._meta.fk_fields or key in model._meta.o2o_fields:
         field_object = model._meta.fields_map[key]
         if hasattr(value, "pk"):
             filter_value = value.pk
         else:
             filter_value = value
         filter_key = field_object.source_field
     elif key in model._meta.m2m_fields:
         filter_key = key
         if hasattr(value, "pk"):
             filter_value = value.pk
         else:
             filter_value = value
     elif (key.split("__")[0] in model._meta.fetch_fields
           or key in self._custom_filters or key in model._meta.filters):
         filter_key = key
         filter_value = value
     else:
         allowed = sorted(
             list(model._meta.fields | model._meta.fetch_fields
                  | set(self._custom_filters)))
         raise FieldError(
             f"Unknown filter param '{key}'. Allowed base values are {allowed}"
         )
     return filter_key, filter_value
Пример #5
0
    def __init__(self, model, update_kwargs, db, q_objects, annotations,
                 custom_filters) -> None:
        super().__init__(model, db)
        table = Table(model._meta.table)
        self.query = self._db.query_class.update(table)
        self.resolve_filters(model=model,
                             q_objects=q_objects,
                             annotations=annotations,
                             custom_filters=custom_filters)

        for key, value in update_kwargs.items():
            field_object = model._meta.fields_map.get(key)
            if not field_object:
                raise FieldError(
                    'Unknown keyword argument {} for model {}'.format(
                        key, model))
            if field_object.generated:
                raise IntegrityError(
                    'Field {} is generated and can not be updated')
            if isinstance(field_object, fields.ForeignKeyField):
                db_field = '{}_id'.format(key)
                value = value.id
            else:
                db_field = model._meta.fields_db_projection[key]
            self.query = self.query.set(db_field, value)
Пример #6
0
    def filter(self, *args, **kwargs) -> 'QuerySet':
        """
        Filters QuerySet by given kwargs. You can filter by related objects like this:

        .. code-block:: python3

            Team.filter(events__tournament__name='Test')

        You can also pass Q objects to filters as args.
        """
        queryset = self._clone()
        for arg in args:
            if not isinstance(arg, Q):
                raise TypeError('expected Q objects as args')
            queryset._q_objects_for_resolve.append(arg)
        for key, value in kwargs.items():
            if key in queryset.model._meta.filters:
                queryset._filter_kwargs[key] = value
            elif key in self.model._meta.fk_fields:
                field_object = self.model._meta.fields_map[key]
                queryset._filter_kwargs[field_object.source_field] = value.id
            elif key.split('__')[0] in self.model._meta.fetch_fields:
                queryset._q_objects_for_resolve.append(Q(**{key: value}))
            elif key in self._available_custom_filters:
                queryset._having[key] = value
            else:
                allowed = sorted(list(self.model._meta.fields | self.model._meta.fetch_fields
                                      | set(self._available_custom_filters)))
                raise FieldError("Unknown filter param '{}'. Allowed base values are {}".format(
                    key, allowed))
        return queryset
Пример #7
0
    def order_by(self, *orderings: str) -> "QuerySet[MODEL]":
        """
        Accept args to filter by in format like this:

        .. code-block:: python3

            .order_by('name', '-tournament__name')

        Supports ordering by related models too.
        """
        queryset = self._clone()
        new_ordering = []
        for ordering in orderings:
            order_type = Order.asc
            if ordering[0] == "-":
                field_name = ordering[1:]
                order_type = Order.desc
            else:
                field_name = ordering

            if not (field_name.split("__")[0] in self.model._meta.fields
                    or field_name in self._annotations):
                raise FieldError(
                    f"Unknown field {field_name} for model {self.model.__name__}"
                )
            new_ordering.append((field_name, order_type))
        queryset._orderings = new_ordering
        return queryset
Пример #8
0
    def resolve_to_python_value(self, model: Type[MODEL],
                                field: str) -> Callable:
        if field in model._meta.fetch_fields:
            # return as is to get whole model objects
            return lambda x: x

        if field in (x[1] for x in model._meta.db_native_fields):
            return lambda x: x

        if field in self.annotations:
            annotation = self.annotations[field]
            field_object = getattr(annotation, "field_object", None)
            if field_object:
                return field_object.to_python_value
            return lambda x: x

        if field in model._meta.fields_map:
            return model._meta.fields_map[field].to_python_value

        field_split = field.split("__")
        if field_split[0] in model._meta.fetch_fields:
            new_model = model._meta.fields_map[
                field_split[0]].related_model  # type: ignore
            return self.resolve_to_python_value(new_model,
                                                "__".join(field_split[1:]))

        raise FieldError(f'Unknown field "{field}" for model "{model}"')
Пример #9
0
    def add_field_to_select_query(self, field, return_as) -> None:
        table = self.model._meta.basetable
        if field in self.model._meta.fields_db_projection:
            db_field = self.model._meta.fields_db_projection[field]
            self.query._select_field(getattr(table, db_field).as_(return_as))
            return

        if field in self.model._meta.fetch_fields:
            raise ValueError('Selecting relation "{}" is not possible, select '
                             "concrete field on related model".format(field))

        if field in self.annotations:
            annotation = self.annotations[field]
            annotation_info = annotation.resolve(self.model)
            self.query._select_other(annotation_info["field"].as_(return_as))
            return

        field_split = field.split("__")
        if field_split[0] in self.model._meta.fetch_fields:
            related_table, related_db_field = self._join_table_with_forwarded_fields(
                model=self.model,
                field=field_split[0],
                forwarded_fields="__".join(field_split[1:]))
            self.query._select_field(
                getattr(related_table, related_db_field).as_(return_as))
            return

        raise FieldError(
            f'Unknown field "{field}" for model "{self.model.__name__}"')
Пример #10
0
    def resolve_to_python_value(self, model: "Type[Model]",
                                field: str) -> Callable:
        if field in model._meta.fetch_fields:
            # return as is to get whole model objects
            return lambda x: x

        if field in [x[1] for x in model._meta.db_native_fields]:
            return lambda x: x

        if field in self.annotations:
            field_object = self.annotations[field].field_object
            if field_object:
                return field_object.to_python_value
            return lambda x: x

        if field in model._meta.fields_map:
            return model._meta.fields_map[field].to_python_value

        field_split = field.split("__")
        if field_split[0] in model._meta.fetch_fields:
            new_model = model._meta.fields_map[
                field_split[0]].model_class  # type: ignore
            return self.resolve_to_python_value(new_model,
                                                "__".join(field_split[1:]))

        raise FieldError(f'Unknown field "{field}" for model "{model}"')
Пример #11
0
    def add_field_to_select_query(self, field: str, return_as: str) -> None:
        table = self.model._meta.basetable
        if field in self.model._meta.fields_db_projection:
            db_field = self.model._meta.fields_db_projection[field]
            self.query._select_field(table[db_field].as_(return_as))
            return

        if field in self.model._meta.fetch_fields:
            raise ValueError('Selecting relation "{}" is not possible, select '
                             "concrete field on related model".format(field))

        if field in self.annotations:
            self._annotations[return_as] = self.annotations[field]
            return

        field_split = field.split("__")
        if field_split[0] in self.model._meta.fetch_fields:
            related_table, related_db_field = self._join_table_with_forwarded_fields(
                model=self.model,
                table=table,
                field=field_split[0],
                forwarded_fields="__".join(field_split[1:]),
            )
            self.query._select_field(
                related_table[related_db_field].as_(return_as))
            return

        raise FieldError(
            f'Unknown field "{field}" for model "{self.model.__name__}"')
Пример #12
0
    def prefetch_related(self, *args: str) -> 'QuerySet':
        """
        Like ``.fetch_related()`` on instance, but works on all objects in QuerySet.
        """
        queryset = self._clone()
        queryset._prefetch_map = {}

        for relation in args:
            if isinstance(relation, Prefetch):
                relation.resolve_for_queryset(queryset)
                continue
            relation_split = relation.split('__')
            first_level_field = relation_split[0]
            if first_level_field not in self.model._meta.fetch_fields:
                raise FieldError(
                    'relation {} for {} not found'.format(
                        first_level_field, self.model._meta.table
                    )
                )
            if first_level_field not in queryset._prefetch_map.keys():
                queryset._prefetch_map[first_level_field] = set()
            forwarded_prefetch = '__'.join(relation_split[1:])
            if forwarded_prefetch:
                queryset._prefetch_map[first_level_field].add(forwarded_prefetch)
        return queryset
Пример #13
0
    def resolver_arithmetic_expression(
        cls, model: "Type[Model]", arithmetic_expression_or_field: Term,
    ) -> Tuple[Term, Optional[Field]]:
        field_object = None

        if isinstance(arithmetic_expression_or_field, Field):
            name = arithmetic_expression_or_field.name
            try:
                arithmetic_expression_or_field.name = model._meta.fields_db_projection[name]

                field_object = model._meta.fields_map.get(name, None)
                if field_object:
                    func = field_object.get_for_dialect(
                        model._meta.db.capabilities.dialect, "function_cast"
                    )
                    if func:
                        arithmetic_expression_or_field = func(
                            field_object, arithmetic_expression_or_field
                        )
            except KeyError:
                raise FieldError(f"There is no non-virtual field {name} on Model {model.__name__}")
        elif isinstance(arithmetic_expression_or_field, ArithmeticExpression):
            left = arithmetic_expression_or_field.left
            right = arithmetic_expression_or_field.right
            (
                arithmetic_expression_or_field.left,
                left_field_object,
            ) = cls.resolver_arithmetic_expression(model, left)
            if left_field_object:
                if field_object and type(field_object) != type(left_field_object):
                    raise FieldError(
                        "Cannot use arithmetic expression between different field type"
                    )
                field_object = left_field_object

            (
                arithmetic_expression_or_field.right,
                right_field_object,
            ) = cls.resolver_arithmetic_expression(model, right)
            if right_field_object:
                if field_object and type(field_object) != type(right_field_object):
                    raise FieldError(
                        "Cannot use arithmetic expression between different field type"
                    )
                field_object = right_field_object

        return arithmetic_expression_or_field, field_object
Пример #14
0
 def to_python_value(
     self, value: Optional[Union[str, dict,
                                 list]]) -> Optional[Union[dict, list]]:
     if isinstance(value, str):
         try:
             return self.decoder(value)
         except Exception:
             raise FieldError(f"Value {value} is invalid json value.")
     return value
Пример #15
0
 def to_db_value(self, value: Optional[Union[dict, list, str]],
                 instance: "Union[Type[Model], Model]") -> Optional[str]:
     if isinstance(value, str):
         try:
             self.decoder(value)
         except Exception:
             raise FieldError(f"Value {value} is invalid json value.")
         return value
     return None if value is None else self.encoder(value)
Пример #16
0
    def values(self, *args: str, **kwargs: str) -> "ValuesQuery":
        """
        Make QuerySet return dicts instead of objects.

        Can pass names of fields to fetch, or as a ``field_name='name_in_dict'`` kwarg.

        If no arguments are passed it will default to a dict containing all fields.

        :raises FieldError: If duplicate key has been provided.
        """
        if args or kwargs:
            fields_for_select: Dict[str, str] = {}
            for field in args:
                if field in fields_for_select:
                    raise FieldError(f"Duplicate key {field}")
                fields_for_select[field] = field

            for return_as, field in kwargs.items():
                if return_as in fields_for_select:
                    raise FieldError(f"Duplicate key {return_as}")
                fields_for_select[return_as] = field
        else:
            _fields = [
                field
                for field in self.model._meta.fields_map.keys()
                if field in self.model._meta.db_fields
            ] + list(self._annotations.keys())

            fields_for_select = {field: field for field in _fields}

        return ValuesQuery(
            db=self._db,
            model=self.model,
            q_objects=self._q_objects,
            fields_for_select=fields_for_select,
            distinct=self._distinct,
            limit=self._limit,
            offset=self._offset,
            orderings=self._orderings,
            annotations=self._annotations,
            custom_filters=self._custom_filters,
            group_bys=self._group_bys,
        )
Пример #17
0
    def resolve_term(self, term: PyPikaTerm, queryset: "AwaitableStatement[MODEL]",
        accept_relation: bool) -> Tuple[Optional[Field], PyPikaTerm]:

        if isinstance(term, ArithmeticExpression):
            pypika_term = copy(term)
            field_left, pypika_term.left = self.resolve_term(term.left, queryset, accept_relation)
            field_right, pypika_term.right = self.resolve_term(term.right, queryset, accept_relation)
            field = field_left or field_right

            return field, pypika_term

        if isinstance(term, PyPikaFunction):
            #
            # There are two options, either resolve all function args, like below,
            # in this case either all the string params are expected to be references
            # to model fields, and hence something like `Coalesce("desc", "demo")`
            # will raise FieldError if `demo` is not a model field. Now a reasonable solution
            # might be to allow unresolvable strings as is, without raising exceptions,
            # but that also has other undesired implication.
            #
            # term_new_args = []
            # field = None
            #
            # for arg in term.args:
            #     term_field, term_arg = resolve_term(arg, queryset, context)
            #     term_new_args.append(term_arg)
            #     field = field or term_field
            #
            # term.args = term_new_args
            # return field, term
            #
            # Another solution is allow on the the first parameter of the function to be
            # a field reference as we do here:
            #

            pypika_term = copy(term)
            field = None
            if len(term.args) > 0:
                pypika_term.args = copy(term.args)
                field, pypika_term.args[0] = self.resolve_term(term.args[0], queryset, accept_relation)

            return field, pypika_term

        elif isinstance(term, Negative):
            pypika_term = copy(term)
            field, pypika_term.term = self.resolve_term(term.term, queryset, accept_relation)
            return field, pypika_term

        elif isinstance(term, ValueWrapper):
            if isinstance(term.value, str):
                return self.resolve_field_name(term.value, queryset, accept_relation)

            return None, term

        raise FieldError(f"Unresolvable term: {term}")
Пример #18
0
    def _make_query(self) -> None:
        table = self.model._meta.basetable
        self.query = self._db.query_class.update(table)
        self.resolve_filters(
            model=self.model,
            q_objects=self.q_objects,
            annotations=self.annotations,
            custom_filters=self.custom_filters,
        )
        # Need to get executor to get correct column_map
        executor = self._db.executor_class(model=self.model, db=self._db)

        for key, value in self.update_kwargs.items():
            field_object = self.model._meta.fields_map.get(key)
            if not field_object:
                raise FieldError(
                    f"Unknown keyword argument {key} for model {self.model}")
            if field_object.pk:
                raise IntegrityError(
                    f"Field {key} is PK and can not be updated")
            if isinstance(field_object,
                          (ForeignKeyFieldInstance, OneToOneFieldInstance)):
                fk_field: str = field_object.source_field  # type: ignore
                db_field = self.model._meta.fields_map[fk_field].source_field
                value = executor.column_map[fk_field](getattr(
                    value, field_object.to_field_instance.model_field_name),
                                                      None)
            else:
                try:
                    db_field = self.model._meta.fields_db_projection[key]
                except KeyError:
                    raise FieldError(
                        f"Field {key} is virtual and can not be updated")
                if isinstance(value, Term):
                    value = F.resolver_arithmetic_expression(
                        self.model, value)[0]
                elif isinstance(value, Function):
                    value = value.resolve(self.model, table)["field"]
                else:
                    value = executor.column_map[key](value, None)

            self.query = self.query.set(db_field, value)
Пример #19
0
    def to_db_value(
        self,
        value: BaseGeometry,
        instance: Union[Type[Model], Model],
    ) -> str:
        if value is None:
            return value

        if not isinstance(value, BaseGeometry):
            raise FieldError("The value to be saved must be a Shapely geometry.")

        return shapely.wkb.dumps(value, hex=True, srid=self.srid)
Пример #20
0
    def to_python_value(
        self, value: Optional[Union[str, bytes, dict, list]]
    ) -> Optional[Union[dict, list]]:
        if isinstance(value, (str, bytes)):
            try:
                return self.decoder(value)
            except Exception:
                raise FieldError(
                    f"Value {value if isinstance(value,str) else value.decode()} is invalid json value."
                )

        self.validate(value)
        return value
Пример #21
0
    def resolve_ordering(self, model, table, orderings, annotations) -> None:
        for ordering in orderings:
            field_name = ordering[0]
            if field_name in model._meta.fetch_fields:
                raise FieldError(
                    "Filtering by relation is not possible. Filter by nested field of related model"
                )
            if field_name.split("__")[0] in model._meta.fetch_fields:
                related_field_name = field_name.split("__")[0]
                related_field = model._meta.fields_map[related_field_name]
                related_table = self._join_table_by_field(
                    table, related_field_name, related_field)
                self.resolve_ordering(
                    related_field.model_class,
                    related_table,
                    [("__".join(field_name.split("__")[1:]), ordering[1])],
                    {},
                )
            elif field_name in annotations:
                annotation = annotations[field_name]
                annotation_info = annotation.resolve(self.model, table)
                self.query = self.query.orderby(annotation_info["field"],
                                                order=ordering[1])
            else:
                field_object = self.model._meta.fields_map.get(field_name)
                if not field_object:
                    raise FieldError(
                        f"Unknown field {field_name} for model {self.model.__name__}"
                    )
                field_name = field_object.source_field or field_name
                field = getattr(table, field_name)

                func = field_object.get_for_dialect(
                    model._meta.db.capabilities.dialect, "function_cast")
                if func:
                    field = func(field_object, field)

                self.query = self.query.orderby(field, order=ordering[1])
Пример #22
0
    def values(self, *args: str, **kwargs: str) -> ValuesQuery:
        """
        Make QuerySet return dicts instead of objects.

        Can pass names of fields to fetch, or as a ``field_name='name_in_dict'`` kwarg.

        If no arguments are passed it will default to a dict containing all fields.
        """
        if args or kwargs:
            fields_for_select: Dict[str, str] = {}
            for field in args:
                if field in fields_for_select:
                    raise FieldError(f"Duplicate key {field}")
                fields_for_select[field] = field

            for return_as, field in kwargs.items():
                if return_as in fields_for_select:
                    raise FieldError(f"Duplicate key {return_as}")
                fields_for_select[return_as] = field

        else:
            fields_for_select = {
                field_name: field_name
                for field_name, field_object in self.model._meta.fields_map.items()
                if field_object.has_db_column
            }

        return ValuesQuery(
            db=self._db,
            model=self.model,
            q_objects=self.q_objects,
            fields_for_select=fields_for_select,
            distinct=self._distinct,
            limit=self._limit,
            offset=self._offset,
            orderings=self._orderings,
            annotations=self.annotations,
        )
Пример #23
0
    def resolve_to_python_value(self, model, field):
        if field in model._meta.fetch_fields:
            # return as is to get whole model objects
            return lambda x: x

        if field in model._meta.fields_map:
            return model._meta.fields_map[field].to_python_value

        field_split = field.split("__")
        if field_split[0] in model._meta.fetch_fields:
            new_model = model._meta.fields_map[field_split[0]].type
            return self.resolve_to_python_value(new_model, "__".join(field_split[1:]))

        raise FieldError('Unknown field "{}" for model "{}"'.format(field, model))
Пример #24
0
    def _get_actual_key(self, queryset: "AwaitableStatement[MODEL]", model: Type["Model"], key: str) -> str:
        if key in model._meta.fields_map:
            field = model._meta.fields_map[key]
            if isinstance(field, (ForeignKey, OneToOneField)):
                return field.id_field_name

            return key

        (field_name, sep, comparision) = key.partition(LOOKUP_SEP)
        if field_name == "pk":
            return f"{model._meta.pk_attr}{sep}{comparision}"

        if field_name in model._meta.fields_map or field_name in queryset.annotations:
            return key

        allowed = sorted(list(model._meta.fields_map.keys() | queryset.annotations.keys()))
        raise FieldError(f"Unknown filter param '{key}'. Allowed base values are {allowed}")
Пример #25
0
    def get_actual_field_name(model, annotations, field_name: str):
        if field_name in model._meta.fields_map:
            field = model._meta.fields_map[field_name]
            if isinstance(field, (ForeignKey, OneToOneField)):
                return field.id_field_name

            return field_name

        if field_name == "pk":
            return model._meta.pk_attr

        if field_name in annotations:
            return field_name

        allowed = sorted(
            list(model._meta.fields_map.keys() | annotations.keys()))
        raise FieldError(
            f"Unknown field name '{field_name}'. Allowed base values are {allowed}"
        )
Пример #26
0
    def to_python_value(self, value: Optional[Union[str,
                                                    dict]]) -> Optional[Dict]:
        if isinstance(value, str):
            try:
                value = self.decoder(value)
            except Exception:
                raise FieldError(f"Value {value} is invalid json value.")

        if not value:
            return {k: Score(calculator_name=k) for k in _AVAILABLE}

        output_dict: Dict[str, Score] = {}
        for k in value.keys():
            if isinstance(value[k], Score):
                output_dict[k] = value[k]
            else:
                if isinstance(value[k], str):
                    value[k] = json.loads(value[k])
                output_dict[k] = Score(**value[k])
        return output_dict
Пример #27
0
 def _get_actual_filter_params(self, model, key, value) -> Tuple[str, Any]:
     if key in model._meta.fk_fields:
         field_object = model._meta.fields_map[key]
         if hasattr(value, 'id'):
             filter_value = value.id
         else:
             filter_value = value
         filter_key = field_object.source_field
     elif (key.split('__')[0] in model._meta.fetch_fields
           or key in self._custom_filters or key in model._meta.filters):
         filter_key = key
         filter_value = value
     else:
         allowed = sorted(
             list(model._meta.fields
                  | model._meta.fetch_fields
                  | set(self._custom_filters)))
         raise FieldError(
             "Unknown filter param '{}'. Allowed base values are {}".format(
                 key, allowed))
     return filter_key, filter_value
Пример #28
0
    def resolve_field_name(
        self,
        field_name,
        queryset: "AwaitableStatement[MODEL]",
        accept_relation: bool,
        check_annotations=True,
        expand_annotation=True) -> Tuple[Optional[Field], PyPikaField]:

        #
        # When expand_annotation is False, we need to make sure the annotation
        # will show up (will be expanded) in the final query, since we are just
        # referring to it here.
        #

        if check_annotations and field_name in queryset.annotations:
            if expand_annotation:
                return None, queryset.annotations[field_name].field
            else:
                return None, PyPikaField(field_name)

        model = self.top.model
        table = self.top.table

        if field_name == "pk":
            field_name = model._meta.pk_attr

        relation_field_name, _, field_sub = field_name.partition(LOOKUP_SEP)
        relation_field = model._meta.fields_map.get(relation_field_name)
        if not relation_field:
            raise UnknownFieldError(relation_field_name, model)

        if isinstance(relation_field, RelationField):
            if field_sub:
                join_data = self.join_table_by_field(table, relation_field)

                self.push(join_data.model, join_data.table)
                (field_object, pypika_field) = self.resolve_field_name(
                    field_sub, queryset, accept_relation, check_annotations=False)

                self.pop()
                return field_object, pypika_field

            elif accept_relation:
                join_data = self.join_table_by_field(table, relation_field, full=False)
                if join_data:
                    return join_data.field_object, join_data.pypika_field

                else:
                    # this can happen only when relation_field is instance of ForeignKey or OneToOneField
                    field_object = model._meta.fields_map[relation_field.id_field_name]
                    pypika_field = table[field_object.db_column]
                    return field_object, pypika_field

            else:
                raise FieldError("{} is a relation. Try a nested field of the related model".format(relation_field_name))

        else:
            if field_sub:
                if isinstance(relation_field, JSONField):
                    path = "{{{}}}".format(field_sub.replace(LOOKUP_SEP, ','))
                    return None, table[relation_field.db_column].get_path_json_value(path)

                raise NotARelationFieldError(relation_field_name, model)

            field_object = relation_field
            pypika_field = table[field_object.db_column]
            func = field_object.get_for_dialect("function_cast")
            if func:
                pypika_field = func(pypika_field)

            return field_object, pypika_field
Пример #29
0
    def resolve_ordering(
        self,
        model: "Type[Model]",
        table: Table,
        orderings: Iterable[Tuple[str, str]],
        annotations: Dict[str, Any],
    ) -> None:
        """
        Applies standard ordering to QuerySet.

        :param model: The Model this queryset is based on.
        :param table: ``pypika.Table`` to keep track of the virtual SQL table
            (to allow self referential joins)
        :param orderings: What columns/order to order by
        :param annotations:  Annotations that may be ordered on

        :raises FieldError: If a field provided does not exist in model.
        """
        # Do not apply default ordering for annotated queries to not mess them up
        if not orderings and self.model._meta.ordering and not annotations:
            orderings = self.model._meta.ordering

        for ordering in orderings:
            field_name = ordering[0]
            if field_name in model._meta.fetch_fields:
                raise FieldError(
                    "Filtering by relation is not possible. Filter by nested field of related model"
                )
            if field_name.split("__")[0] in model._meta.fetch_fields:
                related_field_name = field_name.split("__")[0]
                related_field = cast(
                    RelationalField,
                    model._meta.fields_map[related_field_name])
                related_table = self._join_table_by_field(
                    table, related_field_name, related_field)
                self.resolve_ordering(
                    related_field.related_model,
                    related_table,
                    [("__".join(field_name.split("__")[1:]), ordering[1])],
                    {},
                )
            elif field_name in annotations:
                annotation = annotations[field_name]
                if isinstance(annotation, Term):
                    self.query = self.query.orderby(annotation,
                                                    order=ordering[1])
                else:
                    annotation_info = annotation.resolve(self.model, table)
                    self.query = self.query.orderby(annotation_info["field"],
                                                    order=ordering[1])
            else:
                field_object = model._meta.fields_map.get(field_name)

                if not field_object:
                    raise FieldError(
                        f"Unknown field {field_name} for model {model.__name__}"
                    )
                field_name = field_object.source_field or field_name
                field = table[field_name]

                func = field_object.get_for_dialect(
                    model._meta.db.capabilities.dialect, "function_cast")
                if func:
                    field = func(field_object, field)

                self.query = self.query.orderby(field, order=ordering[1])