Skip to content
This repository was archived by the owner on Jul 20, 2024. It is now read-only.

Commit 7197d9f

Browse files
committed
Added openid, implemented 'login' and 'logout',and db migrationns
1 parent 503960d commit 7197d9f

11 files changed

+183
-11
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,6 @@ ENV/
8989
.ropeproject
9090

9191
.idea/
92+
app.db
93+
db_repository/
94+
tmp/

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1-
[![Build Status](https://travis-ci.org/juliuskrah/flask-blog.svg?branch=master)](https://travis-ci.org/juliuskrah/flask-blog)
21
# Flask Blog
2+
3+
[![Build Status](https://travis-ci.org/juliuskrah/flask-blog.svg?branch=master)](https://travis-ci.org/juliuskrah/flask-blog)
34
This blog is built using the [Flask](http://flask.pocoo.org/ "Flask Framework") Python microframework.
5+
6+
## Database migration
7+
```shell
8+
$ python db_create.py
9+
```
10+
Creates the database `app.db` and the repository folder structure `db_repository`
11+
```shell
12+
$ pyhton db_migrate.py
13+
```
14+
Migrates database changes and keeps track in `db_repository/versions`
15+
```shell
16+
$ python db_upgrade.py
17+
```
18+
This upgrades your database to the current revision
19+
```shell
20+
$ python db_downgrade.py
21+
```
22+
This downgrades your database to the previous version. This is uncommon in most scenarios

app/__init__.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1+
import os
12
from flask import Flask
2-
3+
from flask_login import LoginManager
4+
from flask_openid import OpenID
5+
from flask_sqlalchemy import SQLAlchemy
6+
from config import basedir
37

48
app = Flask(__name__)
59
app.config.from_object('config')
10+
db = SQLAlchemy(app)
11+
lm = LoginManager()
12+
lm.init_app(app)
13+
lm.login_view = 'login'
14+
oid = OpenID(app, os.path.join(basedir, 'tmp'))
615

7-
8-
from app import views # avoids circular import errors when put at the end 'cos the `views` also import `app`
16+
""" avoids circular import errors when put at the end 'cos the `views` also import `app` """
17+
from app import views, models
918

app/models.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from app import db
2+
3+
4+
class User(db.Model):
5+
id = db.Column(db.Integer, primary_key=True)
6+
nickname = db.Column(db.String(64), index=True, unique=True)
7+
email = db.Column(db.String(120), index=True, unique=True)
8+
9+
@property
10+
def is_authenticated(self):
11+
return True
12+
13+
@property
14+
def is_active(self):
15+
return True
16+
17+
@property
18+
def is_anonymous(self):
19+
return False
20+
21+
def get_id(self):
22+
try:
23+
return unicode(self.id) # python 2
24+
except NameError:
25+
return str(self.id) # python 3
26+
27+
def __repr__(self):
28+
return '<User %r>' % self.nickname
29+
30+
31+
class Post(db.Model):
32+
id = db.Column(db.Integer, primary_key=True)
33+
body = db.Column(db.String(140))
34+
timestamp = db.Column(db.DateTime)
35+
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
36+
author = db.relationship('User', backref=db.backref('posts', lazy='dynamic'))
37+
38+
def __repr__(self):
39+
return '<Post %r>' % self.body

app/templates/base.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@
99
{% endif %}
1010
</head>
1111
<body>
12-
<div>J's blog: <a href="/index">Home</a> <a href="/login">Login</a></div>
12+
<div>J's blog:
13+
<a href="{{ url_for('index') }}">Home</a>
14+
{% if g.user.is_authenticated %}
15+
| <a href="{{ url_for('logout') }}">Logout</a></div>
16+
{% else %}
17+
| <a href="{{ url_for('login') }}">Login</a></div>
18+
{% endif %}
1319
<hr>
1420
{% with messages = get_flashed_messages() %}
1521
{% if messages %}

app/views.py

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,46 @@
1-
from app import app
2-
from flask import render_template, flash, redirect
1+
from app import app, db, lm, oid
2+
from flask import render_template, flash, redirect, g, url_for, session, request
3+
from flask_login import login_user, logout_user, current_user, login_required
34
from .forms import LoginForm
5+
from .models import User
6+
7+
8+
@lm.user_loader
9+
def load_user(id):
10+
return User.query.get(int(id))
11+
12+
13+
@app.before_request
14+
def before_request():
15+
g.user = current_user
16+
17+
18+
@oid.after_login
19+
def after_login(resp):
20+
if resp.email is None or resp.email == "":
21+
flash('Invalid login. Please try again.')
22+
return redirect(url_for('login'))
23+
user = User.query.filter_by(email=resp.email).first()
24+
if user is None:
25+
nickname = resp.nickname
26+
if nickname is None or nickname == "":
27+
nickname = resp.email.split('@')[0]
28+
user = User(nickname=nickname, email=resp.email)
29+
db.session.add(user)
30+
db.session.commit()
31+
remember_me = False
32+
if 'remember_me' in session:
33+
remember_me = session['remember_me']
34+
session.pop('remember_me', None)
35+
login_user(user, remember=remember_me)
36+
return redirect(request.args.get('next') or url_for('index'))
437

538

639
@app.route('/')
740
@app.route('/index')
41+
@login_required
842
def index():
9-
user = {'nickname': 'Julius'} # username
43+
user = g.user # username
1044
posts = [
1145
{
1246
'author': {'nickname': 'John'},
@@ -24,15 +58,24 @@ def index():
2458

2559

2660
@app.route('/login', methods=['GET', 'POST'])
61+
@oid.loginhandler
2762
def login():
63+
if g.user is not None and g.user.is_authenticated:
64+
return redirect(url_for('index'))
65+
2866
form = LoginForm()
2967

3068
if form.validate_on_submit():
31-
flash('Login requested for OpenID="%s", remember_me=%s' %
32-
(form.openid.data, str(form.remember_me.data)))
33-
return redirect('/index')
69+
session['remember_me'] = form.remember_me.data
70+
return oid.try_login(form.openid.data, ask_for=['nickname', 'email'])
3471

3572
return render_template('login.html',
3673
title='Sign In',
3774
form=form,
3875
providers=app.config['OPENID_PROVIDERS'])
76+
77+
78+
@app.route('/logout')
79+
def logout():
80+
logout_user()
81+
return redirect(url_for('index'))

config.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
import os
2+
3+
basedir = os.path.abspath(os.path.dirname(__file__))
4+
5+
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.db')
6+
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
7+
SQLALCHEMY_TRACK_MODIFICATIONS = True
8+
19
WTF_CSRF_ENABLED = True
210
SECRET_KEY = '1qaz@WSX'
311

db_create.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from migrate.versioning import api
2+
from config import SQLALCHEMY_DATABASE_URI
3+
from config import SQLALCHEMY_MIGRATE_REPO
4+
from app import db
5+
import os.path
6+
7+
db.create_all()
8+
if not os.path.exists(SQLALCHEMY_MIGRATE_REPO):
9+
api.create(SQLALCHEMY_MIGRATE_REPO, 'database repository')
10+
api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
11+
else:
12+
api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, api.version(SQLALCHEMY_MIGRATE_REPO))

db_downgrade.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from migrate.versioning import api
2+
from config import SQLALCHEMY_DATABASE_URI
3+
from config import SQLALCHEMY_MIGRATE_REPO
4+
5+
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
6+
api.downgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, v - 1)
7+
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
8+
print('Current database version: ' + str(v))

db_migrate.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import types
2+
from migrate.versioning import api
3+
from app import db
4+
from config import SQLALCHEMY_DATABASE_URI
5+
from config import SQLALCHEMY_MIGRATE_REPO
6+
7+
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
8+
migration = SQLALCHEMY_MIGRATE_REPO + ('/versions/%03d_migration.py' % (v+1))
9+
tmp_module = types.ModuleType('old_model')
10+
old_module = api.create_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
11+
exec(old_module, tmp_module.__dict__)
12+
script = api.make_update_script_for_model(SQLALCHEMY_DATABASE_URI,
13+
SQLALCHEMY_MIGRATE_REPO, tmp_module.meta, db.metadata)
14+
open(migration, "wt").write(script)
15+
api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
16+
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
17+
print('New migration saved as ' + migration)
18+
print('Current database version: ' + str(v))

db_upgrade.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from migrate.versioning import api
2+
from config import SQLALCHEMY_DATABASE_URI
3+
from config import SQLALCHEMY_MIGRATE_REPO
4+
5+
api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
6+
v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
7+
print('Current database version: ' + str(v))

0 commit comments

Comments
 (0)