Adding migragtions changing test fixtures
parent
7458224fa1
commit
ebc198c431
@ -1,3 +1,64 @@
|
||||
# {{ cookiecutter.project_name }}
|
||||
|
||||
{{cookiecutter.description}}
|
||||
|
||||
## First time setup
|
||||
|
||||
Create a virtual environment and activate it. Now from the root project directory run `./scripts/bootstrap`. This will install `pip-tools` and sync any dependencies for the first time.
|
||||
|
||||
To run the app you will need a [postgres] database. Create a development and a test database. Update the connection strings within the `{{cookiecutter.project_slug}}.settings.toml`. At this time, if you choose to, you can remove the demo `Todo` code and replace it with your own Model. Otherwise create your first [alembic] migration using the `alembic revision --autogenerate -m "your revision message"` command. Finally, apply your first migration with `alembic upgrade head`.
|
||||
|
||||
|
||||
## Running the developement server
|
||||
A `manage.py` script has been included with a collection of [click] cli functions to assist in development.
|
||||
|
||||
__Note__: the developement server command is not a production webserver. You will need to c
|
||||
|
||||
```
|
||||
python manage.py runserver
|
||||
```
|
||||
|
||||
## Using the interactive interpreter
|
||||
The `manage.py` script can be used to open an interactive interpreter with a configured molten application from your project.
|
||||
```
|
||||
python manage.py shell
|
||||
```
|
||||
|
||||
## Dependency management
|
||||
|
||||
Dependencies are managed via [pip-tools].
|
||||
|
||||
### Adding a dependency
|
||||
|
||||
To add a dependency, edit `requirements.in` (or `dev_requirements.in`
|
||||
for dev dependencies) and add your dependency then run `pip-compile
|
||||
requirements.in`.
|
||||
|
||||
### Syncing dependencies
|
||||
|
||||
Run `pip-sync requirements.txt dev_requirements.txt`.
|
||||
|
||||
|
||||
## Migrations
|
||||
|
||||
Migrations are managed using [alembic].
|
||||
|
||||
### Generating new migrations
|
||||
|
||||
alembic revision --autogenerate -m 'message'
|
||||
|
||||
### Running the migrations
|
||||
|
||||
alembic upgrade head # to upgrade the local db
|
||||
env ENVIRONMENT=test alembic upgrade head # to upgrade the test db
|
||||
env ENVIRONMENT=prod alembic upgrade head # to upgrade prod
|
||||
|
||||
## Testing
|
||||
|
||||
Run the tests by invoking `py.test` in the project root. Make sure you
|
||||
run any pending migrations beforehand.
|
||||
|
||||
[alembic]: http://alembic.zzzcomputing.com/en/latest/
|
||||
[click]: https://click.palletsprojects.com
|
||||
[pip-tools]: https://github.com/jazzband/pip-tools
|
||||
[postgres]: https://www.postgresql.org/
|
@ -0,0 +1 @@
|
||||
Generic single-database configuration.
|
@ -0,0 +1,24 @@
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = ${repr(up_revision)}
|
||||
down_revision = ${repr(down_revision)}
|
||||
branch_labels = ${repr(branch_labels)}
|
||||
depends_on = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade():
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade():
|
||||
${downgrades if downgrades else "pass"}
|
@ -1,42 +1,51 @@
|
||||
import pytest
|
||||
|
||||
from molten import testing
|
||||
from molten.contrib.sqlalchemy import EngineData
|
||||
from molten.contrib.sqlalchemy import Session
|
||||
|
||||
from {{cookiecutter.project_slug}}.index import create_app
|
||||
from {{cookiecutter.project_slug}}.db import Base
|
||||
|
||||
|
||||
def truncate_all_tables(session: Session):
|
||||
table_names = session.execute("""
|
||||
select table_name from information_schema.tables
|
||||
where table_schema = 'public'
|
||||
and table_type = 'BASE TABLE'
|
||||
and table_name != 'alembic_version'
|
||||
""")
|
||||
for (table_name,) in table_names:
|
||||
# "truncate" can deadlock so we use delete which is guaranteed not to.
|
||||
session.execute(f"delete from {table_name}")
|
||||
session.commit()
|
||||
|
||||
# requires function scope so that database is removed on every tests
|
||||
@pytest.fixture(scope="function")
|
||||
def app():
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def app_global():
|
||||
_, app = create_app()
|
||||
yield app
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def create_db(app):
|
||||
"""Creates a test database with session scope"""
|
||||
def _retrieve_engine(engine_data: EngineData):
|
||||
return engine_data.engine
|
||||
|
||||
engine = app.injector.get_resolver().resolve(_retrieve_engine)()
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
@pytest.fixture
|
||||
def app(app_global):
|
||||
# This is a little "clever"/piggy. We only want a single instance
|
||||
# of the app to ever be created, but we also want to ensure that
|
||||
# the DB is cleared after every test hence "app_global" being a
|
||||
# session-scoped fixture and this one being test-scoped.
|
||||
yield app_global
|
||||
resolver = app_global.injector.get_resolver()
|
||||
resolver.resolve(truncate_all_tables)()
|
||||
|
||||
yield
|
||||
|
||||
Base.metadata.drop_all(bind=engine)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
@pytest.fixture
|
||||
def client(app):
|
||||
"""Creates a testing client"""
|
||||
return testing.TestClient(app)
|
||||
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def session():
|
||||
pass
|
||||
@pytest.fixture
|
||||
def load_component(app):
|
||||
def load(annotation):
|
||||
def loader(c: annotation):
|
||||
return c
|
||||
return app.injector.get_resolver().resolve(loader)()
|
||||
return load
|
||||
|
Loading…
Reference in New Issue