<gh_stars>0
import typing

from django.db.models import Model as DjangoModel
from django.db.models.query import QuerySet
from filters import BaseFilter

__all__ = [
    'Model',
]


class Model(BaseFilter):
    """
    Expects the value to correspond to a record in the database.
    """
    CODE_NOT_FOUND = 'not_found'
    CODE_NOT_UNIQUE = 'not_unique'

    templates = {
        CODE_NOT_FOUND:
            'This value does not match any {model} records.',

        CODE_NOT_UNIQUE:
            'This value matches too many {model} records.',
    }

    def __init__(
            self,
            model: typing.Type[DjangoModel],
            field: str = 'pk',
            **predicates: typing.Union[typing.Mapping, typing.Sequence]
    ) -> None:
        """
        :param model:
          The model that will be queried.

        :param field:
          The field that the incoming value will be matched against.

        :param predicates:
          Additional QuerySet methods to invoke to further limit/customize
          the filter result.

          Note: the result from calling each predicate MUST be a
          :py:class:`QuerySet`, or else an exception will be raised at
          runtime (e.g., ``count`` is not a valid predicate).

          IMPORTANT: If you specify a predicate that executes a write
          query (e.g., ``update`` or ``delete``), the filter will still
          raise an exception... but not before executing the query!

          Example::

             f.ext.Model(
               # Find a Post record with a ``slug`` that matches the input.
               model = Post,
               field = 'slug',

               # Predicates
               filter={'published': True},
               exclude={'comments__isnull': True'},
               select_related=('author', 'comments'),
             )
        """
        super(Model, self).__init__()

        self.model = model
        self.field = field
        self.predicates = predicates

    def __str__(self):
        return '{type}({model!r}, field={field!r})'.format(
            type=type(self).__name__,
            model=self.model,
            field=self.field,
        )

    def _apply(self, value):
        query_set = self.model.objects.filter(**{self.field: value})

        for name, args in self.predicates.items():
            predicate = getattr(query_set, name)

            query_set = (
                predicate(**args)
                if isinstance(args, typing.Mapping)
                else predicate(*args)
            )

            if not isinstance(query_set, QuerySet):
                raise ValueError(
                    'Invalid result from predicate "{predicate}" '
                    '(expected QuerySet, actual {actual}).'.format(
                        actual=type(query_set).__name__,
                        predicate=name,
                    ),
                )

        try:
            return query_set.get(**{self.field: value})
        except self.model.DoesNotExist:
            return self._invalid_value(
                exc_info=True,
                reason=self.CODE_NOT_FOUND,
                value=value,

                template_vars={
                    'field': self.field,
                    'model': self.model.__name__,
                },
            )
        except self.model.MultipleObjectsReturned:
            return self._invalid_value(
                value=value,
                reason=self.CODE_NOT_UNIQUE,
                exc_info=True,

                template_vars={
                    'field': self.field,
                    'model': self.model.__name__,
                },
            )

import typing

from django.db.models import Model as DjangoModel
from django.db.models.query import QuerySet
from filters import BaseFilter

__all__ = [
    'Model',
]


class Model(BaseFilter):
    """
    Expects the value to correspond to a record in the database.
    """
    CODE_NOT_FOUND = 'not_found'
    CODE_NOT_UNIQUE = 'not_unique'

    templates = {
        CODE_NOT_FOUND:
            'This value does not match any {model} records.',

        CODE_NOT_UNIQUE:
            'This value matches too many {model} records.',
    }

    def __init__(
            self,
            model: typing.Type[DjangoModel],
            field: str = 'pk',
            **predicates: typing.Union[typing.Mapping, typing.Sequence]
    ) -> None:
        """
        :param model:
          The model that will be queried.

        :param field:
          The field that the incoming value will be matched against.

        :param predicates:
          Additional QuerySet methods to invoke to further limit/customize
          the filter result.

          Note: the result from calling each predicate MUST be a
          :py:class:`QuerySet`, or else an exception will be raised at
          runtime (e.g., ``count`` is not a valid predicate).

          IMPORTANT: If you specify a predicate that executes a write
          query (e.g., ``update`` or ``delete``), the filter will still
          raise an exception... but not before executing the query!

          Example::

             f.ext.Model(
               # Find a Post record with a ``slug`` that matches the input.
               model = Post,
               field = 'slug',

               # Predicates
               filter={'published': True},
               exclude={'comments__isnull': True'},
               select_related=('author', 'comments'),
             )
        """
        super(Model, self).__init__()

        self.model = model
        self.field = field
        self.predicates = predicates

    def __str__(self):
        return '{type}({model!r}, field={field!r})'.format(
            type=type(self).__name__,
            model=self.model,
            field=self.field,
        )

    def _apply(self, value):
        query_set = self.model.objects.filter(**{self.field: value})

        for name, args in self.predicates.items():
            predicate = getattr(query_set, name)

            query_set = (
                predicate(**args)
                if isinstance(args, typing.Mapping)
                else predicate(*args)
            )

            if not isinstance(query_set, QuerySet):
                raise ValueError(
                    'Invalid result from predicate "{predicate}" '
                    '(expected QuerySet, actual {actual}).'.format(
                        actual=type(query_set).__name__,
                        predicate=name,
                    ),
                )

        try:
            return query_set.get(**{self.field: value})
        except self.model.DoesNotExist:
            return self._invalid_value(
                exc_info=True,
                reason=self.CODE_NOT_FOUND,
                value=value,

                template_vars={
                    'field': self.field,
                    'model': self.model.__name__,
                },
            )
        except self.model.MultipleObjectsReturned:
            return self._invalid_value(
                value=value,
                reason=self.CODE_NOT_UNIQUE,
                exc_info=True,

                template_vars={
                    'field': self.field,
                    'model': self.model.__name__,
                },
            )
Expects the value to correspond to a record in the database. :param model:           The model that will be queried.          :param field:           The field that the incoming value will be matched against.          :param predicates:           Additional QuerySet methods to invoke to further limit/customize           the filter result.            Note: the result from calling each predicate MUST be a           :py:class:`QuerySet`, or else an exception will be raised at           runtime (e.g., ``count`` is not a valid predicate).            IMPORTANT: If you specify a predicate that executes a write           query (e.g., ``update`` or ``delete``), the filter will still           raise an exception... but not before executing the query!            Example::               f.ext.Model(                # Find a Post record with a ``slug`` that matches the input.                model = Post,                field = 'slug',                 # Predicates                filter={'published': True},                exclude={'comments__isnull': True'},                select_related=('author', 'comments'),              )