This is my Flask / Peewee / Flask-Security / Flask-Bootstrap / Flask-Babel "hello, world" du jour. Perhaps it may be useful for others, it is for me.
Flask-Peewee used to be a simple way to whip up a site with user management and object admin, but it froze in maintenance-only mode, and it doesn't play nice with application factories.
The playhouse's flask_utils provides smoother flask integration. As recommended, I've chosen Flask-Security for auth, and admin I can live without.
virtualenv -p python2 venv
source venv/bin/activate
pip install -r requirements-local.txt
Heads up: requirements.txt
contains Heroku-specific stuff
(and doesn't contain debug goodies).
./init_local.sh
creates the .env
you need, then ./rundev.sh
runs a web
server (with a debug toolbar) on port 5000
.
heroku create
heroku addons:create heroku-postgresql:hobby-dev
./init_heroku.sh
git push heroku master
heroku open
Everyting is under application/
. Stuff that isn't app-specific is under
application/sitepack/
.
Stuff directly under application/
is the
demo app and you'd probably want to
rewrite most of it, but note that application/templates/security
and
application/sitepack/templates/base.html
are symlinks (from application/
to sitepack/
and back), so if you move things around, remember to update them.
application/default_config.py
contains settings that assume you don't want
SMTP (this is intended for inhouse apps, where users don't register via web, but
rather ask an admin for an account).
See below how to enable Flask-Security's mail-related features.
You can only configure roles at application/__init__.py
(config options ares
ignored, since roles are meaningless unless there's code that uses them).
The example app comes with ['editor']
('admin'
is hardwired and you don't
need to declare it explicitly).
Note: you can add roles, but if you delete one, it doesn't get deleted from an existing database (that's a Flask-Security "feature"). You'll need to either reset the database, or perform "sql surgery" on the existing one in order to do that.
Tl;dr — if you don't want the 'editor'
role, remove it before you
run your app (you can always add it later if you change your mind).
First time you access the app via web, it initializes the auth tables and flashes a message like this (with some other random password, of course):
If you accidentally refresh the page before copy/pasting the password,
you'll need to reset your database (or at least drop the users
table).
If you use Mailgun
or SendGrid
Heroku SMTP addons,
flask-appconfig
will automagically configure your app's
Flask-Mail and you can enable
SECURITY_REGISTERABLE
and SECURITY_RECOVERABLE
right away
(with other SMTP solutions, you'll need to configure it via environment
variables / heroku config:set
).
To enable SECURITY_CONFIRMABLE
, you should first login as the initial admin,
and change to an email address you can receive mail at.
Once you enable SECURITY_CONFIRMABLE
, you should send yourself a
confirmation email via /auth/confirm
, and click on the activation link you
receive, before you can login again.
SECURITY_PASSWORDLESS
and SECURITY_TRACKABLE
are not supported, but should
be pretty easy to support.
IMHO, the sane way for site i18n is to derive locale from URL perfix (/en/
,
/he/
etc.). This way search engines know what they GET
, and tourists aren't
stuck with a foreign language interface just because the hotel's browser accepts
that language.
The only question is what to do when there isn't such a prefix. I've decided that this means staying at the previous language (and hopefully URLs become explicit again once the user clicks on navigation links).
The up side is that you don't need to monkey-patch [e.g.] Flask-Security
to
send the user to a language-specific page after login/logout/etc. but the down
side is search engine consistency. IMHO this is acceptable, since if the user
is looking for a German phrase, it is guaranteed to be found under /de
(whether it also appears under /
is left to chance. Life is a compromise).
- At
__init__.py
'screate_app()
we doBabelByUrl(app)
(instead ofBabel(app)
). This adds wsgi middleware that extracts language code from URL's path. - At
nav.py
there'sLocvalizedView()
, which is likeFlask_Nav
'sView()
but also prepends current (or suplied) language code to the path. - Templates can access
language_code
andlanguage_direction
session variables. Code that might run before the session exists, can also useget_language_code()
that fails gracefully and returns the default. - Templates and code can also use
babel_config('KEY', language_code=None)
to localize config variabless. For example: if language code ispl
,babel_config('SITE_TITLE')
would try to get theSITE_TITLE_PL
config var, and fall back toSITE_TITLE
.