Our member database application.
This is a custom database application and webinterface for membership and event registrations for PeP et al. e.V.
Hosted at registration.pep-dortmund.org.
- keep it simple
- keep it maintainable
- proven technologies > fancy stuff
- use things that we know
therefore we want to use
- poetry (dependency management, packaging, virtualenv)
- sqlite or postgresql (database)
- flask (API)
- itsdangerous (token-based authentication)
- sqlalchemy (ORM)
- if we don't know what to do: follow the flask mega tutorial
- Server-side rendering (flask templates) for the ui with a little sprinkle of client-side js and KaTeX for math
We strongly recommend to read through the first chapters of the the flask mega tutorial
-
Install python (>=3.6) and make sure you have pip
-
install poetry using
pip install poetry
, if you use your system's python, usepip install --user poetry
-
Install the dependencies using
poetry install
-
copy
env-template
to.env
and fill the variables with the appropriate information
For the mail settings, you can either use your own mail account or just use DEBUG mode, which will log email text but not actually send it.
- To initialise the database, run
$ poetry run flask db upgrade
- To populate the database with some test user
admin
with passwordtestdb
and 2 test events, run
$ poetry run python populate_database.py
- Start the server using
FLASK_DEBUG=true poetry run flask run
We use Black to have a opinionated and deterministic code style. To not forget to use black, you can install the pre-commit hook:
$ poetry run pre-commit install
This will automatically run black before you commit and aborts the commit if the code does not follow the black code style. This allows you to add the formatted files to the commit.
We are using pytest
to test our app, see https://flask.palletsprojects.com/en/1.1.x/testing/.
To run the test, use
$ poetry run python -m pytest --cov database
The test results will be printen on the console. To get an idea what tests might be missing you can also view per-line information in the browser by running
$ poetry run python -m pytest --cov database --cov-report html
$ cd htmlcov
$ python -m http.server
To authenticate to certain endpoints you need to add a user. The simplest way for now is to do this interactively in an iPython shell.
-
Fire up iPython via
poetry run ipython
-
Setup the global namespace with everything you need
In [1]: from member_database import app, db In [2]: from member_database.models import Person, User
-
Every User needs to be linked to a Person, so first you need to create a person, and store it in the database
In [3]: p = Person() In [4]: p.name = 'Albert Einstein' In [5]: p.email = '[email protected]'
-
Now you can create a User and set its password
In [6]: u = User() In [7]: u.person = p In [8]: u.username = 'aeinstein' In [9]: u.set_password('supersecurepassword') # you can even check the super secure hash of this password In [10]: u.password_hash
-
Finally store everything in the database
# this is needed to connect to the correct database In [12]: app.app_context().push() In [13]: db.session.add(p, u) In [14]: db.session.commit()
-
You can now login at the
/login
endpoint 🎉
To enable fine-grained access management for users to specific endpoints, each protected endpoint is associated with a uniquely named access level. A role combines multiple access levels and multiple roles can be assigned to different users. All access levels that are currently available will be added to the database automatically at app startup. Just like in the above example, you can fire up an ipython session and...
-
To create a new role with some access levels run
from member_database import app, db from member_database.models import Role, AccessLevel admin_role = Role(id='admin') admin_role.access_levels = [ db.session.get(AccessLevel, 'get_persons'), db.session.get(AccessLevel, 'get_members'), ] db.session.add(admin_role) db.session.commit()
-
You can simply assign a role to a user via
from member_database import app, db from member_database.models import Role, User user = User.query.filter_by(username='aeinstein').first() user.roles.append(db.session.get(Role, 'admin')) db.session.add(user) db.session.commit()
Now, the user aeinstein
will have access to the get_members
and
get_persons
access levels via the admin
role.