Queries¶
Conditions choose which records a query, count, or delete applies to. Build them with F, which escapes values for you, or with where_from_dict for plain data from a model or a web form.
The examples below use tasks, made as shown here, and app, which is qb.app("bq1234567"). Job Tasks has Title, Status, Hours, Due Date, Code, which is unique, and Attachment. It starts with three records, and each example starts from those again. A live test runs every example with both clients and checks its output.
from pyquickbase import Quickbase
with Quickbase(
realm="acme", token="your_user_token"
) as qb:
tasks = qb.app("bq1234567").table("Job Tasks")
from pyquickbase import AsyncQuickbase
async with AsyncQuickbase(
realm="acme", token="your_user_token"
) as qb:
tasks = qb.app("bq1234567").table("Job Tasks")
Build a condition¶
For the full picture, see Reading records.
F¶
A field in a query, named by label (str) or ID (int).
(F("Status") == "Done") & (F("Due Date") < date.today()). Values are escaped for you, so this is the safe way to use text you don't control. == None matches empty fields.
On a user or multiuser field, compare with an email or user ID. On a percent field, a number is a fraction, as it is when writing, so F("Done") >= 0.5 means 50%. On a duration field, a number is milliseconds, or use a timedelta. Text is sent as written, such as F("Budget") > "8 hours".
Parameters
fieldstr | int, required
The field's label or ID.
F(field: str | int)
Example
from pyquickbase import F
where = (F("Status") == "Open") & (F("Hours") >= 10)
for task in tasks.query(where=where, select=["Title"]):
print(task["Title"])
print(tasks.count(F("Title").contains("wall")))
print(tasks.count(F("Title").starts_with("Pour")))
print(tasks.count(F("Status").is_in(["Open", "Done"])))
print(tasks.count(F("Due Date") == None))
print(tasks.count(F("Title").op("XCT", "wall")))
from pyquickbase import F
where = (F("Status") == "Open") & (F("Hours") >= 10)
async for task in tasks.query(where=where, select=["Title"]):
print(task["Title"])
print(await tasks.count(F("Title").contains("wall")))
print(await tasks.count(F("Title").starts_with("Pour")))
print(await tasks.count(F("Status").is_in(["Open", "Done"])))
print(await tasks.count(F("Due Date") == None))
print(await tasks.count(F("Title").op("XCT", "wall")))
Output
Frame walls
2
1
3
1
1
Match text that contains¶
Match records whose value contains this text.
Parameters
valueAny, required
The text to look for.
Returns Condition
A condition to pass as where.
contains(value: Any) -> Condition
Match text that doesn't contain¶
Match records whose value doesn't contain this text.
Parameters
valueAny, required
The text to look for.
Returns Condition
A condition to pass as where.
not_contains(value: Any) -> Condition
Match text that starts with¶
Match records whose value starts with this text.
Parameters
valueAny, required
The text to look for.
Returns Condition
A condition to pass as where.
starts_with(value: Any) -> Condition
Match text that doesn't start with¶
Match records whose value doesn't start with this text.
Parameters
valueAny, required
The text to look for.
Returns Condition
A condition to pass as where.
not_starts_with(value: Any) -> Condition
Match one of a field's values¶
Match records where one of the field's values is exactly this, such as one choice of a multi-select field, or one user of a list-user field.
Parameters
valueAny, required
The whole value to look for. Unlikecontains, part of a value doesn't match.
Returns Condition
A condition to pass as where.
has(value: Any) -> Condition
Match none of a field's values¶
Match records where none of the field's values is exactly this.
Parameters
valueAny, required
The whole value to look for.
Returns Condition
A condition to pass as where.
not_has(value: Any) -> Condition
Match any of several values¶
Match any of the values, as a series of EX conditions joined with OR.
Parameters
valuesIterable[Any], required
The values to match. There must be at least one.
Returns Expression
A condition to pass as where.
Raises
QueryError: There are no values.
is_in(values: Iterable[Any]) -> Expression
Use any operator¶
Use any Quickbase operator directly, such as TV or IR.
Parameters
operatorstr, required
The operator, in capitals. Quickbase treats a lowercase one as "contains".valueAny, required
The value to compare with.
Returns Condition
A condition to pass as where.
Raises
QueryError: Quickbase has no such operator.
op(operator: str, value: Any) -> Condition
Expression¶
A query condition. Combine with & and |, not and and or.
Example
from pyquickbase import F
done_or_long = (F("Status") == "Done") | (F("Hours") > 10)
print(tasks.count(done_or_long))
from pyquickbase import F
done_or_long = (F("Status") == "Done") | (F("Hours") > 10)
print(await tasks.count(done_or_long))
Output
2
Conditions from plain data¶
For the full picture, see Using pyquickbase from an AI agent.
Build a condition from JSON¶
Build a condition from plain data, such as the JSON from a model's tool call.
The result works anywhere a condition built with F does, including combined with one, which is how to keep a model inside a scope your code decides:
scope = F("Project") == project
allowed = ["Status", "Due Date"]
where = scope & where_from_dict(
data, fields=allowed
)
A condition is {"field": "Status", "op": "==", "value": "Open"}, and {"all": [...]} and {"any": [...]} combine them, nested if needed. field is a label or ID. op is ==, !=, <, <=, >, >=, contains, not_contains, starts_with, not_starts_with, has, not_has, in, not_in, or a Quickbase operator such as TV, other than IR and XIR. has matches one of a multi-select field's values exactly.
Spellings a model tends to write are read as the operator they mean: case, spaces, hyphens, and camelCase don't matter, so starts with and startsWith are starts_with, and eq, =, ne, <>, lt, gte, and the like are the matching symbol. and and or are read as all and any. Each of these means exactly one operator, so none can change what a filter means. Labels are never guessed: a label must match exactly.
Values are escaped as they are with F. Unlike F, a value is also checked against the type of its field when the query is built: dates must be YYYY-MM-DD, timestamps need a time zone or to be dates, and numbers and checkboxes must be what they say. So today is refused, as Quickbase matches nothing, with no error, for text it can't read as a date.
This protects a query's shape, not its meaning. A model that chooses the field and operator chooses the result, so a condition can still match every record. See Using pyquickbase from an AI agent.
Parameters
dataMapping[str, Any], required
The condition or group.valueis required on every condition, so that a dropped key can't turn into a search for empty fields. Sendnullto mean that on purpose, which works with==,!=,in, andnot_in.fieldsIterable[str | int] | Iterable[Field] | None, optional
The only fields this filter may use, as labels, IDs, orFieldobjects, which count by label. Pass the same list as towhere_schema. Without this, it may use any field the token can see, and a model could learn a value it can't read by filtering on it. A field listed by label can't be named by its ID.
Returns Expression
A condition to pass as where.
Raises
QueryError: The data can't be used, and the message says where. Nothing was sent. One with over 100 conditions, counting each value ofinandnot_in, or groups over 20 levels deep, is refused too. Type checks raise this when the query is built, not here.UnknownFieldError: The table has no such field, raised when the query is built.
where_from_dict(
data: Mapping[str, Any],
*,
fields: (
Iterable[str | int] | Iterable[Field] | None
) = None,
) -> Expression
Example
from pyquickbase import where_from_dict
data = {
"all": [
{"field": "Status", "op": "==", "value": "Open"},
{"field": "Hours", "op": ">", "value": 10},
]
}
where = where_from_dict(data, fields=["Status", "Hours"])
print(tasks.count(where))
from pyquickbase import where_from_dict
data = {
"all": [
{"field": "Status", "op": "==", "value": "Open"},
{"field": "Hours", "op": ">", "value": 10},
]
}
where = where_from_dict(data, fields=["Status", "Hours"])
print(await tasks.count(where))
Output
1
Describe conditions as JSON Schema¶
Describe the format of where_from_dict as JSON Schema, for a tool's input.
The schema is self-contained, with no $ref, so it can go anywhere in a tool's input schema. It's a new dictionary each time, so you can add to it, such as a description.
Parameters
fieldsIterable[str | int] | Iterable[Field] | None, optional
The fields to offer, as labels, IDs, orFieldobjects, so a model can't name one that doesn't exist. Pass the same fields towhere_from_dict, by label or ID, to enforce the list, as this only describes it. Field labels can contain any text, and this shows them to the model, so only offer fields from an app you trust. GivenFieldobjects, the description also says what a number means on a percent or duration field.field_descriptionsMapping[str | int, str] | None, optional
What each field means, as{label or ID: text}, added to the schema's description so the model can choose the right field. Each must be for a field infields, so a renamed field can't silently keep a description.max_depthint, default2
How many levels ofallandanyto describe, from 0 to 5. The default allowsA and (B or C). A tool's schema is sent to the model with every request, so each level has a cost.where_from_dictaccepts deeper filters regardless.
Returns dict[str, Any]
The JSON Schema, as a dictionary.
Raises
QueryError:fieldsis empty,max_depthis out of range, or a description isn't for a field infields.
where_schema(
*,
fields: (
Iterable[str | int] | Iterable[Field] | None
) = None,
field_descriptions: Mapping[str | int, str] | None = None,
max_depth: int = 2,
) -> dict[str, Any]
Example
from pyquickbase import where_schema
schema = where_schema(fields=["Status", "Hours"], max_depth=1)
print(type(schema).__name__, "$ref" in str(schema))
from pyquickbase import where_schema
schema = where_schema(fields=["Status", "Hours"], max_depth=1)
print(type(schema).__name__, "$ref" in str(schema))
Output
dict False
Check a hand-written condition¶
For the full picture, see Reading records.
Check a hand-written condition¶
Check a hand-written where string and return its conditions.
Useful for extra checks of your own, or for finding out which fields a string refers to.
Parameters
wherestr, required
The string, such as{7.EX.'Open'}.fieldsIterable[Field], required
The table's fields, fromTable.fields().
Returns list[ WhereCondition ]
A WhereCondition for each condition in the string.
Raises
QueryError: It has the same mistakes thattable.query()would refuse.UnknownFieldError: It names a field that isn't infields.
parse_where(
where: str, fields: Iterable[Field]
) -> list[WhereCondition]
Example
from pyquickbase import parse_where
fields = tasks.fields()
where = "{7.EX.'Open'}AND{8.GT.'5'}"
print([c.field_id for c in parse_where(where, fields)])
from pyquickbase import parse_where
fields = await tasks.fields()
where = "{7.EX.'Open'}AND{8.GT.'5'}"
print([c.field_id for c in parse_where(where, fields)])
Output
[7, 8]
WhereCondition¶
One condition from a where string, with its value unquoted and unescaped.
Attributes
field_idint
The ID of the field it tests.operatorstr
Quickbase's operator, such asEX.valuestr
The value it compares with, as text.
Example
from pyquickbase import parse_where
fields = tasks.fields()
condition = parse_where("{7.EX.'Open'}", fields)[0]
print(condition.field_id, condition.operator, condition.value)
from pyquickbase import parse_where
fields = await tasks.fields()
condition = parse_where("{7.EX.'Open'}", fields)[0]
print(condition.field_id, condition.operator, condition.value)
Output
7 EX Open
Ways to name a field¶
For the full picture, see Reading records.
FieldRef¶
A field, by label (str) or ID (int).
FieldRef = str | int