Skip to content

Markdown

Schema changes

Describe changes to an app's tables, fields, and relationships, then pass them to App.preview_schema_changes(), which says what each would do without making it, and App.apply_schema_changes(), which makes them if the app hasn't changed since. A change the app already has counts as done.

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. projects is a table named Job Projects, with a Name field. The examples only plan, unless they say otherwise.

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")

Tables

For the full picture, see Changing the schema.

CreateTable

Make a table. A plan treats a table with this name as already made.

Parameters

  • name str, required
    The table's name.
  • description str | None, optional
    What it holds.
  • single_record_name str | None, optional
    What it calls one record, such as Project.
  • plural_record_name str | None, optional
    What it calls several, such as Projects.
CreateTable(
    *,
    name: str,
    description: str | None = None,
    single_record_name: str | None = None,
    plural_record_name: str | None = None,
)

Example

from pyquickbase import CreateTable

plan = app.preview_schema_changes(
    [CreateTable(name="Job Sites", single_record_name="Site")]
)
print(plan.changes[0].summary)
from pyquickbase import CreateTable

plan = await app.preview_schema_changes(
    [CreateTable(name="Job Sites", single_record_name="Site")]
)
print(plan.changes[0].summary)

Output

Create table Job Sites.

UpdateTable

Rename a table, or change its descriptions. Anything left as None stays as it is.

Parameters

  • table str, required
    The table, by name or ID.
  • name str | None, optional
    A new name.
  • description str | None, optional
    A new description.
2 more: single_record_name, plural_record_name
  • single_record_name str | None, optional
    A new name for one record.
  • plural_record_name str | None, optional
    A new name for several.
UpdateTable(
    *,
    table: str,
    name: str | None = None,
    description: str | None = None,
    single_record_name: str | None = None,
    plural_record_name: str | None = None,
)

Example

from pyquickbase import UpdateTable

change = UpdateTable(table="Job Tasks", description="Work.")
plan = app.preview_schema_changes([change])
print(plan.changes[0].settings["description"].after)
from pyquickbase import UpdateTable

change = UpdateTable(table="Job Tasks", description="Work.")
plan = await app.preview_schema_changes([change])
print(plan.changes[0].settings["description"].after)

Output

Work.

DeleteTable

Delete a table and all its records.

Parameters

  • table str, required
    The table, by name or ID.
DeleteTable(*, table: str)

Example

from pyquickbase import DeleteTable

plan = app.preview_schema_changes(
    [DeleteTable(table="Job Tasks")]
)
change = plan.changes[0]
print(change.risk, change.affected_records)
from pyquickbase import DeleteTable

plan = await app.preview_schema_changes(
    [DeleteTable(table="Job Tasks")]
)
change = plan.changes[0]
print(change.risk, change.affected_records)

Output

destructive 3

Fields

For the full picture, see Changing the schema.

CreateField

Add a field, or a formula field when formula is set. A plan treats a field with this label, type, and formula as already made.

Parameters

  • table str, required
    The table, by name or ID.
  • label str, required
    The new field's label.
  • type str, required
    Its type, such as text or numeric. For a formula, the type of its result.
5 more: choices, required, unique, help_text, formula
  • choices tuple[str, ...] | None, optional
    The options of a text-multiple-choice or multitext field.
  • required bool | None, optional
    Whether it must have a value. Quickbase refuses this on a table with records.
  • unique bool | None, optional
    Whether each record must have a different value.
  • help_text str | None, optional
    The help shown beside it on forms.
  • formula str | None, optional
    Makes it a formula field, calculated from the record's other fields.
CreateField(
    *,
    table: str,
    label: str,
    type: str,
    choices: tuple[str, ...] | None = None,
    required: bool | None = None,
    unique: bool | None = None,
    help_text: str | None = None,
    formula: str | None = None,
)

Example

from pyquickbase import CreateField

plan = app.preview_schema_changes(
    [
        CreateField(
            table="Job Tasks",
            label="Cost",
            type="numeric",
            formula="[Hours] * 40",
        )
    ]
)
print(plan.changes[0].summary)
from pyquickbase import CreateField

plan = await app.preview_schema_changes(
    [
        CreateField(
            table="Job Tasks",
            label="Cost",
            type="numeric",
            formula="[Hours] * 40",
        )
    ]
)
print(plan.changes[0].summary)

Output

Add formula field "Cost" (numeric) to Job Tasks.

UpdateField

Change a field's settings. Anything left as None stays as it is. Its type can't change.

Parameters

  • table str, required
    The table, by name or ID.
  • field str | int, required
    The field, by label or ID.
  • label str | None, optional
    A new label.
5 more: choices, required, unique, help_text, formula
  • choices tuple[str, ...] | None, optional
    A new list of options, replacing the old one.
  • required bool | None, optional
    Whether it must have a value.
  • unique bool | None, optional
    Whether each record must have a different value.
  • help_text str | None, optional
    New help text.
  • formula str | None, optional
    A new formula, for a formula field only.
UpdateField(
    *,
    table: str,
    field: str | int,
    label: str | None = None,
    choices: tuple[str, ...] | None = None,
    required: bool | None = None,
    unique: bool | None = None,
    help_text: str | None = None,
    formula: str | None = None,
)

Example

from pyquickbase import UpdateField

plan = app.preview_schema_changes(
    [
        UpdateField(
            table="Job Tasks",
            field="Status",
            choices=("Open", "Done", "Blocked"),
        )
    ]
)
choices = plan.changes[0].settings["choices"]
print(choices.before, choices.after)
from pyquickbase import UpdateField

plan = await app.preview_schema_changes(
    [
        UpdateField(
            table="Job Tasks",
            field="Status",
            choices=("Open", "Done", "Blocked"),
        )
    ]
)
choices = plan.changes[0].settings["choices"]
print(choices.before, choices.after)

Output

['Open', 'Done'] ['Open', 'Done', 'Blocked']

DeleteField

Delete a field and all its values.

Parameters

  • table str, required
    The table, by name or ID.
  • field str | int, required
    The field, by label or ID.
DeleteField(*, table: str, field: str | int)

Example

from pyquickbase import DeleteField

plan = app.preview_schema_changes(
    [DeleteField(table="Job Tasks", field="Hours")]
)
print(plan.changes[0].warnings[0])
from pyquickbase import DeleteField

plan = await app.preview_schema_changes(
    [DeleteField(table="Job Tasks", field="Hours")]
)
print(plan.changes[0].warnings[0])

Output

3 records lose a value in it.

Relationships

For the full picture, see Changing the schema.

CreateRelationship

Link a child table to a parent, with a new field on the child that holds the parent's Record ID#. A plan treats a link with this foreign key as already made.

Parameters

  • child_table str, required
    The child table, by name or ID, such as Tasks.
  • parent_table str, required
    The parent table, such as Projects.
  • foreign_key str, required
    The label of the new field on the child, such as Project.
CreateRelationship(
    *, child_table: str, parent_table: str, foreign_key: str
)

Example

from pyquickbase import CreateRelationship

plan = app.preview_schema_changes(
    [
        CreateRelationship(
            child_table="Job Tasks",
            parent_table="Job Projects",
            foreign_key="Project",
        )
    ]
)
print(plan.changes[0].summary)
from pyquickbase import CreateRelationship

plan = await app.preview_schema_changes(
    [
        CreateRelationship(
            child_table="Job Tasks",
            parent_table="Job Projects",
            foreign_key="Project",
        )
    ]
)
print(plan.changes[0].summary)

Output

Link Job Tasks to Job Projects through a new field "Project" on Job Tasks.

CreateLookupField

Show a parent's field on each child record.

Parameters

  • child_table str, required
    The child table, by name or ID.
  • foreign_key str, required
    The label of the relationship's foreign key on the child.
  • parent_field str | int, required
    The parent's field to show, by label or ID.
  • label str | None, optional
    The lookup's label. Quickbase names one <foreign key> - <field> without it.
CreateLookupField(
    *,
    child_table: str,
    foreign_key: str,
    parent_field: str | int,
    label: str | None = None,
)

Example

from pyquickbase import (
    CreateLookupField,
    CreateRelationship,
)

plan = app.preview_schema_changes(
    [
        CreateRelationship(
            child_table="Job Tasks",
            parent_table="Job Projects",
            foreign_key="Project",
        ),
        CreateLookupField(
            child_table="Job Tasks",
            foreign_key="Project",
            parent_field="Name",
        ),
    ]
)
print(plan.changes[1].summary)
from pyquickbase import (
    CreateLookupField,
    CreateRelationship,
)

plan = await app.preview_schema_changes(
    [
        CreateRelationship(
            child_table="Job Tasks",
            parent_table="Job Projects",
            foreign_key="Project",
        ),
        CreateLookupField(
            child_table="Job Tasks",
            foreign_key="Project",
            parent_field="Name",
        ),
    ]
)
print(plan.changes[1].summary)

Output

Show "Name" from Job Projects on each Job Tasks record, as "Project - Name".

CreateSummaryField

Total, average, or count a child's records on each parent record.

Parameters

  • child_table str, required
    The child table, by name or ID.
  • foreign_key str, required
    The label of the relationship's foreign key on the child.
  • label str, required
    The label of the new field on the parent.
3 more: function, child_field, where
  • function SummaryFunction, default 'sum'
    How to combine the values, such as sum. count counts the child records, and takes no child_field.
  • child_field str | int | None, optional
    The child's field to combine, by label or ID.
  • where str | None, optional
    Only combine the child records that match, in Quickbase's query language with field IDs, such as {7.EX.true}. The plan checks it against the child's fields, as Quickbase saves one it can't use and the summary is then always empty.
CreateSummaryField(
    *,
    child_table: str,
    foreign_key: str,
    label: str,
    function: SummaryFunction = "sum",
    child_field: str | int | None = None,
    where: str | None = None,
)

Example

from pyquickbase import (
    CreateRelationship,
    CreateSummaryField,
)

plan = app.preview_schema_changes(
    [
        CreateRelationship(
            child_table="Job Tasks",
            parent_table="Job Projects",
            foreign_key="Project",
        ),
        CreateSummaryField(
            child_table="Job Tasks",
            foreign_key="Project",
            label="Total Hours",
            child_field="Hours",
        ),
    ]
)
print(plan.changes[1].summary)
from pyquickbase import (
    CreateRelationship,
    CreateSummaryField,
)

plan = await app.preview_schema_changes(
    [
        CreateRelationship(
            child_table="Job Tasks",
            parent_table="Job Projects",
            foreign_key="Project",
        ),
        CreateSummaryField(
            child_table="Job Tasks",
            foreign_key="Project",
            label="Total Hours",
            child_field="Hours",
        ),
    ]
)
print(plan.changes[1].summary)

Output

Add "Total Hours" to Job Projects: the sum of "Hours" on its Job Tasks records.

DeleteRelationship

Unlink a child table from its parent. Quickbase also deletes the relationship's lookups and summaries, and keeps the foreign key field, with its values, as a plain number.

Parameters

  • child_table str, required
    The child table, by name or ID.
  • foreign_key str, required
    The label of the relationship's foreign key on the child.
DeleteRelationship(
    *, child_table: str, foreign_key: str
)

Example

from pyquickbase import DeleteRelationship

change = DeleteRelationship(
    child_table="Job Tasks", foreign_key="Project"
)
plan = app.preview_schema_changes([change])
print(plan.changes[0].summary)
from pyquickbase import DeleteRelationship

change = DeleteRelationship(
    child_table="Job Tasks", foreign_key="Project"
)
plan = await app.preview_schema_changes([change])
print(plan.changes[0].summary)

Output

Unlink Job Tasks from Job Projects.

SummaryFunction

How CreateSummaryField combines a child's values. count counts the child records.

SummaryFunction = Literal[
    "sum",
    "avg",
    "max",
    "min",
    "std_dev",
    "count",
    "distinct_count",
    "combined_text",
    "combined_user",
]

Plans and results

For the full picture, see Changing the schema.

SchemaPlan

What a list of changes would do to an app, checked against it as it is now.

Pass it to App.apply_schema_changes(), which refuses it if the app has changed since.

Attributes

  • app_id str
    The app it's for.
  • changes tuple[PlannedChange, ...]
    What each change would do, in order.
  • fingerprint str
    A digest of everything the plan was checked against.
  • confirmation str
    From a preview_schema_changes operation, what its apply_schema_changes operation needs. Empty otherwise.
  • blockers tuple[str, ...]
    Every reason the plan can't be applied, from all its changes.
  • warnings tuple[str, ...]
    Everything its changes would also do or affect.
  • has_changes bool
    Whether applying it would change anything.

Example

from pyquickbase import CreateField

change = CreateField(
    table="Job Tasks", label="Code", type="date"
)
plan = app.preview_schema_changes([change])
print(plan.has_changes)
print(plan.blockers[0])
from pyquickbase import CreateField

change = CreateField(
    table="Job Tasks", label="Code", type="date"
)
plan = await app.preview_schema_changes([change])
print(plan.has_changes)
print(plan.blockers[0])

Output

False
Job Tasks already has a field labeled "Code" (text), so it can't be added. Choose another label, or change that field.

PlannedChange

What one change would do, checked against the app as it is now.

Attributes

  • change SchemaChange
    The change asked for.
  • status Literal['ready', 'already_done', 'blocked']
    ready if it would change the app, already_done if the app already has it, and blocked if it can't be applied, which stops the whole plan.
  • risk Risk
    additive, modifying, or destructive.
  • summary str
    What it does, in a sentence, such as Add field "Due" (date) to Tasks.
  • settings Mapping[str, SettingChange]
    What it would set, as {setting: SettingChange}.
  • warnings tuple[str, ...]
    What it would also do or affect, such as formulas that use a deleted field.
  • blockers tuple[str, ...]
    Why it can't be applied.
  • affected_records int | None
    How many records it would affect, such as those that would lose a deleted field's values, if counted.
  • link str | None
    A Quickbase page to check it on, if there is one yet.
  • policy_effects tuple[str, ...]
    What the client's policy would do once the change is made: who can read and write a new field, and its classes, and which rules lose a deleted field or table. A rule it would leave matching nothing is also a blocker.

Example

from pyquickbase import UpdateField

change = UpdateField(
    table="Job Tasks", field="Code", unique=True
)
plan = app.preview_schema_changes([change])
planned = plan.changes[0]
print(planned.status, planned.risk)
from pyquickbase import UpdateField

change = UpdateField(
    table="Job Tasks", field="Code", unique=True
)
plan = await app.preview_schema_changes([change])
planned = plan.changes[0]
print(planned.status, planned.risk)

Output

already_done modifying

SettingChange

One setting before and after a change, with a setting that isn't there as None.

Attributes

  • before Any
    The setting now.
  • after Any
    What the change would set.

Example

from pyquickbase import UpdateField

plan = app.preview_schema_changes(
    [UpdateField(table="Job Tasks", field=6, label="Task")]
)
setting = plan.changes[0].settings["label"]
print(setting.before, "->", setting.after)
from pyquickbase import UpdateField

plan = await app.preview_schema_changes(
    [UpdateField(table="Job Tasks", field=6, label="Task")]
)
setting = plan.changes[0].settings["label"]
print(setting.before, "->", setting.after)

Output

Title -> Task

SchemaChangeResult

What applying a plan did.

Attributes

  • applied tuple[PlannedChange, ...]
    The changes made, in order.
  • already_done tuple[PlannedChange, ...]
    The changes the app already had.
  • failed PlannedChange | None
    The change that failed, if one did. The ones after it weren't attempted.
  • not_attempted tuple[PlannedChange, ...]
    The changes after one that failed.
  • error str | None
    Why it failed, if it did.

Example

from pyquickbase import CreateField, UpdateField

plan = app.preview_schema_changes(
    [
        CreateField(
            table="Job Tasks", label="Crew", type="text"
        ),
        UpdateField(
            table="Job Tasks", field="Code", unique=True
        ),
    ]
)
result = app.apply_schema_changes(plan)
print(len(result.applied), len(result.already_done))
from pyquickbase import CreateField, UpdateField

plan = await app.preview_schema_changes(
    [
        CreateField(
            table="Job Tasks", label="Crew", type="text"
        ),
        UpdateField(
            table="Job Tasks", field="Code", unique=True
        ),
    ]
)
result = await app.apply_schema_changes(plan)
print(len(result.applied), len(result.already_done))

Output

1 1

Risk

How much a planned change can lose: additive only adds, modifying changes what's there, and destructive deletes data or fields.

Risk = Literal["additive", "modifying", "destructive"]

Any change

For the full picture, see Using pyquickbase from an AI agent.

SchemaChange

Any one of the changes on this page, as App.preview_schema_changes() takes them.

SchemaChange = (
    CreateTable
    | UpdateTable
    | DeleteTable
    | CreateField
    | UpdateField
    | DeleteField
    | CreateRelationship
    | CreateLookupField
    | CreateSummaryField
    | DeleteRelationship
)

SchemaAction

The action of a change in the JSON form a model sends, such as create_field.

SchemaAction = Literal[
    "create_table",
    "update_table",
    "delete_table",
    "create_field",
    "update_field",
    "delete_field",
    "create_relationship",
    "create_lookup_field",
    "create_summary_field",
    "delete_relationship",
]