Django Remote Queryset (a.k.a. DRQ) is a library to execute rich ORM queries from client-side using the same interface of Django ORM.
DRQ defines a JSON syntax to call all Django's Queryset methods.
Consider this example:
from myapp.models import MyModel not_null_title = MyModel.objects.filter(title__isnull=False) DRQ query syntax for the previous filter is the following
{ "_query_class" : "filter", "_condition" : "title__isnull", "_value" : false }DRQ supports Django Rest Framework and automatically decodes and applies this filter to any GET request.
DRQ is distrubuted through pypi
$ pip install django-remote-queryset - Add django_remote_queryset to your INSTALLED_APPS:
INSTALLED_APPS = [ ... 'rest_framework', 'django_remote_queryset', ... ]Django rest_framework is strongly recommended but if you prefer to use DRQ without it you can see Advanced Usage section.
- Extend DRQModelViewSet for your model:
from django_remote_queryset.viewset import DRQModelViewSet class MyModelModelViewSet(DRQModelViewSet): queryset = MyModel.objects.all() serializer_class = MyModelModelSerializerAnd use it like any other Django REST Framework ViewSet!
- Enjoy filtering from your browser!
GET: http://yourdomain/my_model_rest_path/?query= query = { "_query_class" : "filter", "_condition" : "title__isnull", "_value" : false } For further informations on DRF ViewSets see Django Rest Framework documentation.
This repository contains a example_django_project folder. Try it doing the following:
$ cd example_django_app # Install site dependencies $ pip install -r requirements.txt # Create sqlite database $ python manage.py migrate # Create demo data $ python manage.py create_demo_data # Run the server $ python manage.py runserverThen:
- Go to http://localhost:8000 and you should see the usual Django Rest Framework page.
- Go to http://localhost:8000/polls and you should see the list of all polls available
- Go to http://localhost:8000/polls/?query={"_query_class":"filter","_condition":"title__icontains","_value":"DRQ"}
It's possible to extend/customize DRQ using it in your extisting ViewSets or integrating it in your own custom Views/Django code.
DRQ is implemented using DRF's filter_backends, you can add it to your ModelViewSet easily
from django_remote_queryset.backend import DRQFilterBackend class AnotherModelViewSet(ModelViewSet): filter_backends = ( ..., DRQFilterBackend, ...)You can use directly the QueryDecoder to decode and apply the json_query to any queryset
from django_remote_queryset.queries import QueryDecoder ... original_queryset = MyModel.objects.all() json_query = { "_query_class" : "filter", "_condition" : "title__isnull", "_value" : false } query = QueryDecoder \ .decodeJSONQuery(json_query) if query is not None: filtered_queryset = query.applyOnQuerySet(original_queryset)DRQ Queries are built using the Composite Pattern. Several queries are already available and the Framework could be extended with custom ones. All the queries are contained in django_remote_queryset.queries module.
This is the abstract one, defines the interface for every child.
class Query(): def __init__(self, json_query): """ Initializes the sub_tree of this query starting from the json_query parameter :param json_query: dict """ pass def applyOnQuerySet(self, queryset): """ Applies the sub_tree of this query to the queryset passed as parameter :param queryset: Queryset :return: Queryset """ return queryset It's JSON syntax is the following
{ "_query_class":"query" }This query applies the .filter( ... ) method on the queryset
It's JSON syntax is the following
{ "_query_class": "filter", "_condition": [string] | string, "_value": [*] | * }Examples:
- Single lookup
Person.filter(age__gte=4) { "_query_class": "filter", "_condition": "age__gte", "_value": 4 }- Multiple lookups
Person.filter(age__gte=4, name__icontains='Nic') { "_query_class": "filter", "_condition": ["age__gte","name__icontains"], "_value": [4,"Nic"] }This query applies the .exclude( ... ) method on the queryset
It's JSON syntax is the following
{ "_query_class": "exclude", "_condition": [string] | string, "_value": [*] | * }Examples:
- Single lookup
Person.exclude(age__gte=4) { "_query_class": "exclude", "_condition": "age__gte", "_value": 4 }- Multiple lookups
Person.exclude(age__gte=4, name__icontains='Nic') { "_query_class": "exclude", "_condition": ["age__gte","name__icontains"], "_value": [4,"Nic"] }This query creates a chain of subqueries applying all it's sub_tree on the queryset.
It's JSON syntax is the following
{ "_query_class": "compositequery", "_sub_queries":[ ... other queries ... ] }The subqueries are applied as a chain.
for query in self._sub_queries: queryset = query.applyOnQuerySet(queryset)Examples:
- Chained filters
Person.objects.filter(age__gte=4).filter(name__icontains='Nic') { "_query_class": "compositequery", "_sub_queries":[ { "_query_class": "filter", "_condition": "age__gte", "_value": 4 }, { "_query_class": "filter", "_condition": "name__icontains", "_value": "Nic" } ] }- Chained mixed queries
Person.objects.filter(age__gte=4).exclude(name__icontains='Nic') { "_query_class": "compositequery", "_sub_queries":[ { "_query_class": "filter", "_condition": "age__gte", "_value": 4 }, { "_query_class": "exclude", "_condition": "name__icontains", "_value": "Nic" } ] }