Skip to content

Rules of your own

For logic the rules can't express, add an object with a decide(request) method to rules. It sees each request the built-in rules allow, as a ReadRequest or WriteRequest, which holds no values, so it's safe to log. It returns None to leave the request alone, or a Decision with allowed=False to refuse it. It can only refuse, so a bug in it can't allow more than the rules do.

from pyquickbase import Decision, Policy, WriteRequest, allow, fields


class SmallBatches:
    def decide(self, request):
        if isinstance(request, WriteRequest) and (request.row_count or 0) > 100:
            return Decision(allowed=False, verb="insert", reason="Up to 100 rows at a time")
        return None


policy = Policy(rules=[allow.insert("Job Tasks", fields.all()), SmallBatches()])

offers() asks about one field and verb at a time, so a rule that decides each field on its own is offered exactly what it allows. It runs once per request, so a write split into batches is checked batch by batch, and it sees each batch's own row_count. A custom rule can't be saved with to_json(), so pass it to from_json(rules=[...]) again.

A WriteRequest says what a write would do:

Attribute What it holds
action insert, update, upsert, delete, delete_file, a schema change such as create_field or delete_relationship, or other
table_id The table's ID, or for a relationship, the child's
field_ids The IDs of the fields it sets, or for update_field and delete_field, the fields it changes
row_count How many rows are in this request
app_id For create_table and update_table, the app
parent_table_id For a relationship, the parent table
field_type For create_field, the new field's type
derived The formula, lookup, and summary fields it makes or changes
method, path The HTTP request, for anything action doesn't cover

A ReadRequest says what a read would do:

Attribute What it holds
action query, summarize, delete, report, summary_report, file, formula, tables, fields, relationships, reports, app, roles, or other
table_id The table's ID
field_ids, filter_field_ids, sort_field_ids The fields it returns, filters on, and sorts by
limit, skip How many records it asks for, which Quickbase calls top, and how many it passes over
report_id The report it runs
method, path The HTTP request, for anything action doesn't cover

Treat None as "couldn't be determined", not as "nothing". For example, field_ids is None if a row writes a field ID in an unusual form, such as 07, which Quickbase reads as field 7, and a query without sortBy has None for sort_field_ids, because the table's default sort would apply. Have your rule ignore an action it doesn't recognize, as a later version can add one.

upsert means a write that may do either, which isn't only table.upsert(). Any merge on a field other than Record ID# is one, because Quickbase creates a record when the value doesn't match. So is a write without a merge field that goes through qb.request() or to a table with a different key field, because it can change an existing record.