{"id":48489546,"url":"https://github.com/cloudblue/django-rql","last_synced_at":"2026-04-07T11:04:28.294Z","repository":{"id":42664426,"uuid":"265935206","full_name":"cloudblue/django-rql","owner":"cloudblue","description":"Django RQL library","archived":false,"fork":false,"pushed_at":"2025-06-23T10:37:54.000Z","size":553,"stargazers_count":95,"open_issues_count":7,"forks_count":15,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-10-05T20:13:23.007Z","etag":null,"topics":["django","django-rest-framework","rest","rql"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cloudblue.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-21T19:32:53.000Z","updated_at":"2025-08-27T12:23:26.000Z","dependencies_parsed_at":"2023-02-13T20:46:43.718Z","dependency_job_id":"16ab70ef-f947-4e0a-8640-36f7336d2ccf","html_url":"https://github.com/cloudblue/django-rql","commit_stats":null,"previous_names":[],"tags_count":50,"template":false,"template_full_name":null,"purl":"pkg:github/cloudblue/django-rql","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudblue%2Fdjango-rql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudblue%2Fdjango-rql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudblue%2Fdjango-rql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudblue%2Fdjango-rql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cloudblue","download_url":"https://codeload.github.com/cloudblue/django-rql/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudblue%2Fdjango-rql/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31509946,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T03:10:19.677Z","status":"ssl_error","status_checked_at":"2026-04-07T03:10:13.982Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["django","django-rest-framework","rest","rql"],"created_at":"2026-04-07T11:04:26.476Z","updated_at":"2026-04-07T11:04:28.288Z","avatar_url":"https://github.com/cloudblue.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Django RQL\n==========\n[![pyversions](https://img.shields.io/pypi/pyversions/django-rql.svg)](https://pypi.org/project/django-rql/)\n[![PyPi Status](https://img.shields.io/pypi/v/django-rql.svg)](https://pypi.org/project/django-rql/)\n[![PyPI status](https://img.shields.io/pypi/status/django-rql.svg)](https://pypi.org/project/django-rql/)\n[![Docs](https://readthedocs.org/projects/django-rql/badge/?version=latest)](https://readthedocs.org/projects/django-rql) \n[![Build Status](https://github.com/cloudblue/django-rql/workflows/Build%20Django-RQL%20library/badge.svg)](https://github.com/cloudblue/django-rql/actions)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=django-rql\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=django-rql)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=django-rql\u0026metric=coverage)](https://sonarcloud.io/summary/new_code?id=django-rql)\n[![PyPI Downloads](https://img.shields.io/pypi/dm/django-rql)](https://pypi.org/project/django-rql/)\n\n`django-rql` is the Django app, that adds RQL filtering to your application.\nThis library is based on core [lib-rql](https://github.com/cloudblue/lib-rql) library.\n\n\nRQL\n---\n\nRQL (Resource query language) is designed for modern application development. It is built for the web, ready for NoSQL, and highly extensible with simple syntax.\nThis is a query language fast and convenient database interaction. RQL was designed for use in URLs to request object-style data structures.\n\n[RQL Reference](https://connect.cloudblue.com/community/api/rql/)\n\n\nCurrently supported operators\n=============================\n1. Comparison (eq, ne, gt, ge, lt, le, like, ilike, search)\n2. List (in, out)\n3. Logical (and, or, not)\n4. Constants (null(), empty())\n5. Ordering (ordering)\n6. Select (select)\n7. Tuple (t)\n\n\nDocumentation\n=============\n\nFull documentation is available at [https://django-rql.readthedocs.org](https://django-rql.readthedocs.org).\n\n\nExample\n=======\n\n```python\nfrom dj_rql.filter_cls import RQLFilterClass, RQL_NULL\n\nfrom py_rql.constants import FilterLookups\n\n\nclass ModelFilterClass(RQLFilterClass):\n    \"\"\"\n    MODEL - Django ORM model\n    FILTERS - List of filters\n    EXTENDED_SEARCH_ORM_ROUTES - List of additional Django ORM fields for search\n    DISTINCT - Boolean flag, that specifies if queryset must always be DISTINCT\n    SELECT - Boolean flag, that specifies if Filter Class supports select operations and queryset optimizations\n    OPENAPI_SPECIFICATION - Python class that renders OpenAPI specification\n    MAX_ORDERING_LENGTH_IN_QUERY - Integer max allowed number of provided ordering filters in query ordering expression\n    ALLOWED_ORDERING_PERMUTATIONS_IN_QUERY - Set of tuples of strings to specify a set of allowed ordering permutations\n\n    Filters can be set in two ways:\n        1) string (default settings are calculated from ORM)\n        2) dict (overriding settings for specific cases)\n\n    Filter Dict Structure\n    {\n        'filter': str\n        # or\n        'namespace': str\n\n        'source': str\n        # or\n        'sources': iterable\n        # or\n        'custom': bool\n        # or\n        'dynamic': bool\n        'field': obj\n\n        'lookups': set\n\n        'qs': obj\n\n        'use_repr': bool  # can't be used in namespaces\n        'ordering': bool  # can't be true if 'use_repr=True'\n        'search': bool    # can't be true if 'use_repr=True'\n        'hidden': bool\n    }\n\n    \"\"\"\n    MODEL = Model\n    FILTERS = ['id', {\n        # `null_values` can be set to override ORM is_null behaviour\n        # RQL_NULL is the default value if NULL lookup is supported by field\n        'filter': 'title',\n        'null_values': {RQL_NULL, 'NULL_ID'},\n        'ordering': False,\n    }, {\n        # `ordering` can be set to True, if filter must support ordering (sorting)\n        # `ordering` can't be applied to non-db fields\n        'filter': 'status',\n        'ordering': True,\n    }, {\n        # `search` must be set to True for filter to be used in searching\n        # `search` must be applied only to text db-fields, which have ilike lookup\n        'filter': 'author__email',\n        'search': True,\n    }, {\n        # `source` must be set when filter name doesn't match ORM path\n        'filter': 'name',\n        'source': 'author__name',\n    }, {\n        # `namespace` is useful for API consistency, when dealing with related models\n        'namespace': 'author',\n        'filters': ['id', 'name'],  # will be converted to `author.id` and `author.name`\n    },{\n        # `distinct` needs to be setup for filters that require QS to work in DISTINCT mode\n        # `openapi` configuration is automatically collected by OpenAPI autogenerator\n        'filter': 'published.at',\n        'source': 'published_at',\n        'distinct': True,\n        'openapi': {\n            'required': True,\n            'deprecated': True,\n            'description': 'Good description',\n            'hidden': False,  # can be set to avoid collecting by autogenerator\n            # type and format are collected automatically and shouldn't be setup, in general\n            'type': 'string',\n            'format': 'date',\n        },\n    }, {\n        # `use_repr` flag is used to filter by choice representations\n        'filter': 'rating.blog',\n        'source': 'blog_rating',\n        'use_repr': True,\n    }, {\n        # `hidden` flag is used to set default select behaviour for associated field\n        'filter': 'rating.blog_int',\n        'source': 'blog_rating',\n        'use_repr': False,\n        'ordering': True,\n        'hidden': True,\n    }, {\n        # We can change default lookups for a certain filter\n        'filter': 'amazon_rating',\n        'lookups': {FilterLookups.GE, FilterLookups.LT},\n    }, {\n        # Sometimes it's needed to filter by several sources at once (distinct is always True).\n        # F.e. this could be helpful for searching.\n        'filter': 'd_id',\n        'sources': {'id', 'author__id'},\n        'ordering': True,\n    }, {\n        # Some fields may have no DB representation or non-typical ORM filtering\n        # `custom` option must be set to True for such fields\n        'filter': 'custom_filter',\n        'custom': True,\n        'lookups': {FilterLookups.EQ, FilterLookups.IN, FilterLookups.I_LIKE},\n        'ordering': True,\n        'search': True,\n         # Optional ORM field for query parameter value validation\n        'field': IntegerField(), \n\n        'custom_data': [1],\n    }]\n\n\nfrom dj_rql.drf.backend import RQLFilterBackend\nfrom dj_rql.drf.paginations import RQLContentRangeLimitOffsetPagination\n\n\nclass DRFViewSet(mixins.ListModelMixin, GenericViewSet):\n    queryset = MODEL.objects.all()\n    serializer_class = ModelSerializer\n    rql_filter_class = ModelFilterClass\n    pagination_class = RQLContentRangeLimitOffsetPagination\n    filter_backends = (RQLFilterBackend,)\n```\n\nNotes\n=====\n0. Values with whitespaces or special characters, like ',' need to have \"\" or ''\n1. Supported date format is ISO8601: 2019-02-12\n2. Supported datetime format is ISO8601: 2019-02-12T10:02:00 / 2019-02-12T10:02Z / 2019-02-12T10:02:00+03:00\n3. Support for Choices() fields from [Django Model Utilities](https://django-model-utils.readthedocs.io/en/latest/utilities.html#choices) is added\n4. Library supports [caching with different strategies](https://cachetools.readthedocs.io/en/stable/#cache-implementations) for queryset building, which can be very useful for collections, which use `select()`.\n\u003e Queryset execution result (filtered data) is NOT cached (!), only queryset building is cached.\n\n```python\nfrom dj_rql.filter_cls import RQLFilterClass\n\nfrom cachetools import LRUCache\n\nclass MyFilterClass(RQLFilterClass):\n    SELECT = True\n    QUERIES_CACHE_BACKEND = LRUCache\n    QUERIES_CACHE_SIZE = 100\n```\n\nHelpers\n================================\nThere is a Django command `generate_rql_class` to decrease development and integration efforts for filtering.\nThis command automatically generates a filter class for a given model with all relations and all optimizations (!) to the specified depth.\n\nExample\n-------\n```commandline\ndjango-admin generate_rql_class --settings=tests.dj_rf.settings tests.dj_rf.models.Publisher --depth=1 --exclude=authors,fk2\n```\nThis command for the model `Publisher` from tests package will produce the following output to stdout:\n```python\nfrom tests.dj_rf.models import Publisher\n\nfrom dj_rql.filter_cls import RQLFilterClass\nfrom dj_rql.qs import NSR\n\n\nclass PublisherFilters(RQLFilterClass):\n    MODEL = Publisher\n    SELECT = True\n    EXCLUDE_FILTERS = ['authors', 'fk2']\n    FILTERS = [\n    {\n        \"filter\": \"id\",\n        \"ordering\": True,\n        \"search\": False\n    },\n    {\n        \"filter\": \"name\",\n        \"ordering\": True,\n        \"search\": True\n    },\n    {\n        \"namespace\": \"fk1\",\n        \"filters\": [\n            {\n                \"filter\": \"id\",\n                \"ordering\": True,\n                \"search\": False\n            }\n        ],\n        \"qs\": NSR('fk1')\n    }\n]\n\n```\n\n\nDjango Rest Framework Extensions\n================================\n1. Pagination (limit, offset)\n2. Support for custom fields, inherited at any depth from basic model fields, like CharField().\n3. Backend `DjangoFiltersRQLFilterBackend` with automatic conversion of [Django-Filters](https://django-filter.readthedocs.io/en/master/) query to RQL query.\n4. OpenAPI docs are autogenerated for filter classes.\n\nBest Practices\n==============\n1. 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.\n2. Prefer using `custom=True` with `RQLFilterClass.build_q_for_custom_filter` overriding over overriding `RQLFilterClass.build_q_for_filter`.\n3. Custom filters may support ordering (`ordering=True`) with `build_name_for_custom_ordering`.\n4. Django JSON fields can't be used as namespaces currently, but can be supported via `dynamic=True`, for example:\n```python\n{\n    'filter': 'json_data.key',\n    'source': 'json_data__key',\n    'dynamic': True,\n    'field': CharField(null=True),\n},\n```\n\nDevelopment\n===========\n\n1. Python 3.8+\n2. Install poetry: `pip install poetry`\n3. Install dependencies: `poetry install`\n4. We use `isort` library to order and format our imports, and `black` - to format the code. \nWe check it using `flake8-isort` and `flake8-black` libraries (automatically on `flake8` run).  \nFor convenience you may run `isort . \u0026\u0026 black .` to format the code.\n5. Run flake8: `poetry run flake8`\n\nTesting\n=======\n\n1. Python 3.8+\n2. Install poetry: `pip install poetry`\n3. Install dependencies: `poetry install`\n\nCheck code style: `poetry run flake8`\nRun tests: `poetry run pytest`\n\nTests reports are generated in `tests/reports`.\n* `out.xml` - JUnit test results\n* `coverage.xml` - Coverage xml results\n\nTo generate HTML coverage reports use:\n`--cov-report html:tests/reports/cov_html`\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudblue%2Fdjango-rql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcloudblue%2Fdjango-rql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudblue%2Fdjango-rql/lists"}