Package: django-rql
Django RQLdjango-rqlis the Django app, that adds RQL filtering to your application.
This library is based on corelib-rqllibrary.RQLRQL (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 ReferenceCurrently supported operatorsComparison (eq, ne, gt, ge, lt, le, like, ilike, search)List (in, out)Logical (and, or, not)Constants (null(), empty())Ordering (ordering)Select (select)Tuple (t)DocumentationFull documentation is available athttps://django-rql.readthedocs.org.Examplefromdj_rql.filter_clsimportRQLFilterClass,RQL_NULLfrompy_rql.constantsimportFilterLookupsclassModelFilterClass(RQLFilterClass):"""MODEL - Django ORM modelFILTERS - List of filtersEXTENDED_SEARCH_ORM_ROUTES - List of additional Django ORM fields for searchDISTINCT - Boolean flag, that specifies if queryset must always be DISTINCTSELECT - Boolean flag, that specifies if Filter Class supports select operations and queryset optimizationsOPENAPI_SPECIFICATION - Python class that renders OpenAPI specificationMAX_ORDERING_LENGTH_IN_QUERY - Integer max allowed number of provided ordering filters in query ordering expressionALLOWED_ORDERING_PERMUTATIONS_IN_QUERY - Set of tuples of strings to specify a set of allowed ordering permutationsFilters 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=ModelFILTERS=['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],}]fromdj_rql.drf.backendimportRQLFilterBackendfromdj_rql.drf.paginationsimportRQLContentRangeLimitOffsetPaginationclassDRFViewSet(mixins.ListModelMixin,GenericViewSet):queryset=MODEL.objects.all()serializer_class=ModelSerializerrql_filter_class=ModelFilterClasspagination_class=RQLContentRangeLimitOffsetPaginationfilter_backends=(RQLFilterBackend,)NotesValues with whitespaces or special characters, like ',' need to have "" or ''Supported date format is ISO8601: 2019-02-12Supported datetime format is ISO8601: 2019-02-12T10:02:00 / 2019-02-12T10:02Z / 2019-02-12T10:02:00+03:00Support for Choices() fields fromDjango Model Utilitiesis addedLibrary supportscaching with different strategiesfor queryset building, which can be very useful for collections, which useselect().Queryset execution result (filtered data) is NOT cached (!), only queryset building is cached.fromdj_rql.filter_clsimportRQLFilterClassfromcachetoolsimportLRUCacheclassMyFilterClass(RQLFilterClass):SELECT=TrueQUERIES_CACHE_BACKEND=LRUCacheQUERIES_CACHE_SIZE=100HelpersThere is a Django commandgenerate_rql_classto 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.Exampledjango-admin generate_rql_class --settings=tests.dj_rf.settings tests.dj_rf.models.Publisher --depth=1 --exclude=authors,fk2This command for the modelPublisherfrom tests package will produce the following output to stdout:fromtests.dj_rf.modelsimportPublisherfromdj_rql.filter_clsimportRQLFilterClassfromdj_rql.qsimportNSRclassPublisherFilters(RQLFilterClass):MODEL=PublisherSELECT=TrueEXCLUDE_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 ExtensionsPagination (limit, offset)Support for custom fields, inherited at any depth from basic model fields, like CharField().BackendDjangoFiltersRQLFilterBackendwith automatic conversion ofDjango-Filtersquery to RQL query.OpenAPI docs are autogenerated for filter classes.Best PracticesUsedj_rql.utils.assert_filter_clsto 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.Prefer usingcustom=TruewithRQLFilterClass.build_q_for_custom_filteroverriding over overridingRQLFilterClass.build_q_for_filter.Custom filters may support ordering (ordering=True) withbuild_name_for_custom_ordering.Django JSON fields can't be used as namespaces currently, but can be supported viadynamic=True, for example:{'filter':'json_data.key','source':'json_data__key','dynamic':True,'field':CharField(null=True),},DevelopmentPython 3.8+Install poetry:pip install poetryInstall dependencies:poetry installWe useisortlibrary to order and format our imports, and we check it usingflake8-isortlibrary (automatically onflake8run).For convenience you may runisort .to order imports.Run flake8:poetry run flake8TestingPython 3.8+Install poetry:pip install poetryInstall dependencies:poetry installCheck code style:poetry run flake8Run tests:poetry run pytestTests reports are generated intests/reports.out.xml- JUnit test resultscoverage.xml- Coverage xml resultsTo generate HTML coverage reports use:--cov-report html:tests/reports/cov_html