flask-day5
类视图
app.add_url_rule方法
除了装饰器@app.route(rule)可以将方法暴露给client外,还可以采用app.add_url_rule方法暴露方法
@app.route('/')
def hello_world():
print(url_for("profile")) # profile是app.add_url_rule方法的endpoint参数定义的,代表my_profile方法
return 'Hello World!'
def my_profile():
context = {
'name':'zhangsan',
'sex':'male',
'age':18
}
return render_template("profile.html", **context)
app.add_url_rule("/profile/", view_func=my_profile)
profile.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
这是个人信息页面:
<ul>
<li> 姓名:{{ name }}</li>
<li>性别:{{ sex }}</li>
<li>年龄:{{ age }}</li>
</ul>
</body>
</html>
避坑:view_func=my_profile就是暴露函数my_profile,使能通过访问http://host:port/profile/访问到该函数,my_profile不需要引号或括号。
app.add_url_rule("/profile/", view_func=my_profile)可以加入endpoint参数,如:
app.add_url_rule("/profile/", endpoint="profile",view_func=my_profile)
当你写了 endpoint=‘profile’ 那么在 hello_world方法中获取路由地址必须通过 url_for(‘jack’);
如果没有写endpoint 可以通过 url_for(‘my_profile’) 获取到 /profile/.
标准类视图
标准类视图是继承自flask.views.View
,并且在子类中必须实现dispatch_request
方法,这个方法类似于视图函数,也要返回一个基于Response
或者其子类的对象。以下将用一个例子进行讲解:
#app.py
class login(View): #1. 定义类,继承自flask.views.View
def dispatch_request(self): #2. 必须有此方法,除了名称有要求外,其他写法与视 图函数一样
context = {
'username': "zhangwuji",
'password': "123456"
}
return render_template("login.html", **context)
app.add_url_rule("/login", view_func=login.as_view("login")) #3.使用add_url_rule将类方法映射到url,从而实现类视图
类视图的继承
- 场景:login类视图和register类视图都差不多,可以新建一个common类作为被继承者。方法是:
#app.py
class common(View):
def __init__(self): #定义类属性
self.context = { #self.context就是login,register类方法里的内容
'username': "zhangwuji",
'password': "123456"
}
class login(common): #继承common类
def dispatch_request(self):
return render_template("login.html", **self.context) #**self.context调用common类的属性
class register(common):
def dispatch_request(self):
self.context.update({ #使用或修改父类common的context内容
"username":"zhaomin",
"age":20
})
return render_template("register.html", **self.context)
app.add_url_rule("/login", view_func=login.as_view("login"))
app.add_url_rule("/register", view_func=register.as_view("register"))
- 使用类视图返回json
#app.py
class Jsonview(View): #1.定义类
def dispatch_request(self): #3-1使用子类get_data方法获取数据,并进行json化
return jsonify(self.get_data())
def get_data(self): #3-2 如果出错,则报错
raise NotImplementedError
class ListView(Jsonview):
def get_data(self): #2.将数据返回父类,父类获取后进行jsonify
return {
"username": "zhouzhiruo",
"age": 18,
"door": "ermei"
}
app.add_url_rule("/list", view_func=ListView.as_view("list"))
使用装饰器修饰视图类和视图函数
- 视图函数装饰器
step 1:视图函数settings
@app.route("/settings")
def settings():
return "设置页面"
step 2: 增加装饰器,实现验证功能
装饰器的作用:用户通过http://…/settings?username=zhaomin访问才能调用函数settings,从而实现验证功能
def loginRequired(func):
def wrapper(*args, **kwargs):
username = request.args.get("username")
password = request.args.get("password")
if username and username=="zhaomin":
return func(*args,**kwargs)
else:
return "请先登录"
return wrapper
step3:将装饰器加载在settings上:
@app.route("/settings")
@loginRequired #装饰器必须放在其他装饰器之后,靠近函数
def settings():
return "设置页面"
检验:
(1) 访问http://127.0.0.1:5000/settings,提示“请先登录”,缺少验证
(2)访问http://127.0.0.1:5000/settings?username=abc,提示“请先登录”,验证错误
(3)访问http://127.0.0.1:5000/settings?username=zhaomin,成功调用,显示“
蓝本(可理解成模块)
基本使用
之前我们写的url
和视图函数都是处在同一个文件app.py,如果项目比较大的话,这显然不是一个合理的结构,而蓝图可以优雅的帮我们实现这种需求。通过blueprint模块对项目进行拆分,如豆瓣项目拆分成电影,阅读,音乐等模块,独立开发独立访问:访问时用douban.com/book,douban.com/book/list的形式:
step 1:
项目中新建package,在其中新建book.py,music.py用于子模块
step 2:
以book模块为例,book.py的代码 :
@book_bp.route("/")
def book():
return "这是阅读首页"
@book_bp.route("/list")
def listBook():
return "这是阅读列表"
step 3: (关键词:url_predix="/book")
将book.py变成蓝本:
http://127.0.0.1:5000/book
from flask import Blueprint # 1.导入蓝本模块
book_bp = Blueprint("book", __name__, url_prefix="/book") #2.将book.py变成类似app.py的模块,book_bp就是蓝图的实例
@book_bp.route("/") # 3.对应http://127.0.0.1:5000/book
def book():
return "这是阅读首页"
@book_bp.route("/list") # 4.对应http://127.0.0.1:5000/book/list
def listBook():
return "这是阅读列表"
step4: 通过__init__将book_bp暴露出去
from .book import book_bp
step5:设置app.py,使其可以与book_bp通信
app.py
from flask import Flask
from blueprints import book_bp #1. 将book_bp导入
app = Flask(__name__)
app.register_blueprint(book_bp) #2.注册book_bp
@app.route('/')
def index():
return '这是豆瓣首页'
if __name__ == '__main__':
app.run()
完成。访问http://127.0.0.1:8888/ 调用app.py的index(),访问http://127.0.0.1:8888/book 调用book.py的book(),访问http://127.0.0.1:8888/book/list 调用book.py的listBook().实现主模块与子模块的拆分。
模板与静态资源的拆分
每个模块可以使用自己的模板目录和静态资源目录,只需在Blueprint函数中指定目录地址即可
book_bp = Blueprint("book", __name__, url_prefix="/book", template_folder="./book_templates",
static_folder="./book_static")
@book_bp.route("/listTemplates")
def listFromTemplates():
return render_template("booklist.html") # 访问地址http://127.0.0.1:8888/book/listTemplates,返回book_tempalates目录的booklist.html
查找顺序:
return render_template(“booklist.html”) 中会先在templates目录下查看有无该模板,如果没有再到指定的book_templates目录下找该模板;
静态资源由模板文件指定,如booklist.html的<link type=“text/css” rel=“stylesheet” href={{ url_for(“static”,filename=“css.css”) }} >指定的css.css文件是在app的static目录,<link type=“text/css” rel=“stylesheet” href={{ url_for(“book.static”,filename=“css.css”) }} >指向的是book.py蓝图中指定的static目录(注:=由于已指定,不用写目录全称,只要写book.static即可=)
二级域名 (关键词:Blueprint(“data”,name,subdomain=“data”)
与url_prefix设置方式不一样的地方只有两处:一是要映射主机名,第二是要在data.py的Blueprint(subdomain=‘data’)
- 将ip地址映射到域名上 打开本地hosts文件,增加两行:127.0.0.1 lai.com 127.0.0.1 data.lai.com
- 在app.py上进行映射主机名,访问地址改成:http://lai.com:8888
from flask import Flask
from blueprints import data_bp
app = Flask(__name__)
app.config['SERVER_NAME'] = 'lai.com:8888' #映射主机名
app.register_blueprint(data_bp)
@app.route('/')
def index():
return '这是豆瓣首页'
if __name__ == '__main__':
app.run()
- data.py
from flask import render_template, Blueprint
data_bp = Blueprint('data', __name__, template_folder="./data_templates", subdomain='data',static_folder='./data_static') # 与前缀url的不同之处是设置subdomain="data",不再设置url_prefix
@data_bp.route("/")
def data():
return render_template("index.html")
@data_bp.route("/list")
def data_list():
return "this is data_list page"
4.init
from .data import data_bp
ORM
为了实现继承重用,在进行关系数据库操作时不直接使用sql语言,而是使用ORM模型(对象关系模型).
其思想基于对象与关系的对应:类-数据表,类的属性对应表的字段,实例对应表的记录.
好处:减少出错,增加复用
使用SQLALchemy操作数据库之前要安装如下模块:
pymysql,mysql
pip install mysql
pip install pymysql
创建表
create.py
# ---coding:utf-8----
# 文件名: sqlalchemy_create.py
# @Time:2020/4/19 11:47
from sqlalchemy import create_engine, Column, Integer, String, Text
from sqlalchemy.ext.declarative import declarative_base
HOSTNAME = '120.24.*.*'
PORT = '3306'
DATABASE = 'school'
USERNAME = 'python'
PASSWORD = '*'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URI) # 1. create_engine得到操作数据库的相关信息
Base = declarative_base(engine) # 2. declarative_base将上述信息生成一个类,该类具有创建表的功能
class Book(Base): #3.Book类继承自Base类
__tablename__ = "book"
id = Column(Integer,primary_key=True,autoincrement=True)
name = Column(String(50))
author = Column(String(20))
brief = Column(Text())
Base.metadata.create_all() #4.建表
# Base.metadata.drop_all() #5. 如果需要更改Book类,直接修改是无效的,需要先删除类,再执行一次修改后的类方法
此处有坑
运行上述代码后,成功创建了一个表book.复制所有代码粘贴到新建py中,发现无法创建新表,如book1,必须要关掉create.py后才能正常运行创建新表.
查删改
# ---coding:utf-8----
# 文件名: sqlalchemy_create.py
# @Time:2020/4/19 11:47
from sqlalchemy import create_engine, Column, Integer, String, Text, or_
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
HOSTNAME = '120.24.*.*'
PORT = '3306'
DATABASE = 'school'
USERNAME = 'python'
PASSWORD = '*'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
engine = create_engine(DB_URI)
Base = declarative_base(engine) # Base是一个类,由declarative_base基于engine生成,
session = sessionmaker(engine)() # sessionmaker(engine)生成一个具有操作engine的类,session是它的实例
class Book(Base):
__tablename__ = "book"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50))
author = Column(String(20))
brief = Column(Text())
def __str__(self):
return "书名:%s,作者:%s,简介:%s"% (self.name,self.author,self.brief)
# Base.metadata.drop_all()
Base.metadata.create_all()
def add_data(): #增加记录(即ORM中的对象)
b1 = Book(name="西游记", author="吴承恩", brief="西天取经") #1.实例化对象
b2 = Book(name="红楼梦", author="曹雪芹", brief="贾府风月")
b3 = Book(name="藏秘排油", author="郭德纲", brief="减肥314")
session.add(b1) #2.通过session添加
session.add_all([b2,b3])
session.commit() #3.执行
def query_data(): # 查询记录可使用filter_by或filter后者功能更强大
# all_books = session.query(Book).all()
# all_books = session.query(Book).filter_by(name="西游记",author="吴承恩").all()
# all_books = session.query(Book).filter(Book.name == "西游记").all()
# all_books = session.query(Book).filter_by(name="西游记", author="吴承恩").all()
# all_books = session.query(Book).filter(or_(Book.name=="西游记", Book.author=="郭德纲")).all()
all_books = session.query(Book).filter(Book.author!="").all()
for b in all_books:
print(b)
def update_data(): #修改记录
# book = session.query(Book).filfirst()
book = session.query(Book).filter_by(author="郭德纲").first()
book.brief = "减肥产品抢购一空,观众表示不服"
session.commit()
def del_data(): # 删除记录
book = session.query(Book).filter_by(author="吴承恩").first()
session.delete(book)
session.commit()
if __name__ == "__main__":
# add_data()
# update_data()
del_data()
query_data()
SQLALchemy使用原生sql语句
# ---coding:utf-8----
# 文件名: constants.py
# @Time:2020/4/19 15:59
from sqlalchemy import create_engine
HOSTNAME = '120.24.*.*'
PORT = '3306'
DATABASE = 'school'
USERNAME = 'python'
PASSWORD = '*'
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
#连接数据库
engine = create_engine(DB_URI, echo=True)
# 使用with语句连接数据库,如果发生异常会被捕获
with engine.connect() as con:
# 先删除authors表
con.execute("drop table if exists authors")
# 创建一个users表,有自增长的id和name
con.execute("create table authors(id int primary key auto_increment,name varchar(30))")
# 插入两条数据到表中
con.execute("insert into authors(name) value('岳云鹏')")
con.execute("insert into authors(name) value('张九龄')")
# 执行查询操作
result = con.execute("select * from authors")
# 从查找的结果中遍历
for a in result:
print(a)