You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/development/database-migrations.md
+50Lines changed: 50 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -101,6 +101,32 @@ You can also run the `pytest-alembic` tests to further validate the newly create
101
101
pytest tests/migrations
102
102
```
103
103
104
+
## Migrations in local development (swapping between branches/downgrading)
105
+
106
+
- If you pull a branch with new migrations, you will need to restart the stack to apply the new migrations (see above).
107
+
- If you work on a branch with new migrations, and then switch back to main (or another branch without those migrations), you will need to downgrade the database to the previous migration version, and then restart the stack to apply the downgraded schema, here are the steps you can follows:
108
+
109
+
### Protocol
110
+
111
+
1. Be in the branch that has the latest migration script (as you need the downgrade function from the latest migration script so trying this from any other branch will not work).
112
+
113
+
2. Use the db-migrator container to run the downgrade command (so adjust the command/entry point in the docker compose file to something like this:
["alembic", "downgrade", "<REVISION_ID_TO_DOWNGRADE_TO>"] # or -1 to downgrade one step
118
+
```
119
+
120
+
3. Down everything
121
+
122
+
4. Undo the change to the db-migrator container in the docker compose file.
123
+
124
+
5. Swap to the other branch (e.g. main) that does not have the new migration script.
125
+
126
+
6. docker compose up/watch and it should just work as you will now be at "head" again.
127
+
128
+
The validation run by the fastapi lifespan event on startup will help tell you if you succeded or not, as it will error if you are not at the head revision for that branch. So if fastapi is working, it's probably working.
129
+
104
130
## Production Deployment
105
131
106
132
Documentation on how to run migrations in production/deployed environments is covered in our [private repository, argocd-divbase](https://github.com/ScilifelabDataCentre/argocd-divbase).
@@ -121,3 +147,27 @@ Documentation on how to run migrations in production/deployed environments is co
121
147
### Starlette admin/admin panel not showing the models
122
148
123
149
- You need the models in your src to match the postgres schema. So if you have pending changes (that you have or have not created migrations for) they won't display until you've actually done the migration.
150
+
151
+
### Migrations that include new fields with non-nullable constraints
152
+
153
+
One way to solve this is to modify the migration scripts upgrade command as follows:
154
+
155
+
In this example, we're adding a new column "organisation" to the user table, and it cannot be null (and we already have users...), so we need to provide a default value for existing rows.
156
+
157
+
```python
158
+
def upgrade() -> None:
159
+
# Add the server_default param to the add operation for the new column
160
+
# "server_default=sa.text("'Not specified'") was added to below command
161
+
# We use sa.text to ensure the default value is set as a string in the database,
162
+
# see e.g. here for why: https://github.com/sqlalchemy/alembic/discussions/1433
# Any rows in the database table that existed before the migration will now be populated with the default value 'Not specified'.
166
+
# At the end of the same migration script, remove the server_default param from the column (with `server_default=None`) - so our models match exactly with the database schema.
167
+
# New rows will now require the application to provide a value for the organisation column.
**You don't need to set the server_default in the models.py, just in the migration script.**
172
+
173
+
Alternatively you could set the server_default in the models.py, but then you should be comfortable with it always having a default value. There are also other ways to solve this problem...
Helper to resolve "Other" fields in registration/profile update forms.
127
+
These fields have a dropdown with predefined options and an "Other" option that reveals a text input.
128
+
If "Other" selected, we take the value from the text input instead. If the text input is empty or too short, we return None to indicate an error (e.g. in the registration/profile update endpoint), otherwise we return the resolved value. If "Other" is not selected, we just return the original value.
129
+
130
+
If non-resolvable, returns None, so calling function can return an error response.
0 commit comments