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
namestr, required
The table's name.descriptionstr | None, optional
What it holds.single_record_namestr | None, optional
What it calls one record, such asProject.plural_record_namestr | None, optional
What it calls several, such asProjects.
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
tablestr, required
The table, by name or ID.namestr | None, optional
A new name.descriptionstr | None, optional
A new description.
2 more: single_record_name, plural_record_name
single_record_namestr | None, optional
A new name for one record.plural_record_namestr | 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
tablestr, 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
tablestr, required
The table, by name or ID.labelstr, required
The new field's label.typestr, required
Its type, such astextornumeric. For a formula, the type of its result.
5 more: choices, required, unique, help_text, formula
choicestuple[str, ...] | None, optional
The options of atext-multiple-choiceormultitextfield.requiredbool | None, optional
Whether it must have a value. Quickbase refuses this on a table with records.uniquebool | None, optional
Whether each record must have a different value.help_textstr | None, optional
The help shown beside it on forms.formulastr | 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
tablestr, required
The table, by name or ID.fieldstr | int, required
The field, by label or ID.labelstr | None, optional
A new label.
5 more: choices, required, unique, help_text, formula
choicestuple[str, ...] | None, optional
A new list of options, replacing the old one.requiredbool | None, optional
Whether it must have a value.uniquebool | None, optional
Whether each record must have a different value.help_textstr | None, optional
New help text.formulastr | 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
tablestr, required
The table, by name or ID.fieldstr | 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_tablestr, required
The child table, by name or ID, such asTasks.parent_tablestr, required
The parent table, such asProjects.foreign_keystr, required
The label of the new field on the child, such asProject.
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_tablestr, required
The child table, by name or ID.foreign_keystr, required
The label of the relationship's foreign key on the child.parent_fieldstr | int, required
The parent's field to show, by label or ID.labelstr | 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_tablestr, required
The child table, by name or ID.foreign_keystr, required
The label of the relationship's foreign key on the child.labelstr, required
The label of the new field on the parent.
3 more: function, child_field, where
functionSummaryFunction, default'sum'
How to combine the values, such assum.countcounts the child records, and takes nochild_field.child_fieldstr | int | None, optional
The child's field to combine, by label or ID.wherestr | 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_tablestr, required
The child table, by name or ID.foreign_keystr, 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_idstr
The app it's for.changestuple[PlannedChange, ...]
What each change would do, in order.fingerprintstr
A digest of everything the plan was checked against.confirmationstr
From apreview_schema_changesoperation, what itsapply_schema_changesoperation needs. Empty otherwise.blockerstuple[str, ...]
Every reason the plan can't be applied, from all its changes.warningstuple[str, ...]
Everything its changes would also do or affect.has_changesbool
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
changeSchemaChange
The change asked for.statusLiteral['ready', 'already_done', 'blocked']
readyif it would change the app,already_doneif the app already has it, andblockedif it can't be applied, which stops the whole plan.riskRisk
additive,modifying, ordestructive.summarystr
What it does, in a sentence, such asAdd field "Due" (date) to Tasks.settingsMapping[str, SettingChange]
What it would set, as{setting: SettingChange}.warningstuple[str, ...]
What it would also do or affect, such as formulas that use a deleted field.blockerstuple[str, ...]
Why it can't be applied.affected_recordsint | None
How many records it would affect, such as those that would lose a deleted field's values, if counted.linkstr | None
A Quickbase page to check it on, if there is one yet.policy_effectstuple[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
beforeAny
The setting now.afterAny
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
appliedtuple[PlannedChange, ...]
The changes made, in order.already_donetuple[PlannedChange, ...]
The changes the app already had.failedPlannedChange | None
The change that failed, if one did. The ones after it weren't attempted.not_attemptedtuple[PlannedChange, ...]
The changes after one that failed.errorstr | 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",
]