add babel to sqla example and split into separate modules

parent 0fca4043
......@@ -9,7 +9,7 @@ To run this example:
2. Create and activate a virtual environment::
virtualenv env
virtualenv env -p python3
source env/bin/activate
3. Install requirements::
......@@ -18,10 +18,7 @@ To run this example:
4. Run the application::
python examples/sqla/app.py
python examples/sqla/run_server.py
The first time you run this example, a sample sqlite database gets populated automatically. To suppress this behaviour,
comment the following lines in app.py:::
if not os.path.exists(database_path):
build_sample_db()
The first time you run this example, a sample sqlite database gets populated automatically. To start
with a fresh database: `rm examples/sqla/admin/sample_db.sqlite`, and then restart the application.
from flask import Flask, request, session
from flask_sqlalchemy import SQLAlchemy
from flask_babelex import Babel
app = Flask(__name__)
app.config.from_pyfile('config.py')
db = SQLAlchemy(app)
# Initialize babel
babel = Babel(app)
@babel.localeselector
def get_locale():
override = request.args.get('lang')
if override:
session['lang'] = override
return session.get('lang', 'en')
import admin.main
# set optional bootswatch theme
# see http://bootswatch.com/3/ for available swatches
FLASK_ADMIN_SWATCH = 'cerulean'
# Create dummy secrey key so we can use sessions
SECRET_KEY = '123456790'
# Create in-memory database
DATABASE_FILE = 'sample_db.sqlite'
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + DATABASE_FILE
SQLALCHEMY_ECHO = True
SQLALCHEMY_TRACK_MODIFICATIONS = False
from admin import db
from admin.models import User, Post, Tag, Tree, AVAILABLE_USER_TYPES
import random
import datetime
def build_sample_db():
"""
Populate a small db with some example entries.
"""
db.drop_all()
db.create_all()
# Create sample Users
first_names = [
'Harry', 'Amelia', 'Oliver', 'Jack', 'Isabella', 'Charlie', 'Sophie', 'Mia',
'Jacob', 'Thomas', 'Emily', 'Lily', 'Ava', 'Isla', 'Alfie', 'Olivia', 'Jessica',
'Riley', 'William', 'James', 'Geoffrey', 'Lisa', 'Benjamin', 'Stacey', 'Lucy'
]
last_names = [
'Brown', 'Brown', 'Patel', 'Jones', 'Williams', 'Johnson', 'Taylor', 'Thomas',
'Roberts', 'Khan', 'Clarke', 'Clarke', 'Clarke', 'James', 'Phillips', 'Wilson',
'Ali', 'Mason', 'Mitchell', 'Rose', 'Davis', 'Davies', 'Rodriguez', 'Cox', 'Alexander'
]
countries = [
("ZA", "South Africa", 27, "ZAR", "Africa/Johannesburg"),
("BF", "Burkina Faso", 226, "XOF", "Africa/Ouagadougou"),
("US", "United States of America", 1, "USD", "America/New_York"),
("BR", "Brazil", 55, "BRL", "America/Sao_Paulo"),
("TZ", "Tanzania", 255, "TZS", "Africa/Dar_es_Salaam"),
("DE", "Germany", 49, "EUR", "Europe/Berlin"),
("CN", "China", 86, "CNY", "Asia/Shanghai"),
]
user_list = []
for i in range(len(first_names)):
user = User()
country = random.choice(countries)
user.type = random.choice(AVAILABLE_USER_TYPES)[0]
user.first_name = first_names[i]
user.last_name = last_names[i]
user.email = first_names[i].lower() + "@example.com"
user.website = "https://www.example.com"
user.ip_address = "127.0.0.1"
user.coutry = country[1]
user.currency = country[3]
user.timezone = country[4]
user.dialling_code = country[2]
user.local_phone_number = '0' + ''.join(random.choices('123456789', k=9))
user_list.append(user)
db.session.add(user)
# Create sample Tags
tag_list = []
for tmp in ["YELLOW", "WHITE", "BLUE", "GREEN", "RED", "BLACK", "BROWN", "PURPLE", "ORANGE"]:
tag = Tag()
tag.name = tmp
tag_list.append(tag)
db.session.add(tag)
# Create sample Posts
sample_text = [
{
'title': "de Finibus Bonorum et Malorum - Part I",
'content': "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor \
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud \
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure \
dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt \
mollit anim id est laborum."
},
{
'title': "de Finibus Bonorum et Malorum - Part II",
'content': "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque \
laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto \
beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur \
aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi \
nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, \
adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam \
aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam \
corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum \
iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum \
qui dolorem eum fugiat quo voluptas nulla pariatur?"
},
{
'title': "de Finibus Bonorum et Malorum - Part III",
'content': "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium \
voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati \
cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id \
est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam \
libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod \
maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. \
Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet \
ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur \
a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis \
doloribus asperiores repellat."
}
]
for user in user_list:
entry = random.choice(sample_text) # select text at random
post = Post()
post.user = user
post.title = "{}'s opinion on {}".format(user.first_name, entry['title'])
post.text = entry['content']
post.background_color = random.choice(["#cccccc", "red", "lightblue", "#0f0"])
tmp = int(1000 * random.random()) # random number between 0 and 1000:
post.date = datetime.datetime.now() - datetime.timedelta(days=tmp)
post.tags = random.sample(tag_list, 2) # select a couple of tags at random
db.session.add(post)
# Create a sample Tree structure
trunk = Tree(name="Trunk")
db.session.add(trunk)
for i in range(5):
branch = Tree()
branch.name = "Branch " + str(i + 1)
branch.parent = trunk
db.session.add(branch)
for j in range(5):
leaf = Tree()
leaf.name = "Leaf " + str(j + 1)
leaf.parent = branch
db.session.add(leaf)
db.session.commit()
return
import os
import os.path as op
from flask import Flask, Markup
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import composite
from sqlalchemy import sql, cast
import uuid
import random
import string
from admin import app, db
from admin.models import AVAILABLE_USER_TYPES, User, Post, Tag, Tree
from flask import Markup
from wtforms import validators
......@@ -15,140 +8,27 @@ import flask_admin as admin
from flask_admin.base import MenuLink
from flask_admin.contrib import sqla
from flask_admin.contrib.sqla import filters
from flask_admin.contrib.sqla.form import InlineModelConverter
from flask_admin.contrib.sqla.fields import InlineModelFormList
from flask_admin.contrib.sqla.filters import BaseSQLAFilter, FilterEqual
from flask_admin.babel import gettext
from sqlalchemy_utils import ChoiceType, EmailType, UUIDType, URLType, CurrencyType, Currency
from colour import Color
from sqlalchemy_utils import ColorType, ArrowType, IPAddressType, TimezoneType
import arrow
import enum
# Create application
app = Flask(__name__)
# set optional bootswatch theme
# see http://bootswatch.com/3/ for available swatches
app.config['FLASK_ADMIN_SWATCH'] = 'cerulean'
# Create dummy secrey key so we can use sessions
app.config['SECRET_KEY'] = '123456790'
# Create in-memory database
app.config['DATABASE_FILE'] = 'sample_db.sqlite'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + app.config['DATABASE_FILE']
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
class EnumChoices(enum.Enum):
first = 1
second = 2
AVAILABLE_USER_TYPES = [
(u'admin', u'Admin'),
(u'content-writer', u'Content writer'),
(u'editor', u'Editor'),
(u'regular-user', u'Regular user'),
]
# Create models
class User(db.Model):
id = db.Column(UUIDType(binary=False), default=uuid.uuid4, primary_key=True)
# use a regular string field, for which we can specify a list of available choices later on
type = db.Column(db.String(100))
# fixed choices can be handled in a number of different ways:
enum_choice_field = db.Column(db.Enum(EnumChoices), nullable=True)
sqla_utils_choice_field = db.Column(ChoiceType(AVAILABLE_USER_TYPES), nullable=True)
sqla_utils_enum_choice_field = db.Column(ChoiceType(EnumChoices, impl=db.Integer()), nullable=True)
first_name = db.Column(db.String(100))
last_name = db.Column(db.String(100))
# some sqlalchemy_utils data types (see https://sqlalchemy-utils.readthedocs.io/)
email = db.Column(EmailType, unique=True, nullable=False)
website = db.Column(URLType)
ip_address = db.Column(IPAddressType)
currency = db.Column(CurrencyType, nullable=True, default=None)
timezone = db.Column(TimezoneType(backend='pytz'))
dialling_code = db.Column(db.Integer())
local_phone_number = db.Column(db.String(10))
featured_post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
featured_post = db.relationship('Post', foreign_keys=[featured_post_id])
@hybrid_property
def phone_number(self):
if self.dialling_code and self.local_phone_number:
number = str(self.local_phone_number)
return "+{} ({}) {} {} {}".format(self.dialling_code, number[0], number[1:3], number[3:6], number[6::])
return
@phone_number.expression
def phone_number(cls):
return sql.operators.ColumnOperators.concat(cast(cls.dialling_code, db.String), cls.local_phone_number)
def __str__(self):
return "{}, {}".format(self.last_name, self.first_name)
def __repr__(self):
return "{}: {}".format(self.id, self.__str__())
# Create M2M table
post_tags_table = db.Table('post_tags', db.Model.metadata,
db.Column('post_id', db.Integer, db.ForeignKey('post.id')),
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(120))
text = db.Column(db.Text, nullable=False)
date = db.Column(db.Date)
# some sqlalchemy_utils data types (see https://sqlalchemy-utils.readthedocs.io/)
background_color = db.Column(ColorType)
created_at = db.Column(ArrowType, default=arrow.utcnow())
user_id = db.Column(UUIDType(binary=False), db.ForeignKey(User.id))
user = db.relationship(User, foreign_keys=[user_id], backref='posts')
tags = db.relationship('Tag', secondary=post_tags_table)
def __str__(self):
return "{}".format(self.title)
class Tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Unicode(64), unique=True)
def __str__(self):
return "{}".format(self.name)
class Tree(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64))
# recursive relationship
parent_id = db.Column(db.Integer, db.ForeignKey('tree.id'))
parent = db.relationship('Tree', remote_side=[id], backref='children')
def __str__(self):
return "{}".format(self.name)
# Flask views
@app.route('/')
def index():
return '<a href="/admin/">Click me to get to Admin!</a>'
tmp = u"""
<p><a href="/admin/?lang=en">Click me to get to Admin! (English)</a></p>
<p><a href="/admin/?lang=cs">Click me to get to Admin! (Czech)</a></p>
<p><a href="/admin/?lang=de">Click me to get to Admin! (German)</a></p>
<p><a href="/admin/?lang=es">Click me to get to Admin! (Spanish)</a></p>
<p><a href="/admin/?lang=fa">Click me to get to Admin! (Farsi)</a></p>
<p><a href="/admin/?lang=fr">Click me to get to Admin! (French)</a></p>
<p><a href="/admin/?lang=pt">Click me to get to Admin! (Portuguese)</a></p>
<p><a href="/admin/?lang=ru">Click me to get to Admin! (Russian)</a></p>
<p><a href="/admin/?lang=pa">Click me to get to Admin! (Punjabi)</a></p>
<p><a href="/admin/?lang=zh_CN">Click me to get to Admin! (Chinese - Simplified)</a></p>
<p><a href="/admin/?lang=zh_TW">Click me to get to Admin! (Chinese - Traditional)</a></p>
"""
return tmp
# Custom filter class
......@@ -164,13 +44,15 @@ class FilterLastNameBrown(BaseSQLAFilter):
# Customized User model admin
def phone_number_formatter (view, context, model, name):
def phone_number_formatter(view, context, model, name):
return Markup("<nobr>{}</nobr>".format(model.phone_number)) if model.phone_number else None
def is_numberic_validator(form, field):
if field.data and not field.data.isdigit():
raise validators.ValidationError(gettext('Only numbers are allowed.'))
class UserAdmin(sqla.ModelView):
can_view_details = True # show a modal dialog with records details
......@@ -187,8 +69,8 @@ class UserAdmin(sqla.ModelView):
},
}
form_widget_args = {
'id':{
'readonly':True
'id': {
'readonly': True
}
}
column_list = [
......@@ -265,10 +147,11 @@ class UserAdmin(sqla.ModelView):
form.featured_post.query_factory = lambda: Post.query.filter(Post.user_id == form._obj.id).all()
return form
# Customized Post model admin
class PostAdmin(sqla.ModelView):
column_display_pk = True
column_list = ['id', 'user', 'title', 'date', 'tags', 'background_color', 'created_at',]
column_list = ['id', 'user', 'title', 'date', 'tags', 'background_color', 'created_at', ]
column_editable_list = ['background_color', ]
column_default_sort = ('date', True)
create_modal = True
......@@ -346,12 +229,13 @@ class TreeView(sqla.ModelView):
'parent',
]
form_excluded_columns = ['children', ]
column_filters = ['id', 'name', 'parent',]
column_filters = ['id', 'name', 'parent', ]
# override the 'render' method to pass your own parameters to the template
def render(self, template, **kwargs):
return super(TreeView, self).render(template, foo="bar", **kwargs)
# Create admin
admin = admin.Admin(app, name='Example: SQLAlchemy', template_mode='bootstrap3')
......@@ -363,145 +247,3 @@ admin.add_view(TreeView(Tree, db.session, category="Other"))
admin.add_sub_category(name="Links", parent_name="Other")
admin.add_link(MenuLink(name='Back Home', url='/', category='Links'))
admin.add_link(MenuLink(name='External link', url='http://www.example.com/', category='Links'))
def build_sample_db():
"""
Populate a small db with some example entries.
"""
import random
import datetime
db.drop_all()
db.create_all()
# Create sample Users
first_names = [
'Harry', 'Amelia', 'Oliver', 'Jack', 'Isabella', 'Charlie', 'Sophie', 'Mia',
'Jacob', 'Thomas', 'Emily', 'Lily', 'Ava', 'Isla', 'Alfie', 'Olivia', 'Jessica',
'Riley', 'William', 'James', 'Geoffrey', 'Lisa', 'Benjamin', 'Stacey', 'Lucy'
]
last_names = [
'Brown', 'Brown', 'Patel', 'Jones', 'Williams', 'Johnson', 'Taylor', 'Thomas',
'Roberts', 'Khan', 'Clarke', 'Clarke', 'Clarke', 'James', 'Phillips', 'Wilson',
'Ali', 'Mason', 'Mitchell', 'Rose', 'Davis', 'Davies', 'Rodriguez', 'Cox', 'Alexander'
]
countries = [
("ZA", "South Africa", 27, "ZAR", "Africa/Johannesburg"),
("BF", "Burkina Faso", 226, "XOF", "Africa/Ouagadougou"),
("US", "United States of America", 1, "USD", "America/New_York"),
("BR", "Brazil", 55, "BRL", "America/Sao_Paulo"),
("TZ", "Tanzania", 255, "TZS", "Africa/Dar_es_Salaam"),
("DE", "Germany", 49, "EUR", "Europe/Berlin"),
("CN", "China", 86, "CNY", "Asia/Shanghai"),
]
user_list = []
for i in range(len(first_names)):
user = User()
country = random.choice(countries)
user.type = random.choice(AVAILABLE_USER_TYPES)[0]
user.first_name = first_names[i]
user.last_name = last_names[i]
user.email = first_names[i].lower() + "@example.com"
user.website = "https://www.example.com"
user.ip_address = "127.0.0.1"
user.coutry = country[1]
user.currency = country[3]
user.timezone = country[4]
user.dialling_code = country[2]
user.local_phone_number = '0' + ''.join(random.choices('123456789', k=9))
user_list.append(user)
db.session.add(user)
# Create sample Tags
tag_list = []
for tmp in ["YELLOW", "WHITE", "BLUE", "GREEN", "RED", "BLACK", "BROWN", "PURPLE", "ORANGE"]:
tag = Tag()
tag.name = tmp
tag_list.append(tag)
db.session.add(tag)
# Create sample Posts
sample_text = [
{
'title': "de Finibus Bonorum et Malorum - Part I",
'content': "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor \
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud \
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure \
dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. \
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt \
mollit anim id est laborum."
},
{
'title': "de Finibus Bonorum et Malorum - Part II",
'content': "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque \
laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto \
beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur \
aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi \
nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, \
adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam \
aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam \
corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum \
iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum \
qui dolorem eum fugiat quo voluptas nulla pariatur?"
},
{
'title': "de Finibus Bonorum et Malorum - Part III",
'content': "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium \
voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati \
cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id \
est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam \
libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod \
maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. \
Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet \
ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur \
a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis \
doloribus asperiores repellat."
}
]
for user in user_list:
entry = random.choice(sample_text) # select text at random
post = Post()
post.user = user
post.title = "{}'s opinion on {}".format(user.first_name, entry['title'])
post.text = entry['content']
post.background_color = random.choice(["#cccccc", "red", "lightblue", "#0f0"])
tmp = int(1000*random.random()) # random number between 0 and 1000:
post.date = datetime.datetime.now() - datetime.timedelta(days=tmp)
post.tags = random.sample(tag_list, 2) # select a couple of tags at random
db.session.add(post)
# Create a sample Tree structure
trunk = Tree(name="Trunk")
db.session.add(trunk)
for i in range(5):
branch = Tree()
branch.name = "Branch " + str(i+1)
branch.parent = trunk
db.session.add(branch)
for j in range(5):
leaf = Tree()
leaf.name = "Leaf " + str(j+1)
leaf.parent = branch
db.session.add(leaf)
db.session.commit()
return
if __name__ == '__main__':
# Build a sample db on the fly, if one does not exist yet.
app_dir = op.realpath(os.path.dirname(__file__))
database_path = op.join(app_dir, app.config['DATABASE_FILE'])
if not os.path.exists(database_path):
build_sample_db()
# Start app
app.run(debug=True)
from admin import db
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy import sql, cast
import uuid
from sqlalchemy_utils import ChoiceType, EmailType, UUIDType, URLType, CurrencyType
from sqlalchemy_utils import ColorType, ArrowType, IPAddressType, TimezoneType
import arrow
import enum
AVAILABLE_USER_TYPES = [
(u'admin', u'Admin'),
(u'content-writer', u'Content writer'),
(u'editor', u'Editor'),
(u'regular-user', u'Regular user'),
]
class EnumChoices(enum.Enum):
first = 1
second = 2
# Create models
class User(db.Model):
id = db.Column(UUIDType(binary=False), default=uuid.uuid4, primary_key=True)
# use a regular string field, for which we can specify a list of available choices later on
type = db.Column(db.String(100))
# fixed choices can be handled in a number of different ways:
enum_choice_field = db.Column(db.Enum(EnumChoices), nullable=True)
sqla_utils_choice_field = db.Column(ChoiceType(AVAILABLE_USER_TYPES), nullable=True)
sqla_utils_enum_choice_field = db.Column(ChoiceType(EnumChoices, impl=db.Integer()), nullable=True)
first_name = db.Column(db.String(100))
last_name = db.Column(db.String(100))
# some sqlalchemy_utils data types (see https://sqlalchemy-utils.readthedocs.io/)
email = db.Column(EmailType, unique=True, nullable=False)
website = db.Column(URLType)
ip_address = db.Column(IPAddressType)
currency = db.Column(CurrencyType, nullable=True, default=None)
timezone = db.Column(TimezoneType(backend='pytz'))
dialling_code = db.Column(db.Integer())
local_phone_number = db.Column(db.String(10))
featured_post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
featured_post = db.relationship('Post', foreign_keys=[featured_post_id])
@hybrid_property
def phone_number(self):
if self.dialling_code and self.local_phone_number:
number = str(self.local_phone_number)
return "+{} ({}) {} {} {}".format(self.dialling_code, number[0], number[1:3], number[3:6], number[6::])
return
@phone_number.expression
def phone_number(cls):
return sql.operators.ColumnOperators.concat(cast(cls.dialling_code, db.String), cls.local_phone_number)
def __str__(self):
return "{}, {}".format(self.last_name, self.first_name)
def __repr__(self):
return "{}: {}".format(self.id, self.__str__())
# Create M2M table
post_tags_table = db.Table('post_tags', db.Model.metadata,
db.Column('post_id', db.Integer, db.ForeignKey('post.id')),
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(120))
text = db.Column(db.Text, nullable=False)
date = db.Column(db.Date)
# some sqlalchemy_utils data types (see https://sqlalchemy-utils.readthedocs.io/)
background_color = db.Column(ColorType)
created_at = db.Column(ArrowType, default=arrow.utcnow())
user_id = db.Column(UUIDType(binary=False), db.ForeignKey(User.id))
user = db.relationship(User, foreign_keys=[user_id], backref='posts')
tags = db.relationship('Tag', secondary=post_tags_table)
def __str__(self):
return "{}".format(self.title)
class Tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Unicode(64), unique=True)
def __str__(self):
return "{}".format(self.name)
class Tree(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64))
# recursive relationship
parent_id = db.Column(db.Integer, db.ForeignKey('tree.id'))
parent = db.relationship('Tree', remote_side=[id], backref='children')
def __str__(self):
return "{}".format(self.name)
......@@ -4,7 +4,7 @@
<div class="container">
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<h1>Flask-Admin example</h1>
<h1>Flask-Admin {{ _gettext('example') }}</h1>
<p class="lead">
Basic SQLAlchemy model views.
</p>
......@@ -14,7 +14,7 @@
<p>
The views are generated automatically, but it is perfectly possible to customize them to suit your needs.
</p>
<a class="btn btn-primary" href="/"><i class="glyphicon glyphicon-chevron-left"></i> Back</a>
<a class="btn btn-primary" href="/"><i class="glyphicon glyphicon-chevron-left"></i> {{ _gettext('Back') }}</a>
</div>
</div>
</div>
......
from admin import app
from admin.data import build_sample_db
import os
import os.path as op
# Build a sample db on the fly, if one does not exist yet.
app_dir = op.join(op.realpath(os.path.dirname(__file__)), 'admin')
database_path = op.join(app_dir, app.config['DATABASE_FILE'])
if not os.path.exists(database_path):
build_sample_db()
# Start app
app.run(debug=True)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment