Skip to main content

Django RQL Filtering

Project description

Django RQL

pyversions PyPi Status Docs codecov Build Status PyPI status Quality Gate Status

django-rql is an Django application, that implements RQL filter backend for your web application.

RQL

RQL (Resource query language) is designed for modern application development. It is built for the web, ready for NoSQL, and highly extensible with simple syntax. This is a query language fast and convenient database interaction. RQL was designed for use in URLs to request object-style data structures.

RQL Reference

RQL for Web

Notes

Parsing is done with Lark (cheatsheet). The current parsing algorithm is LALR(1) with standard lexer.

Currently supported operators

  1. Comparison (eq, ne, gt, ge, lt, le, like, ilike, search)
  2. List (in, out)
  3. Logical (and, or, not)
  4. Constants (null(), empty())
  5. Ordering (ordering)
  6. Select (select)

Documentation

Full documentation is available at https://django-rql.readthedocs.org.

Example

from dj_rql.constants import FilterLookups
from dj_rql.filter_cls import RQLFilterClass, RQL_NULL


class ModelFilterClass(RQLFilterClass):
    """
    MODEL - Django ORM model
    FILTERS - List of filters
    EXTENDED_SEARCH_ORM_ROUTES - List of additional Django ORM fields for search
    DISTINCT - Boolean flag, that specifies if queryset must always be DISTINCT
    SELECT - Boolean flag, that specifies if Filter Class supports select operations and queryset optimizations
    OPENAPI_SPECIFICATION - Python class that renders OpenAPI specification

    Filters can be set in two ways:
        1) string (default settings are calculated from ORM)
        2) dict (overriding settings for specific cases)

    Filter Dict Structure
    {
        'filter': str
        # or
        'namespace': str

        'source': str
        # or
        'sources': iterable
        # or
        'custom': bool
        # or
        'dynamic': bool
        'field': obj

        'lookups': set

        'qs': obj

        'use_repr': bool  # can't be used in namespaces
        'ordering': bool  # can't be true if 'use_repr=True'
        'search': bool    # can't be true if 'use_repr=True'
        'hidden': bool
    }

    """
    MODEL = Model
    FILTERS = ['id', {
        # `null_values` can be set to override ORM is_null behaviour
        # RQL_NULL is the default value if NULL lookup is supported by field
        'filter': 'title',
        'null_values': {RQL_NULL, 'NULL_ID'},
        'ordering': False,
    }, {
        # `ordering` can be set to True, if filter must support ordering (sorting)
        # `ordering` can't be applied to non-db fields
        'filter': 'status',
        'ordering': True,
    }, {
        # `search` must be set to True for filter to be used in searching
        # `search` must be applied only to text db-fields, which have ilike lookup
        'filter': 'author__email',
        'search': True,
    }, {
        # `source` must be set when filter name doesn't match ORM path
        'filter': 'name',
        'source': 'author__name',
    }, {
        # `namespace` is useful for API consistency, when dealing with related models
        'namespace': 'author',
        'filters': ['id', 'name'],  # will be converted to `author.id` and `author.name`
    },{
        # `distinct` needs to be setup for filters that require QS to work in DISTINCT mode
        # `openapi` configuration is automatically collected by OpenAPI autogenerator
        'filter': 'published.at',
        'source': 'published_at',
        'distinct': True,
        'openapi': {
            'required': True,
            'deprecated': True,
            'description': 'Good description',
            'hidden': False,  # can be set to avoid collecting by autogenerator
            # type and format are collected automatically and shouldn't be setup, in general
            'type': 'string',
            'format': 'date',
        },
    }, {
        # `use_repr` flag is used to filter by choice representations
        'filter': 'rating.blog',
        'source': 'blog_rating',
        'use_repr': True,
    }, {
        # `hidden` flag is used to set default select behaviour for associated field
        'filter': 'rating.blog_int',
        'source': 'blog_rating',
        'use_repr': False,
        'ordering': True,
        'hidden': True,
    }, {
        # We can change default lookups for a certain filter
        'filter': 'amazon_rating',
        'lookups': {FilterLookups.GE, FilterLookups.LT},
    }, {
        # Sometimes it's needed to filter by several sources at once (distinct is always True).
        # F.e. this could be helpful for searching.
        'filter': 'd_id',
        'sources': {'id', 'author__id'},
        'ordering': True,
    }, {
        # Some fields may have no DB representation or non-typical ORM filtering
        # `custom` option must be set to True for such fields
        'filter': 'custom_filter',
        'custom': True,
        'lookups': {FilterLookups.EQ, FilterLookups.IN, FilterLookups.I_LIKE},
        'ordering': True,
        'search': True,
         # Optional ORM field for query parameter value validation
        'field': IntegerField(), 

        'custom_data': [1],
    }]


from dj_rql.drf.backend import  RQLFilterBackend
from dj_rql.drf.paginations import RQLContentRangeLimitOffsetPagination

class DRFViewSet(mixins.ListModelMixin, GenericViewSet):
    queryset = MODEL.objects.all()
    serializer_class = ModelSerializer
    rql_filter_class = ModelFilterClass
    pagination_class = RQLContentRangeLimitOffsetPagination
    filter_backends = (RQLFilterBackend,)

Notes

  1. Values with whitespaces or special characters, like ',' need to have "" or ''
  2. Supported date format is ISO8601: 2019-02-12
  3. Supported datetime format is ISO8601: 2019-02-12T10:02:00 / 2019-02-12T10:02Z / 2019-02-12T10:02:00+03:00
  4. Support for Choices() fields from Django Model Utilities is added

Helpers

There is a Django command generate_rql_class to decrease development and integration efforts for filtering. This command automatically generates a filter class for a given model with all relations and all optimizations (!) to the specified depth.

Example

django-admin generate_rql_class --settings=tests.dj_rf.settings tests.dj_rf.models.Publisher --depth=1 --exclude=authors,fk2

This command for the model Publisher from tests package will produce the following output to stdout:

from tests.dj_rf.models import Publisher

from dj_rql.filter_cls import RQLFilterClass
from dj_rql.qs import NSR


class PublisherFilters(RQLFilterClass):
    MODEL = Publisher
    SELECT = True
    EXCLUDE_FILTERS = ['authors', 'fk2']
    FILTERS = [
    {
        "filter": "id",
        "ordering": True,
        "search": False
    },
    {
        "filter": "name",
        "ordering": True,
        "search": True
    },
    {
        "namespace": "fk1",
        "filters": [
            {
                "filter": "id",
                "ordering": True,
                "search": False
            }
        ],
        "qs": NSR('fk1')
    }
]

Django Rest Framework Extensions

  1. Pagination (limit, offset)
  2. Support for custom fields, inherited at any depth from basic model fields, like CharField().
  3. Backend DjangoFiltersRQLFilterBackend with automatic conversion of Django-Filters query to RQL query.
  4. OpenAPI docs are autogenerated for filter classes.

Best Practices

  1. Use dj_rql.utils.assert_filter_cls to test your API view filters. If the mappings are correct and there is no custom filtering logic, then it's practically guaranteed, that filtering will work correctly.
  2. Prefer using custom=True with RQLFilterClass.build_q_for_custom_filter overriding over overriding RQLFilterClass.build_q_for_filter.
  3. Custom filters may support ordering (ordering=True) with build_name_for_custom_ordering.

Development

  1. Python 3.6+
  2. Install dependencies requirements/dev.txt and requirements/extra.txt

Testing

  1. Python 3.6+
  2. Install dependencies requirements/test.txt
  3. export PYTHONPATH=/your/path/to/django-rql/

Check code style: flake8 Run tests: pytest

Tests reports are generated in tests/reports.

  • out.xml - JUnit test results
  • coverage.xml - Coverage xml results

To generate HTML coverage reports use: --cov-report html:tests/reports/cov_html

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-rql-3.14.0.tar.gz (49.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_rql-3.14.0-py3-none-any.whl (75.1 kB view details)

Uploaded Python 3

File details

Details for the file django-rql-3.14.0.tar.gz.

File metadata

  • Download URL: django-rql-3.14.0.tar.gz
  • Upload date:
  • Size: 49.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.8.12

File hashes

Hashes for django-rql-3.14.0.tar.gz
Algorithm Hash digest
SHA256 4b2a100eccf8004f8ae04ce2b9b42e2416ca6f0528fd28714e239d63d63f5b3c
MD5 9ef316d363a9000a404c3f8aaaef77c5
BLAKE2b-256 bc1a976023e4bee49dc7510d78412d203d44d201a59b261f24d0be3790e28431

See more details on using hashes here.

File details

Details for the file django_rql-3.14.0-py3-none-any.whl.

File metadata

  • Download URL: django_rql-3.14.0-py3-none-any.whl
  • Upload date:
  • Size: 75.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.8.12

File hashes

Hashes for django_rql-3.14.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ac086c77d5ef752b4fb104a6dffceb1b0aa8ba746040a8579914ae8bf042b81e
MD5 56db587bed6ee55f60876454a53e096d
BLAKE2b-256 f3115ec3f828341c4b3ec2c079f50b930616db2dcadd9bc8f776d6a641988cfa

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page