Queries¶
Norman features a flexible and extensible query API, the basis of which is
the Query
class. Queries are constructed by manipulating Field
and other
Query
objects; the result of each operation is another Query
.
Examples¶
The following examples explain the basic concepts behind Norman queries.
Queries are constructed as a series of field comparisons, for example:
q1 = MyTable.age > 4
q2 = MyTable.parent.name == 'Bill'
These can be joined together with set combination operators:
q3 = (MyTable.age > 4) | (MyTable.parent.name == 'Bill')
Containment in an iterable can be checked using the &
operator. This
is the same usage as in set
:
q4 = MyTable.parent.name & ['Bill', 'Bob', 'Bruce']
Since queries are themselves iterable, another query can be used as the container:
q5 = MyTable.age & OtherTable.age
A custom function can be used for filtering records from a Table
or
another Query
:
def isvalid(record):
return record.parrot.endswith('notlob')
q6 = query(isvalid, q5)
If the filter function is omitted, then all records are assumed to pass. This is useful for creating a query of a whole table:
q7 = query(MyTable)
The result of each of these is a Query
object, which can be iterated over
to yield records. The query is not evaluated until a result is requested
from it (including len
). An existing query can be refreshed after the
base data has changed by calling it as a function. The return value is the
query iteself, so to ensure that the result is up to date, you could call:
latest_size = len(q7())
API¶
-
norman.
query
([func, ]table)¶ Return a new
Query
for records in table for which func isTrue
.table is a
Table
orQuery
object. If func is missing, all records are assumed to pass. If it is specified, is should accept a record as its argument and returnTrue
for passing records.
-
class
norman.
Query
(op, *args, **kwargs)¶ This object should never be instantiated directly, instead it should be created as the result of a
Field
comparison or by using thequery
function. The interface allows most operations permitted on sets, such as unions and intersections, but returns a newQuery
object instead of any results. The following operations are supported:Operation Description r in q
Return True
if recordr
is in the results of queryq
.len(q)
Return the number of results in q
.iter(q)
Return an iterator over records in q
.q1 == q2
Return True
ifq1
andq2
contain the same records.q1 != q2
Return True
ifnot a == b
q1 & q2
Return a new Query
object containing records in bothq1
andq2
.q1 | q2
Return a new Query
object containing records in eitherq1
orq2
.q1 ^ q2
Return a new Query
object containing records in eitherq1
orq2
, but not both.q1 - q2
Return a new Query
object containing records inq1
which are not inq2
.Queries evaluate to
True
if they contain any results, andFalse
if they do not.Calling a query forces it to be re-evaluated, and the query object is returned.
-
add
([arg, **kwargs])¶ Add a record based on the query criteria, and return the new record. There are two modes of operation for this method, depending on the query. For either mode, the query must be defined by a clear set of field values for a single
Table
. This includes queries such as(MyTable.field1` == 1) & (MyTable.field2` == 2)
but notMyTable.field1` > 1
.The first mode accepts keyword arguments, which are combined with the parameters used to construct the query and passed to the table constructor. For example:
``((MyTable.a` == 1) & (MyTable.b` == 2)).add(c=3)``
evaluates to:
MyTable(a=1, b=2, c=3)
The second mode is used when the query has been created by
field
. In this case, a single argument is expected which is the record to apply to the field. For example:(Table1.id == 4).field('table2').add(table2_instance)
is the same as:
(Table1.id == 4).add(table2=table2_instance)
-
delete
()¶ Delete all records matching the query from their table. If no records match, nothing is deleted.
-
field
(fieldname)¶ Return a new
Query
containing records in a single field.The set of records returned by this is similar to:
set(getattr(r, fieldname) for r in query)
However, the returned object is another
Query
instead of a set. Only instances of aTable
subclass are contained in the results, other values are dropped. This is functionally similar to a SQL query on a foreign key. If the target field is aJoin
, then all the results of each join are concatenated.
-
one
([default])¶ Return a single value from the query results. If the query is empty and default is specified, then it is returned instead, otherwise an
IndexError
is raised.
-