flask_bootstrap
- 如何在flask中使用Boostrap?
要想在程序中集成Bootstrap,显然要对模板做所有必要的改动。不过,更简单的方法是使用一个名为Flask-Bootstrap 的Flask 扩展,简化集成的过程。Flask-Bootstrap 使用pip安装:
pip install flask_bootstrap
- Flask 扩展一般都在创建程序实例时初始化,下面是Flask_Bootstrap的初始化方法:
from flask_bootstrap import Bootstrap
app = Flask(__name__)
bootstrap = Bootstrap(app)
初始化Flask-Bootstrap 之后,就可以在程序中使用一个包含所有Bootstrap 文件的基模板。这个模板利用Jinja2 的模板继承机制,让程序扩展一个具有基本页面结构的基模板,其中就有用来引入Bootstrap 的元素。
- 如何引用bootatrap的基模板?
{%extends "bootstrap/base.html"%}
{%block title %}Flask{% endblock %}
这两个块分别表示页面中的导航条和主体内容。在这个模板中,navbar 块使用Bootstrap 组件定义了一个简单的导航条。content 块中有个
-
Flask-Bootstrap定义的其他可用块:
参考链接: https://pythonhosted.org/Flask-Bootstrap/basic-usage.html#available-blocks
块名 说明
doc 整个html文档
html_attribs html标签属性
html html标签中的内容
head head标签中的内容
title title标签中的内容
metas 一组meta标签
styles 层叠样式表定义
body_attribs body标签的属性
body body标签中的内容
navbar 用户定义的导航条
content 用户定义的页面内容
scripts 文档底部的JavaScript 声明 -
如何继承原有内容:
上表中的很多块都是Flask-Bootstrap 自用的,如果直接重定义可能会导致一些问题。例如,Bootstrap 所需的文件在styles 和scripts 块中声明。如果程序需要向已经有内容的块中添加新内容,必须使用Jinja2 提供的super() 函数。例如,如果要在衍生模板中添加新的JavaScript 文件,需要这么定义scripts 块:
{% block scripts %}
{{ super() }}
<script type="text/javascript" src="my-script.js"></script>
{% endblock %}
首先了解一下bootstrap提供了多少block,bootstrap中的base.html如下:
{% block doc -%} ###doc:整个HTML文档(开始)
<!DOCTYPE html>
<html{% block html_attribs %}{% endblock html_attribs %}>###html_attribs:<html>标签的属性
{%- block html %} ###html:<html>标签中的内容(开始)
<head>
{%- block head %} ###head:<head>标签中的内容(开始)
<title>{% block title %}{{title|default}}{% endblock title %}</title> ###title:<title>标签中的内容
{%- block metas %} ###metas:一组<meta>标签
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{%- endblock metas %}
{%- block styles %} ##styles:层叠样式表定义
<!-- Bootstrap -->
<link href="{{bootstrap_find_resource('css/bootstrap.css', cdn='bootstrap')}}" rel="stylesheet">
{%- endblock styles %}
{%- endblock head %} ###head:<head>标签中的内容(结束)
</head> ###body_attribs:<body>标签的属性
<body{% block body_attribs %}{% endblock body_attribs %}>
{% block body -%} ###body:<body>标签中的内容(开始)
{% block navbar %} ###navbar:用户定义的导航条
{%- endblock navbar %}
{% block content -%} ###content:用户定义的页面内容
{%- endblock content %}
{% block scripts %} ###scripts:文档底部的JavaScript声明
<script src="{{bootstrap_find_resource('jquery.js', cdn='jquery')}}"></script>
<script src="{{bootstrap_find_resource('js/bootstrap.js', cdn='bootstrap')}}"></script>
{%- endblock scripts %}
{%- endblock body %} ###body:<body>标签中的内容(结束)
</body>
{%- endblock html %} ###html:<html>标签中的内容(结束)
</html>
{% endblock doc -%} ###doc:整个HTML文档(结束)
flask_wtf
-
为什么使用Flask-WTF?
request对象公开了所有客户端发送的请求信息。特别是request.form可以访问POST请求提交的表单数据。
尽管Flask的request对象提供的支持足以处理web表单,但依然有许多任务会变得单调且重复。
表单的HTML代码生成和验证提交的表单数据就是两个很好的例子。
优势:
Flask-WTF扩展使得处理web表单能获得更愉快的体验。该扩展是一个封装了与框架无关的WTForms包的Flask集成。 -
什么是表单处理?
在网页中,为了和用户进行信息交互总是不得不出现一些表单。
flask设计了WTForm表单库来使flask可以更加简便地管理操作表单数据。
WTForm中最重要的几个概念如下:1). Form类,开发者自定义的表单必须继承自Form类或者其子类。
Form类最主要的功能是通过其所包含的Field类提供对表单内数据的快捷访问方式。
2). 各种Field类,即字段。一般而言每个Field类都对应一个input的HTML标签。
比如WTForm自带的一些Field类比如BooleanField就对应,
SubmitField就对应等等。
3). Validator类。这个类用于验证用户输入的数据的合法性。
比如Length验证器可以用于验证输入数据的长度,
FileAllowed验证上传文件的类型等等。
另外,flask为了防范csfr(cross-site request forgery)攻击,
默认在使用flask-wtf之前要求app一定要设置过secret_key。
最简单地可以通过app.config[‘SECRET_KEY’] = 'xxxx’来配置。
- 常见的Field类
PasswordField 密码字段,自动将输入转化为小黑点
DateField 文本字段,格式要求为datetime.date一样
IntergerField 文本字段,格式要求是整数
DecimalField 文本字段,格式要求和decimal.Decimal一样
FloatField 文本字段,值是浮点数
BooleanField 复选框,值为True或者False
RadioField 一组单选框
SelectField 下拉列表,需要注意一下的是choices参数确定了下拉选项,
但是和HTML中的 标签一样,其是一个tuple组成的列表,
可以认为每个tuple的第一项是选项的真正的值,而第二项是alias。
MultipleSelectField 可选多个值的下拉列表
-
Validator是验证函数:
Validator是验证函数,把一个字段绑定某个验证函数之后,flask会在接收表单中的数据之前对数据做一个验证,
如果验证成功才会接收数据。验证函数Validator如下,具体的validator可能需要的参数不太一样,这里只给出
一些常用的,更多详细的用法可以参见wtforms/validators.py文件的源码,参看每一个validator类需要哪些参数:
*基本上每一个validator都有message参数,指出当输入数据不符合validator要求时显示什么信息。
Email 验证电子邮件地址的合法性,要求正则模式是.+@([.@][^@]+)$
EqualTo 比较两个字段的值,通常用于输入两次密码等场景,可写参数fieldname,不过注意其是一个字符串变量,指向同表单中的另一个字段的字段名
IPAddress 验证IPv4地址,参数默认ipv4=True,ipv6=False。如果想要验证ipv6可以设置这两个参数反过来。
Length 验证输入的字符串的长度,可以有min,max两个参数指出要设置的长度下限和上限,注意参数类型是字符串,不是INT!!
NumberRange 验证输入数字是否在范围内,可以有min和max两个参数指出数字上限下限,注意参数类型是字符串,不是INT!!然后在这个validator的message参数里可以设置%(min)s和%(max)s两个格式化部分,来告诉前端这个范围到底是多少。其他validator也有这种类似的小技巧,可以参看源码。
Optional 无输入值时跳过同字段的其他验证函数
Required 必填字段
Regexp 用正则表达式验证值,参数regex=‘正则模式’
URL 验证URL,要求正则模式是[a-z]+://(?P<host>[/:]+)(?P:[0-9]+)?(?P
AnyOf 确保值在可选值列表中。参数是values(一个可选值的列表)。特别提下,和SelectField进行配合使用时,不知道为什么SelectField的choices中项的值不能是数字。。否则AnyOf的values参数中即使有相关数字也无法识别出当前选项是合法选项。我怀疑NoneOf可能也是一样的套路。
NoneOf 确保值不在可选值列表中
flask-bootstrap(添加css, js样式)---提供了base.html, wtf.html
flask-wtf(表单处理)-
表单类型(StringField, PasswordField, SubmitField, FileField)
验证表单(DataRequired, Length, Email, Regexp, EqualTo)
eg:
wtflogin.htm:
{% extends 'bootstrap/base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block content %}
<form method="POST" action="/login/">
{{ wtf.quick_form(form) }}
{% if message %}
<p style="color:red">{{ message }}</p>
{% endif %}
</form>
{% endblock %}
import random
from flask import Flask, redirect, render_template, flash
from forms import LoginForm
from flask_bootstrap import Bootstrap
app = Flask(__name__)
bootstrap = Bootstrap(app)
app.config['SECRET_KEY'] = random._urandom(24)
@app.route('/success/')
def success():
return "success"
@app.route('/login/', methods=('GET', 'POST'))
def submit():
# 实例化表单对象;
form = LoginForm()
# 如果是post方法并且表单验证通过的话, 返回True;
if form.validate_on_submit():
return redirect('/success/')
return render_template('wtflogin.html', form=form)
app.run(port=9001)
forms
from flask_wtf import FlaskForm
from flask_wtf.file import FileRequired, FileAllowed
from wtforms import StringField, PasswordField, SubmitField, FileField
from wtforms.validators import DataRequired, Length
class LoginForm(FlaskForm):
name = StringField(
label="用户名/邮箱/手机号",
validators=[
DataRequired(message="请输入用户名"),
Length(3, 10, message="长度不符"),
]
)
passwd = PasswordField(
label="密码",
validators=[
DataRequired(message="输入密码"),
Length(3, 10,message="长度不符")
] )
file = FileField(
label="头像",
validators=[
FileRequired(),
FileAllowed(['png', 'jpg'],message='文件非图片')
]
)
sumbit = SubmitField(
label="登陆"
)
flask_sqlalchemy
flask_sqlalchemy-------关于数据库操作的文件,通过创建类的方法实现数据库操作,实际是将python的代码转换为数据库语句。
sqlchemy支持的字段类型:
sqlchemy支持的字段限制:
查询过路器:
执行函数:
分页对象属性:
分页对象拥有的方法:
from datetime import datetime
import pymysql
# 关于数据库操作的文件
from flask_sqlalchemy import SQLAlchemy
from flask import Flask
from sqlalchemy import desc
app = Flask(__name__)
#用于连接数据的数据库。mysql://username:password@server/db
app.config['SQLALCHEMY_DATABASE_URI']='mysql+pymysql://cooffee:cooffee@172.25.254.78/user'
# SQLAlchemy 将会追踪对象的修改并且发送信号。
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=True
db = SQLAlchemy(app)
# 默认情况下创建一个表, 表名为类名; 如果指定了__tablename__, 那么表名为你指定的名称;
class Student(db.Model):
_tablename_='students'
# sid: 表头的一列, db.SMALLINT代表存储的数据类型, primary_key(主键), 数据是唯一的;
sid = db.Column(db.SMALLINT,primary_key=True)
sname = db.Column(db.String(50))
sage = db.Column(db.SMALLINT)
# 实现一对多(Role(1): User(n))的关系
# 多的一端写外键
# 少的一端写反向引用
class User(db.Model):
# autoincrement=True自增
id = db.Column(db.INTEGER,autoincrement=True,primary_key=True)
# unique=True, name的值不能重复, 是唯一的;
name = db.Column(db.String(50),unique=True)
# 长度为100, 是因为网站密码一般会加密;
passwd = db.Column(db.String(100))
# 指定用户注册/创建的时间,
# default, 指定默认值, datetime.now()获取当前时间;
# 用户注册时间为当前时间;
add_time = db.Column(db.DateTime,default=datetime.now())
# 用户的角色id,不能随便写, 必须要关联其他的数据库表(role) --- 外键
role_id = db.Column(db.INTEGER,db.ForeignKey('role.id'))
def __repr__(self):
return '<User:%s>' %(self.name)
class Role(db.Model):
id = db.Column(db.INTEGER, autoincrement=True, primary_key=True)
name = db.Column(db.String(50), unique=True)
# Role表中的users属性与User表关联, 并且User这个表中可以由role这个对象属性;
users = db.relationship('User',backref='role')
def __repr__(self):
return '<Role:%s>' %(self.name)
if __name__=='__main__':
# 删除定义的表结构 db.drop_all()
# 创建定义的表结构
db.create_all()
#往表中插入数据
# 创建Python对象
# 把它添加到会话
# 提交会话
role1 = Role(name='超级会员')
role2 = Role(name='会员')
db.session.add(role1)
db.session.add(role2)
db.session.commit()
#添加20个用户,其中10个为超级会员, 10个为会员;
for i in range(10):
u = User(name='cooffee%s' %(i),passwd='cooffee',role_id=1)
db.session.add(u)
db.session.commit()
for i in range(10):
u = User(name='floating%s' %(i),passwd='floating',role_id=2)
db.session.add(u)
db.session.commit()
# 查询所有数据
print(User.query.all())
print(Role.query.all())
# 根据条件查询数据(筛选数据(filter_by)); slect * from table where xxx=xxx;
print(User.query.filter_by(role_id=1).all())
print(User.query.filter_by(role_id=2).all())
# 对于找到的内容进行更新
u = User.query.filter_by(name='cooffee0').first()
print(u)
u.passwd='123456'
db.session.add(u)
db.session.commit()
#筛选数据方法2(filter)
# 通过这种方式可以查看原生的sql语句
user = User.query.filter(User.role_id==1)
print(user)
# 对于查询的信息进行显示限制;
users = User.query.filter_by(role_id=1).limit(5).all()
print(users,len(users),end='\n')
# 对于查询的信息进行排序输出(默认情况由小到大进行排序), 如果想要由大到小: desc(User.add_time);
users = User.query.filter_by(role_id=1).order_by(desc(User.add_time)).all()
print(users)
# 多个过滤函数加一个显示函数
# # offset指定偏移量, limit 指定输出数量, 类似于切片操作;
# # 1 2 3 4 5 6 7 8 9
# # limit(5): 1 2 3 4 5
# # limit(5).offset(2): 3 4 5 6 7
users = User.query.filter_by(role_id=1).order_by(desc(User.add_time)).limit(5).all()
print(users)
users = User.query.filter_by(role_id=1).order_by(desc(User.add_time)).limit(5).offset(2).all()
print(users)
# # 切片操作li[2:7]
count = User.query.filter_by(role_id=1).order_by(desc(User.add_time)).slice(2,7).count()
print(count)
# 分页:第一个参数代表显示第几页的数据, 第二个参数代表每页显示多少条数据;
users = User.query.paginate(1,5)
print(users.items)
users = User.query.paginate(2,5)
print(users.items)
# 反向引用的使用
u = User.query.filter_by(name='westos0').first()
print(u.name, u.passwd, u.add_time, u.role_id, u.role, u.role.id, u.role.name)
flask_migrate及flask_script
Flask-Migrate是用于处理SQLAlchemy 数据库迁移的扩展工具。当Model出现变更的时候,通过migrate去管理数据库变更。
Flask Script扩展提供向Flask插入外部脚本的功能,包括运行一个开发用的服务器,一个定制的Python shell,设置数据库的脚本,cronjobs,及其他运行在web应用之外的命令行任务;使得脚本和系统分开;
# 1). 创建迁移仓库(migrations目录)
python script.py db init
# 2). 读取类的内容, 生成版本文件, 并没有真正在数据库中添加或删除;
python script.py db migrate -m "添加性别"
# 3). 在数据库中曾删改;
python script.py db upgrade
# 4). 检测是否成功?
mysql -ucooffee -p
# 5). 去查看改变的历史状态;
python script.py db history
# 6). 返回指定的版本状态;
python script.py db downgrade base
在modles.py文件里边的User类添加gender即在数据库中user表添加gender属性:
modles.py:
class User(db.Model):
id = db.Column(db.INTEGER,autoincrement=True,primary_key=True)
name = db.Column(db.String(50),unique=True)
passwd = db.Column(db.String(100))
#添加的属性
gender = db.Column(db.Boolean, default=True)
add_time = db.Column(db.DateTime,default=datetime.now())
role_id = db.Column(db.INTEGER,db.ForeignKey('role.id'))
def __repr__(self):
return '<User:%s>' %(self.name)
python manage.py db init:
python manage.py db migrate -m “添加性别” :
python manage.py db upgrade:
添加自定义命令的3中方式:
定义Command的子类;
使用command装饰器
使用option装饰器
from flask_migrate import Migrate,MigrateCommand
from flask_script import Shell, Manager, Command
from models import app,db,User,Role
# 用来管理命令的对象, Manager用来跟踪所有名林不过并处理他们如何从命令行调用;
manager = Manager(app)
migrate = Migrate(app,db)
# 添加一条数据库管理的命令
manager.add_command('db',MigrateCommand)
#使用command装饰器
@manager.command
def showUser():
"""显示所有的用户"""
users = User.query.all()
print(users[:5])
#定义Command的子类;
class showRole(Command):
"""显示所有角色"""
def run(self):
print(Role.query.all())
manager.add_command('showRole',showRole)
#使用option装饰器 入符哦命令需要添加参数时
# -n 简写, --name, 全称,
@manager.option('-n','--name',help="新建用户的名称")
@manager.option('-p','--passwd',help="新建用户密码")
def add_user(name,passwd):
"""创建用户"""
try:
user1 = User(name=name,passwd=passwd)
db.session.add(user1)
db.session.commit()
except Exception as e:
print("创建用户失败!",e)
else:
print("创建用户%s成功!" %(name))
# 实现添加用户的命令
if __name__=='__main__':
manager.run()
用这及个插件简化及完善news
目录结构
各个py及html文件内容:
models.py:
import random
import pymysql
from datetime import datetime
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
from flask import Flask
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI']='mysql+pymysql://cooffee:cooffee@172.25.254.78/usernews'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
app.config['SECRET_KEY']=random._urandom(24)
boostrap = Bootstrap(app)
class User(db.Model):
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
name = db.Column(db.String(50), unique=True)
passwd = db.Column(db.String(100))
add_time = db.Column(db.DateTime, default=datetime.now())
gender = db.Column(db.Boolean, default=True)
role_id = db.Column(db.INTEGER, db.ForeignKey('role.id'))
def __repr__(self):
return '<User:%s>' %(self.name)
class Role(db.Model):
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
name = db.Column(db.String(50), unique=True)
users = db.relationship('User',backref='role')
def __repr__(self):
return "<Role:%s>" %(self.name)
if __name__=="__main__":
db.drop_all()
db.create_all()
role1 = Role(name="超级会员")
role2 = Role(name="会员")
db.session.add(role1)
db.session.add(role2)
db.session.commit()
user = User(name='root',passwd='root',role_id=1)
db.session.add(user)
db.session.commit()
manage.py:
from flask_migrate import Migrate,MigrateCommand
from flask_script import Shell, Manager, Command
from models import app,db,User,Role
# 用来管理命令的对象, Manager用来跟踪所有名林不过并处理他们如何从命令行调用;
manager = Manager(app)
migrate = Migrate(app,db)
# 添加一条数据库管理的命令
manager.add_command('db',MigrateCommand)
#使用command装饰器
@manager.command
def showUser():
"""显示所有的用户"""
users = User.query.all()
print(users[:5])
#定义Command的子类;
class showRole(Command):
"""显示所有角色"""
def run(self):
print(Role.query.all())
manager.add_command('showRole',showRole)
#使用option装饰器 入符哦命令需要添加参数时
# -n 简写, --name, 全称,
@manager.option('-n','--name',help="新建用户的名称")
@manager.option('-p','--passwd',help="新建用户密码")
def add_user(name,passwd):
"""创建用户"""
try:
#这里默认用户为会员
user1 = User(name=name,passwd=passwd,role_id=2)
db.session.add(user1)
db.session.commit()
except Exception as e:
print("创建用户失败!",e)
else:
print("创建用户%s成功!" %(name))
# 实现添加用户的命令
if __name__=='__main__':
manager.run()
forms.py:
from flask_wtf import FlaskForm
from wtforms import StringField,PasswordField,SubmitField,FileField
from wtforms.validators import DataRequired, Length, Regexp, EqualTo, Email
from flask_wtf.file import FileRequired,FileAllowed
class LoginForm(FlaskForm):
user = StringField(
label="用户名/手机/邮箱",
validators=[
DataRequired(message="用户名不能为空"),
Length(3,12,message="长度在3-12之间")
]
)
passwd = PasswordField(
label="密码",
validators=[
Length(4,6,message="长度在4-6之间")
]
)
submit = SubmitField(
label="登录"
)
class RegisterForm(FlaskForm):
user = StringField(
label="用户名/手机/邮箱",
validators=[
DataRequired(message="用户名不能为空"),
Length(3,12,message="长度在3-12之间")
]
)
passwd = PasswordField(
label="密码",
validators=[
Length(4, 6, message="长度在4-6之间")
]
)
repasswd = PasswordField(
label="确认密码",
validators=[
EqualTo('passwd',"两次密码输入不一致")
]
)
email = StringField(
label="邮箱",
validators=[
Email("邮箱格式不正确!")
]
)
phone = StringField(
label="电话",
validators=[
Regexp(r'1\d{10}',message="号码格式不正确!")
]
)
face = FileField(
label="上传头像",
validators=[
FileAllowed(['png','jpg'],message="文件非图片")
]
)
submit = SubmitField(
label="注册"
)
run.py:
import random
import functools
import os
from datetime import datetime
from models import app, User, db
import psutil as psutil
from flask import Flask, request, render_template, redirect, url_for, abort, session
from forms import LoginForm,RegisterForm
def is_login(f):
"""判断用户是否登陆的装饰器"""
@functools.wraps(f)
def wrapper(*args,**kwargs):
if 'user' not in session:
return redirect(url_for('login'))
return f(*args,**kwargs)
return wrapper
# 用户主页
@app.route('/')
def index():
return render_template('index.html')
# 用户登陆按钮
@app.route('/login/',methods=['GET','POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = form.data['user']
passwd =form.data['passwd']
u = User.query.filter_by(name=user).first()
if u and passwd == u.passwd:
session['user']=user
session['passwd']=passwd
return redirect(url_for('index'))
else:
return render_template('login.html',message='用户名或者密码错误',form=form)
else:
return render_template('login.html',form=form)
# 用户注销
@app.route('/logout/')
def logout():
session.pop('user',None)
session.pop('passwd',None)
return redirect(url_for('index'))
# 用户注册
@app.route('/register/',methods=['GET','POST'])
def register():
form = RegisterForm()
if form.validate_on_submit():
user = request.form['user']
passwd = request.form['passwd']
u = User.query.filter_by(name=user).first()
if u:
message = "用户已经存在"
return render_template('signup.html',message=message)
else:
use = User(name=user,passwd=passwd,role_id=2)
db.session.add(use)
db.session.commit()
return redirect(url_for('login'))
else:
return render_template('signup.html',form=form)
@app.route('/sysinfo/')
@is_login
def sysinfo():
info = os.uname()
boot_time = psutil.boot_time()
boot_time = datetime.fromtimestamp(boot_time)
now_time = datetime.now()
delta_time = now_time - boot_time
delta_time = str(delta_time).split('.')[0]
return render_template('sysinfo.html',
hostname=info.nodename,
sysname=info.sysname,
release=info.release,
version= info.version,
machine=info.machine,
now_time=str(now_time).split('.')[0],
boot_time=boot_time,
delta_time=delta_time
)
@app.route('/userinfo/')
@is_login
def userinfo():
users = User.query.all()
return render_template("userinfo.html",users=users)
# 404异常处理: 类似于捕获异常
@app.errorhandler(404)
def not_found(e):
return render_template('404.html')
app.run(port=9002)
base.html:
{% extends "bootstrap/base.html" %}
{% block head %}
<title>{% block title %}首页{% endblock %} | cooffee</title>
{% block styles %}
{{super()}}
<link rel="stylesheet" href="../static/css/main.css">
{% endblock %}
{% endblock %}
{% block navbar %}
<!--导航栏-->
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html"></a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active" style="padding-bottom: 15px;"><a href="index.html">首页<span class="sr-only">(current)</span></a></li>
<li><a href="#">国内</a></li>
<li><a href="#">数读</a></li>
<li><a href="#">社会</a></li>
<li><a href="/sysinfo/">系统信息</a></li>
<li><a href="/userinfo/">登陆用户</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
{% if 'user' in session %}
<!--<li><a><span class="glyphicon glyphicon-user"></span></a></li>-->
<li><a href="#">{{session.user}}</a></li>
<li><a href="/logout/">注销</a></li>
{% else %}
<li><a href="/login/">登陆</a></li>
<li><a href="/register/">注册</a></li>
{% endif %}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{% endblock %}
{% block content %}
{% block newcontent %}
{% endblock %}
{% block footer %}
<div class="footer">
京ICP备11008151号京公网安备11010802014853
</div>
{% endblock %}
{% endblock %}
{% block scripts %}
{{super()}}
<script src="../static/js/bootstrap.min.js"></script>
{% endblock %}
index.html:
{% extends "base.html" %}
{% block title %}
{{super()}}
{% endblock %}
{% block newcontent %}
<div class="container container-fluid">
<div class="row">
<!--左侧导航-->
<div class="col-xs-2">
<div class="list-group left-side">
<a class="list-group-item left-side-active" href="#">综合</a>
<a class="list-group-item" href="#">电影</a>
<a class="list-group-item" href="#">电视剧</a>
<a class="list-group-item" href="#"> 明星</a>
<a class="list-group-item" href="#">娱乐</a>
</div>
</div>
<!--中间新闻-->
<div class="col-xs-7">
<div class="media">
<div class="media-left">
<a href="news.html">
<img class="media-object news-png"
src="../static/img/index1.jpg"
alt="新闻图片"></a>
</div>
<div class="media-body">
<h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4>
<div class="news-info">
<img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg">
<span>王花花</span>.
<span>25K评论</span>.
<span>7分钟前</span>
</div>
</div>
</div>
<div class="media">
<div class="media-left">
<a href="news.html">
<img class="media-object news-png"
src="../static/img/2.2.jpg"
alt="新闻图片"></a>
</div>
<div class="media-body">
<h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4>
<div class="news-info">
<img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg">
<span>王花花</span>.
<span>25K评论</span>.
<span>7分钟前</span>
</div>
</div>
</div>
</div>
<!--页角-->
<nav aria-label="Page navigation">
<ul class="pagination">
<li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">上一页</span></a>
</li>
<li class="active"><a href="#">1 <span class="sr-only">(current)</span></a></li>
<li><a href="#">2 <span class="sr-only">(current)</span></a></li>
<li><a href="#">3 <span class="sr-only">(current)</span></a></li>
<li><a href="#">4 <span class="sr-only">(current)</span></a></li>
<li><a href="#">... <span class="sr-only">(current)</span></a></li>
<li><a href="#">10 <span class="sr-only">(current)</span></a></li>
<li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">下一页</span></a>
</li>
</ul>
</nav>
</div>
<div class="col-xs-3">
<!--搜索栏-->
<div class="input-group input-info">
<input type="text" class="form-control" placeholder="搜一下">
</div>
<!--有害信息-->
<div class="bad-infomation">
<table id="bad-table">
<tr>
<td rowspan="2">
<img class="bad-jpg" src="../static/img/4.png">
</td>
<td id="bad-font1">有害信息举报专区</td>
</tr>
<tr>
<td id="bad-font2">
举报电话12377
</td>
</tr>
</table>
</div>
<!--新闻推荐-->
<div class="list-group hot-news">
<a href="#" class="list-group-item">
<span style="font-size: larger"><b>24小时热闻</b></span>
</a>
<a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info">
<span>25K评论</span>
<span>7分钟前</span></div></a>
<a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info">
<span>25K评论</span>
<span>7分钟前</span></div></a>
<a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info">
<span>25K评论</span>
<span>7分钟前</span></div></a>
<a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info">
<span>25K评论</span>
<span>7分钟前</span></div></a>
<a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info">
<span>25K评论</span>
<span>7分钟前</span></div></a>
</div>
</div>
</div>
</div>
{% endblock %}
login.html:
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}
登陆
{% endblock %}
{% block newcontent %}
<!--登陆界面-->
<div class="container container-small login">
<h1>登录
<small>没有账号?<a href="/register/">注册</a></small>
</h1>
{{wtf.quick_form(form)}}
{% if message %}
<p style="color: red">{{message}}</p>
{% endif %}
<!--</form>-->
</div>
{% endblock %}
signup.html:
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}
注册
{% endblock %}
{% block newcontent %}
<!--注册界面-->
<div class="container container-small login">
<h1>注册
<small>没有账号?<a href="/register/">注册</a></small>
</h1>
{{wtf.quick_form(form)}}
<div class="form-group">
注册咖啡或浮云即代表您同意<a href="#">咖啡或浮云服务条款</a>
</div>
{% if message %}
<p style="color: red">{{message}}</p>
{% endif %}
</div>
{% endblock %}
sysinfo.html:
{% extends 'base.html' %}
{% block title %} 系统信息 {% endblock %}
{% block newcontent %}
{# 表格内容----bootstrap #}
<table class="table table-striped" style="width: 50%; margin: auto " >
<tr>
<td><b style="font-size: large">系统信息</b></td>
<td></td>
</tr>
<tr>
<td>主机名</td>
<td>{{ hostname }}</td>
</tr>
<tr>
<td>内核名称</td>
<td>{{ sysname }}</td>
</tr>
<tr>
<td>发行版本号</td>
<td>{{ release }}</td>
</tr>
<tr>
<td>内核版本</td>
<td>{{ version}}</td>
</tr>
<tr>
<td>系统构架</td>
<td>{{ machine }}</td>
</tr>
<tr>
<td>现在时间</td>
<td>{{ now_time }}</td>
</tr>
<tr>
<td>开机时间</td>
<td>{{ boot_time }}</td>
</tr>
<tr>
<td>运行时间</td>
<td>{{ delta_time }}</td>
</tr>
</table>
{% endblock %}
userinfo.html:
{% extends 'base.html' %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %} 用户信息 {% endblock %}
{% block newcontent %}
<table class="table table-striped" style="width: 50%; margin: auto " >
<tr>
<td><b style="font-size: large">用户信息</b></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<th>编号</th>
<th>用户名</th>
<th>创建时间</th>
<th>用户角色</th>
</tr>
{% for user in users %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.add_time }}</td>
{# 因为反向引用 #}
<td>{{ user.role.name }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
404.html:
{% extends "bootstrap/base.html" %}
{% block content %}
<h2 style="color: red;">404: 页面未找到</h2>
{% endblock }