OData query options

OData query options from Python - $filter, $select, $expand, $orderby, paging and counts, in both V2 and V4.

OData pushes filtering, sorting and shaping of results to the server - the client describes what it wants in query options and only the matching data travels over the wire. All the options below work with .read, .iter and .get, either as keyword arguments or through a reusable Query object.

Keyword arguments

The most direct form - each OData system query option is a keyword argument, without the dollar prefix:

conn = self.odata['OData.Sample']

customers = conn.read('Customers',
    filter="City eq 'London' and IsBlocked eq false",
    select='ID,DisplayName,City',
    expand='Orders',
    orderby='DisplayName desc',
    top=20,
    skip=40,
)

The client encodes each option correctly for the connection's OData version - for instance, inline counts are requested with $count=true in V4 and $inlinecount=allpages in V2.

NamePurpose
filterServer-side filtering, e.g. Price gt 100 and Category eq 'Books'
selectOnly the named properties travel back - a string or a list of names
expandRelated entities come inline, e.g. Orders or Orders($top=5) in V4
orderbySorting, e.g. Name or Price desc - a string or a list
topAt most this many entities
skipSkip this many entities first
countInclude the total match count alongside the results
searchFree-text search (V4)
applyAggregations and grouping (V4), e.g. groupby((Category),aggregate(Price with sum as Total))
customA dict of extra parameters passed through as they are, e.g. SAP's sap-client

The Query object

When the same options apply to more than one call, or when they are built up conditionally, use a Query object:

# Zato
from zato.common.odata.query import Query

query = Query(
    filter="Country eq 'DE'",
    select=['ID', 'DisplayName'],
    orderby='DisplayName',
    top=50,
)

customers = conn.read('Customers', query)

Escaping literals

Values embedded in filters follow OData's literal rules - single quotes inside strings are doubled, dates and GUIDs have version-specific formats. The format_literal function produces a correct literal for any Python value, matching the connection's OData version:

# Zato
from zato.common.odata.query import format_literal

name = "O'Brien"
customers = conn.read('Customers', filter=f'DisplayName eq {format_literal(name)}')

With ODataAdapter services, {placeholder} values substituted into the filter attribute are escaped automatically.

Counting

To learn only the number of matching entities, use .count - it maps to the $count path segment and transfers a single number, not the data:

how_many = conn.count('Orders', filter="Status eq 'Open'")

Paging

Servers cap how many entities one response contains and include a link to the next page. The .iter method follows those links transparently:

for order in conn.iter('Orders', filter="Status eq 'Open'"):
    process(order)

If the connection's Page size is set, the client also asks the server for pages of that size using the Prefer: odata.maxpagesize header.

Learn more