Using OData with Django

Basic Usage

The easiest way to add OData filtering to a Django QuerySet is with the shorthand:

from odata_query.django import apply_odata_query

orm_query = MyModel.objects  # This can be a Manager or a QuerySet.
odata_query = "name eq 'test'"  # This will usually come from a query string parameter.

query = apply_odata_query(orm_query, odata_query)
results = query.all()

Advanced Usage

If you need some more flexibility or advanced features, the implementation of the shorthand is a nice starting point: odata_query.django.shorthand

Let’s break it down real quick:

Parsing the OData Query

To get from a string representing an OData query to a usable representation, we need to tokenize and parse it as follows:

from odata_query.grammar import ODataParser, ODataLexer

lexer = ODataLexer()
parser = ODataParser()
ast = parser.parse(lexer.tokenize(my_odata_query))

This process is described in more detail in Parsing OData.

Optional: Modifying the AST

There are cases where you’ll want to modify the query before executing it. That’s what NodeTransformer’s are for!

One example might be that certain fields are exposed to end users under a different name than the one in the database. In this case, the odata_query.rewrite.AliasRewriter will come in handy. Just pass it a mapping of aliases to their full name and let it do its job:

from odata_query.rewrite import AliasRewriter

rewriter = AliasRewriter({
    "name": "author/name",
})
new_ast = rewriter.visit(ast)

Building a Query Filter

To get from an AST to something Django can use, you’ll need to use the odata_query.django.django_q.AstToDjangoQVisitor. It needs to know about the ‘root model’ of your query in order to build relationships if necessary. In most cases, this will be queryset.model.

from odata_query.django.django_q import AstToDjangoQVisitor

visitor = AstToDjangoQVisitor(MyModel)
query_filter = visitor.visit(ast)

Optional: QuerySet Annotations

For some queries using advanced expressions, the AstToDjangoQVisitor will generate queryset annotations. For the query to work, these will need to be applied:

if visitor.queryset_annotations:
    queryset = queryset.annotate(**visitor.queryset_annotations)

Running the query

Finally, we’re ready to run the query:

results = queryset.filter(query_filter).all()