Преглед на файлове

Add database setup with SQLalchemy, alembic

Maarten van den Berg преди 6 години
родител
ревизия
eff1514a0d

+ 74 - 0
piket_server/database/alembic.ini

@@ -0,0 +1,74 @@
1
+# A generic, single database configuration.
2
+
3
+[alembic]
4
+# path to migration scripts
5
+script_location = alembic
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 alembic/versions.  When using multiple version
31
+# directories, initial revisions must be specified with --version-path
32
+# version_locations = %(here)s/bar %(here)s/bat alembic/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 = driver://user:pass@localhost/dbname
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

+ 1 - 0
piket_server/database/alembic/README

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

+ 79 - 0
piket_server/database/alembic/env.py

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

+ 24 - 0
piket_server/database/alembic/script.py.mako

@@ -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"}

+ 28 - 0
piket_server/database/alembic/versions/a09086bfe84c_create_persons_table.py

@@ -0,0 +1,28 @@
1
+"""Create persons table
2
+
3
+Revision ID: a09086bfe84c
4
+Revises: 
5
+Create Date: 2018-08-19 21:03:55.857085
6
+
7
+"""
8
+from alembic import op
9
+import sqlalchemy as sa
10
+
11
+
12
+# revision identifiers, used by Alembic.
13
+revision = 'a09086bfe84c'
14
+down_revision = None
15
+branch_labels = None
16
+depends_on = None
17
+
18
+
19
+def upgrade():
20
+    op.create_table(
21
+        'people',
22
+        sa.Column('person_id', sa.Integer, primary_key=True),
23
+        sa.Column('name', sa.String, nullable=False)
24
+    )
25
+
26
+
27
+def downgrade():
28
+    op.drop_table('people')

+ 20 - 0
piket_server/database/schema.py

@@ -0,0 +1,20 @@
1
+"""
2
+Defines the database schema for the backend.
3
+"""
4
+
5
+from sqlalchemy import Column, Integer, String
6
+from sqlalchemy.ext.declarative import declarative_base
7
+
8
+
9
+BASE = declarative_base()
10
+
11
+
12
+class Person(BASE):
13
+    ''' Represents a person to be shown on the lists. '''
14
+    __tablename__ = 'people'
15
+
16
+    person_id = Column(Integer, primary_key=True)
17
+    name = Column(String, nullable=False)
18
+
19
+    def __repr__(self):
20
+        return f"<Person(id={self.person_id}, name={self.name})>"

+ 10 - 4
setup.py

@@ -19,10 +19,6 @@ setup(
19 19
         packages=['piket_client', 'piket_server'],
20 20
 
21 21
         install_requires=[
22
-            'PySide2',
23
-            'simpleaudio',
24
-            'requests',
25
-            'Flask',
26 22
         ],
27 23
 
28 24
         extras_require={
@@ -30,6 +26,16 @@ setup(
30 26
                 'black',
31 27
                 'pylint',
32 28
             ],
29
+            'server': [
30
+                'Flask',
31
+                'SQLAlchemy',
32
+                'alembic',
33
+            ],
34
+            'client': [
35
+                'PySide2',
36
+                'simpleaudio',
37
+                'requests',
38
+            ],
33 39
         },
34 40
 
35 41
         include_package_data=True,