Browse Source

Handle database migrations

parent
commit
5da06280be

+ 16
- 0
CONTRIBUTING.md View File

@@ -77,4 +77,20 @@ If you want to add new data files, especially for public transportation stops
77 77
 3. Write a preprocessing function in `flatisfy/data_files/__init__.py`. You
78 78
    can have a look at the existing functions for a model.
79 79
 
80
+
81
+## Adding new migrations
82
+
83
+If you want to change the database schema, you should create a matching
84
+migration. Here is the way to do it correctly:
85
+
86
+1. First, edit the `flatisfy/models` files to create / remove the required
87
+   fields. If you create a new database from scratch, these are the files
88
+   which will be used.
89
+2. Then, run `alembic revision -m "Some description"` in the root of the git
90
+   repo to create a new migration.
91
+3. Finally, edit the newly created migration file under the `migrations/`
92
+   folder to add the required code to alter the database (both upgrade and
93
+   downgrade).
94
+
95
+
80 96
 Thanks!

+ 74
- 0
alembic.ini View File

@@ -0,0 +1,74 @@
1
+# A generic, single database configuration.
2
+
3
+[alembic]
4
+# path to migration scripts
5
+script_location = migrations
6
+
7
+# template used to generate migration files
8
+# file_template = %%(rev)s_%%(slug)s
9
+
10
+# timezone to use when rendering the date
11
+# within the migration file as well as the filename.
12
+# string value is passed to dateutil.tz.gettz()
13
+# leave blank for localtime
14
+# timezone =
15
+
16
+# max length of characters to apply to the
17
+# "slug" field
18
+#truncate_slug_length = 40
19
+
20
+# set to 'true' to run the environment during
21
+# the 'revision' command, regardless of autogenerate
22
+# revision_environment = false
23
+
24
+# set to 'true' to allow .pyc and .pyo files without
25
+# a source .py file to be detected as revisions in the
26
+# versions/ directory
27
+# sourceless = false
28
+
29
+# version location specification; this defaults
30
+# to migrations/versions.  When using multiple version
31
+# directories, initial revisions must be specified with --version-path
32
+# version_locations = %(here)s/bar %(here)s/bat migrations/versions
33
+
34
+# the output encoding used when revision files
35
+# are written from script.py.mako
36
+# output_encoding = utf-8
37
+
38
+sqlalchemy.url = sqlite:///data/flatisfy.db
39
+
40
+
41
+# Logging configuration
42
+[loggers]
43
+keys = root,sqlalchemy,alembic
44
+
45
+[handlers]
46
+keys = console
47
+
48
+[formatters]
49
+keys = generic
50
+
51
+[logger_root]
52
+level = WARN
53
+handlers = console
54
+qualname =
55
+
56
+[logger_sqlalchemy]
57
+level = WARN
58
+handlers =
59
+qualname = sqlalchemy.engine
60
+
61
+[logger_alembic]
62
+level = INFO
63
+handlers =
64
+qualname = alembic
65
+
66
+[handler_console]
67
+class = StreamHandler
68
+args = (sys.stderr,)
69
+level = NOTSET
70
+formatter = generic
71
+
72
+[formatter_generic]
73
+format = %(levelname)-5.5s [%(name)s] %(message)s
74
+datefmt = %H:%M:%S

+ 10
- 0
doc/0.getting_started.md View File

@@ -208,3 +208,13 @@ If you want to build the web assets, you can use `npm run build:dev`
208 208
 (respectively `npm run watch:dev` to build continuously and monitor changes in
209 209
 source files). You can use `npm run build:prod` (`npm run watch:prod`) to do
210 210
 the same in production mode (with minification etc).
211
+
212
+
213
+## Upgrading
214
+
215
+To update the app, you can simply `git pull` the latest version. The database
216
+schema might change from time to time. Here is how to update it automatically:
217
+
218
+* First, edit the `alembic.ini` file and ensure the `sqlalchemy.url` entry
219
+  points to the database URI you are actually using for Flatisfy.
220
+* Then, run `alembic upgrade head` to run the required migrations.

+ 1
- 0
migrations/README View File

@@ -0,0 +1 @@
1
+Generic single-database configuration.

+ 70
- 0
migrations/env.py View File

@@ -0,0 +1,70 @@
1
+from __future__ import with_statement
2
+from alembic import context
3
+from sqlalchemy import engine_from_config, pool
4
+from logging.config import fileConfig
5
+
6
+# this is the Alembic Config object, which provides
7
+# access to the values within the .ini file in use.
8
+config = context.config
9
+
10
+# Interpret the config file for Python logging.
11
+# This line sets up loggers basically.
12
+fileConfig(config.config_file_name)
13
+
14
+# add your model's MetaData object here
15
+# for 'autogenerate' support
16
+# from myapp import mymodel
17
+# target_metadata = mymodel.Base.metadata
18
+target_metadata = None
19
+
20
+# other values from the config, defined by the needs of env.py,
21
+# can be acquired:
22
+# my_important_option = config.get_main_option("my_important_option")
23
+# ... etc.
24
+
25
+
26
+def run_migrations_offline():
27
+    """Run migrations in 'offline' mode.
28
+
29
+    This configures the context with just a URL
30
+    and not an Engine, though an Engine is acceptable
31
+    here as well.  By skipping the Engine creation
32
+    we don't even need a DBAPI to be available.
33
+
34
+    Calls to context.execute() here emit the given string to the
35
+    script output.
36
+
37
+    """
38
+    url = config.get_main_option("sqlalchemy.url")
39
+    context.configure(
40
+        url=url, target_metadata=target_metadata, literal_binds=True)
41
+
42
+    with context.begin_transaction():
43
+        context.run_migrations()
44
+
45
+
46
+def run_migrations_online():
47
+    """Run migrations in 'online' mode.
48
+
49
+    In this scenario we need to create an Engine
50
+    and associate a connection with the context.
51
+
52
+    """
53
+    connectable = engine_from_config(
54
+        config.get_section(config.config_ini_section),
55
+        prefix='sqlalchemy.',
56
+        poolclass=pool.NullPool)
57
+
58
+    with connectable.connect() as connection:
59
+        context.configure(
60
+            connection=connection,
61
+            target_metadata=target_metadata
62
+        )
63
+
64
+        with context.begin_transaction():
65
+            context.run_migrations()
66
+
67
+if context.is_offline_mode():
68
+    run_migrations_offline()
69
+else:
70
+    run_migrations_online()

+ 24
- 0
migrations/script.py.mako View File

@@ -0,0 +1,24 @@
1
+"""${message}
2
+
3
+Revision ID: ${up_revision}
4
+Revises: ${down_revision | comma,n}
5
+Create Date: ${create_date}
6
+
7
+"""
8
+from alembic import op
9
+import sqlalchemy as sa
10
+${imports if imports else ""}
11
+
12
+# revision identifiers, used by Alembic.
13
+revision = ${repr(up_revision)}
14
+down_revision = ${repr(down_revision)}
15
+branch_labels = ${repr(branch_labels)}
16
+depends_on = ${repr(depends_on)}
17
+
18
+
19
+def upgrade():
20
+    ${upgrades if upgrades else "pass"}
21
+
22
+
23
+def downgrade():
24
+    ${downgrades if downgrades else "pass"}

+ 30
- 0
migrations/versions/8155b83242eb_add_is_expired.py View File

@@ -0,0 +1,30 @@
1
+"""Add is_expired
2
+
3
+Revision ID: 8155b83242eb
4
+Revises:
5
+Create Date: 2018-10-16 22:51:25.442678
6
+
7
+"""
8
+from alembic import op
9
+import sqlalchemy as sa
10
+
11
+
12
+# revision identifiers, used by Alembic.
13
+revision = '8155b83242eb'
14
+down_revision = None
15
+branch_labels = None
16
+depends_on = None
17
+
18
+
19
+def upgrade():
20
+    op.add_column(
21
+        'flats',
22
+        sa.Column('is_expired', sa.Boolean(), default=False)
23
+    )
24
+
25
+
26
+def downgrade():
27
+    op.drop_column(
28
+        'flats',
29
+        'is_expired'
30
+    )

+ 1
- 0
requirements.txt View File

@@ -1,3 +1,4 @@
1
+alembic
1 2
 appdirs
2 3
 arrow
3 4
 backports.csv; python_version < '3.0'