赞
踩
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果使用蓝图,那么以后想要反转蓝图中的视图函数为url,就应该 在使用url_for的时候指定这个蓝图名字。 app类中、模版中、同一个蓝图类中都是如此。否则就找不到这个 endpoint
html文件中
新闻列表 OK写法
{# 新闻列表 no Ok写法#}
python文件中
from flask import
Blueprint,render_template,url_for
user_bp=Blueprint(‘news’,name,url_prefix=‘/user’,template_folder=‘user_page’,static_folder=‘user_static’)
@user_bp.route(‘/list/’)
def user_list():
#如下写法:才找得到 url_for(‘蓝图名称.方法名’)
print(url_for(‘user.user_list’)) #/user/list/
print(url_for(‘user.user_detail’)) #/user/detail/
return render_template(‘user_list.html’)
@user_bp.route(‘/detail/’)
def user_detail():
return ‘用户详情页面’
蓝图实现子域名:
使用蓝图技术。
在创建蓝图对象的时候,需要传递一个 subdomain 参数,来指定这 个子域名的前缀。
cms_bp=Blueprint(‘cms’,name,subdomain=‘cms’)
app.config[‘SERVER_NAME’]=‘baidu.com:5000’
注意 ip地址不能有子域名 localhost也不能有子域名
设置
设置cookie是在Response的对象上设置。 flask.Response 对象有一个 set_cookie 方法,可以通过这个方法来设置 cookie 信息。
key,value形式设置信息
from flask import Flask, make_response
app = Flask(name)
@app.route(‘/cookie’)
def set_cookie():
resp = make_response(‘set cookie ok’)
resp.set_cookie(‘uname’, ‘itbaizhan’)
return resp
查看Cookie
在Chrome浏览器中查看cookie的方式:
方式1:借助于 开发调式工具进行查看
方式2:在Chrome的设置界面->高级设置->内容设置->所有 cookie->找到当前域名下的cookie。
from flask import request
@app.route(‘/get_cookie’)
def get_cookie():
resp = request.cookies.get(‘uname’)
return resp
删除cookie
方式1:通过 Response对象.delete_cookie ,指定cookie的key,就可以删 除cookie了。
from flask import request
@app.route(‘/delete_cookie’)
def delete_cookie():
response = make_response(‘helloworld’)
response.delete_cookie(‘uname’)
return response
方式2:在客户端浏览器人为的删除(清除浏览器浏览历史记录 后,很多网站之前免密登录的都不好使了)
Cookie的有效期
默认的过期时间:如果没有显示的指定过期时间,那么这个cookie 将会在浏览器关闭后过期。 max_age:以秒为单位,距离现在多少秒后cookie会过期。
expires:为datetime类型。这个时间需要设置为格林尼治时间, 相对北京时间来说 会自动+8小时 如果max_age和expires都设置了,那么这时候以max_age为标 准。
注意
max_age在IE8以下的浏览器是不支持的。 expires虽然在新版的HTTP协议中是被废弃了,但是到目前为 止,所有的浏览器都还是能够支持,所以如果想要兼容IE8以下 的浏览器,那么应该使用expires,否则可以使用max_age。
from flask import Flask,Response
app = Flask(name)
@app.route(‘/’)
def index():
return ‘Hello!!’
@app.route(‘/create_cookie/defualt/’)
def create_cookie1():
resp = Response(‘通过默认值,设置cookie有效期’)
resp.set_cookie(‘uname’,‘zs’)
return resp
@app.route(‘/create_cookie/max_age/’)
def create_cookie2():
resp = Response(‘通过max_age,设置cookie有效期’)
age = 60602
resp.set_cookie(‘uname’,‘zs’,max_age=age)
return resp
from datetime import datetime
@app.route(‘/create_cookie/expires/’)
def create_cookie3():
resp = Response(‘通过expires,设置cookie有效期’)
tmp_time = datetime(2021, 11,11,hour=18,minute=0,second=0)
resp.set_cookie(‘uname’,‘python’,expires=tmp_time)
return resp
from datetime import timedelta
@app.route(‘/create_cookie/expires2/’)
def create_cookie4():
resp = Response(‘通过expires,设置cookie有效期’)
tmp_time = datetime.now() +timedelta(days=2)
resp.set_cookie(‘uname’,‘python_sql’,expires=tmp_time)
return resp
@app.route(‘/create_cookie/exp_max/’)
def create_cookie5():
resp = Response(‘通过expires与max_age,设置cookie有效期’)
tmp_time = datetime.now() +timedelta(days=2)
resp.set_cookie(‘uname’,‘python_sql’,expires=tmp_time,max_age = 60602)
return resp
if name == ‘main’:
app.run(debug=True)
需要先设置SECRET_KEY
class DefaultConfig(object):
SECRET_KEY = ‘fih9fh9eh9gh2’
app.config.from_object(DefaultConfig)
app.secret_key=‘xihwidfw9efw’
设置、修改
from flask import session
@app.route(‘/set_session/’)
def set_session():
session[‘username’] = ‘zs’
return ‘set session ok’
读取
@app.route(‘/get_session/’)
def get_session():
username = session.get(‘username’)
return ‘get session username {}’.format(username)
删除
@app.route(‘/del_session/’)
def delete_session():
#删除指定的key的session
#删除session中的所有的key 【删除所有】
session.clear()
return ‘删除成功’
Flask设置Session的有效期
如果没有设置session的有效期。那么默认就是浏览器关闭后过期。 如果设置session.permanent=True,那么就会默认在31天后过 期。 如果不想在31天后过期,按如下步骤操作。
1 session.permanent=True
2 可以设置 app.config[‘PERMANENT_SESSION_LIFETIME’] = timedelta(hour=2) 在两个小时后过期。
from flask import Flask,session
from datetime import timedelta
app = Flask(name)
app.secret_key = ‘sdfdfdsfsss’
app.config[‘PERMANENT_SESSION_LIFETIME’] = timedelta(days=2)
@app.route(‘/’)
def index():
return ‘Hello!!’
@app.route(‘/set_session/’)
def set_session():
session.permanent = True
session[‘uname’] = ‘10001’
return ‘设置一个Session的信息’
@app.route(‘/get_session/’)
def get_session():
return session.get(‘uname’)
if name == ‘main’:
app.run(debug=True)
login.html
账号: | |
密码: | |
{% if msg %} {{ msg }} {% endif %} |
from flask import Flask, session,
request,redirect,url_for,views,render_template
app = Flask(name)
class LoginView(views.MethodView):
def __jump(self,msg=None):
return render_template(‘login.html’,msg = msg)
def get(self):
msg = request.args.get(‘msg’)
return self.__jump(msg)
def post(self):
uname = request.form.get(‘uname’)
pwd = request.form.get(‘pwd’)
if uname == “zs” and pwd == “123”:
session[‘uname’] = uname
return render_template(‘index.html’)
else:
return self.__jump(msg=“用户名或者密码错误”)
@app.route(‘/index/’)
def index():
uname = session.get(‘uname’)
if uname:
return ‘这个是主页!!!’
return redirect(url_for(‘login’,msg=‘请先登录’))
app.add_url_rule(‘/login/’,view_func=LoginView.as_view(‘login’))
if name == ‘main’:
app.secret_key = ‘xihwidfw9efw’
app.run(debug=True)
需求
要实现并发效果, 每一个请求进来的时候我们都开启一个进程, 这显然是不合理的, 于是就可以使用 线程 那么线程中数据互相不隔离,存在修改数据的时候数据不安全的问题
Local对象
在Flask中,类似于 request 对象,其实是绑定到了一个 werkzeug.local.Local 对象上。 这样,即使是同一个对象,那么在多个线程中都是隔离的。类似的 对象还有 session 对象。
ThreadLocal变量
Python提供了ThreadLocal 变量,它本身是一个全局变量, 但是每个线程却可以利用它来保存属于自己的私有数据, 这些私有数据对其他线程也是不可见的。
from threading import Thread,local
local =local()
local.request = ‘具体用户的请求对象’
class MyThread(Thread):
def run(self):
local.request = ‘zs’
print(‘子线程:’,local.request)
mythread = MyThread()
mythread.start()
mythread.join()
print(‘主线程:’,local.request)
from werkzeug.local import Local
local = Local()
local.request = ‘具体用户的请求对象’
class MyThread(Thread):
def run(self):
local.request = ‘sxt’
print(‘子线程:’,local.request)
mythread = MyThread()
mythread.start()
mythread.join()
print(‘主线程:’,local.request)
总结
只要满足绑定到"local"或"Local"对象上的属性,在每个线程中都是 隔离的,那么他就叫做 ThreadLocal 对象,也叫’ThreadLocal’变量。
App上下文,也叫应用上下文
上下文(感性的理解)
每一段程序都有很多外部变量,只有像add这种简单的函数才是 没有外部变量的。 一旦一段程序有了外部变量,这段程序就不 完整,不能独立运行。为了能让这段程序可以运行,就要给所 有的外部变量一个一个设置一些值。就些值所在的集合就是叫 上下文。 并且上下文这一概念在中断任务的场景下具有重大意义,其中 任务在被中断后,处理器保存上下文并提供中断处理,因些在 这之后,任务可以在同一个地方继续执行。(上下文越小,延迟 越小)
举例
运行的Flask项目,每一个路由映射的内容片段,都不可以单独 拿出来使用.
当获取到了APP_Context以后,就可以直接通过程序映射的地 址访问逻辑,并且可以重复使用。
上下文的一个典型应用场景就是用来缓存一些我们需要在发生请求 之前或者要使用的资源。举个例子,比如数据库连接。当我们在应 用上下文中来存储东西的时候你得选择一个唯一的名字,这是因为 应用上下文为 Flask 应用和扩展所共享。
应用上下文:
应用上下文是存放到一个 LocalStack 的栈中。和应用app相关的操作就 必须要用到应用上下文
比如:
通过 current_app 获取当前的这个 app 名字。
注意
在视图函数中,不用担心应用上下文的问题。因为视图函数要 执行,那么肯定是通过访问url的方式执行的, 那么这种情况下,Flask底层就已经自动的帮我们把应用上下文 都推入到了相应的栈中。
如果想要在视图函数外面执行相关的操作, 比如: 获取当前的app名称,那么就必须要手动推入应用上下文
第一种方式:便于理解的写法
from flask import Flask,current_app
app = Flask(name)
#app上下文
app_context = app.app_context()
app_context.push()
print(current_app.name)
@app.route(‘/’)
def hello_world():
print(current_app.name) #获取应用的名称
return ‘Hello World!’
if name == ‘main’:
app.run(debug=True)
第二种方式:用with语句
from flask import Flask,current_app
app = Flask(name)
#app上下文
#换一种写法
with app.app_context():
print(current_app.name)
@app.route(‘/’)
def hello_world():
print(current_app.name) #获取应用的名称
return ‘Hello World!’
if name == ‘main’:
app.run(debug=True)
保存为全局对象g对象的好处:
g对象是在整个Flask应用运行期间都是可以使用的。 并且也跟request一样,是线程隔离的。 这个对象是专门用来存储开发者自己定义的一些数据,方便在整个 Flask程序中都可以使用。 一般使用就是,将一些经常会用到的数据绑定到上面,以后就直接 从g上面取就可以了,而不需要通过传参的形式,这样更加方便。
g对象使用场景:
有一个工具类utils.py 和 用户办理业务:
def funa(uname):
print(f’funa {uname}‘)
def funb(uname):
print(f’funb {uname}’)
def func(uname):
print(f’func {uname}')
用户办理业务
from flask import Flask,request
from utils import funa,funb,func
app = Flask(name)
#Flask_线程隔离的g对象使用详解
@app.route(“/profile/”)
def my_profile():
#从url中取参
uname = request.args.get(‘uname’)
#调用功能函数办理业务
funa(uname)
funb(uname)
func(uname)
#每次都得传参 麻烦,引入g对象进行优化
return “办理业务成功”
if name == ‘main’:
app.run(debug=True)
优化工具类utils.py
from flask import g
def funa():
print(f’funa {g.uname}‘)
def funb():
print(f’funb {g.uname}’)
def func():
print(f’func {g.uname}')
钩子函数概念
在Flask中钩子函数是使用特定的装饰器装饰的函数。 为什么叫做钩子函数呢,是因为钩子函数可以在正常执行的代码 中,插入一段自己想要执行的代码。 那么这种函数就叫做钩子函数。
常见的钩子函数
@app.before_first_request
def first_request():
print(‘first time request’)
@app.before_request
def before_request():
if not hasattr(g,‘glo1’):
setattr(g,‘glo1’,‘想要设置的’)
@app.teardown_appcontext
def teardown(exc=None):
if exc is None:
db.session.commit()
else:
db.session.rollback()
db.session.remove()
@app.template_filter(“upper”)
def upper_filter(s):
return s.upper()
@app.context_processor
def context_processor():
if hasattr(g,‘user’):
return {“current_user”:g.user}
else:
return {}
@app.errorhandler(404)
def page_not_found(error):
return ‘This page does not exist’,404
信号机制
大白话来说,类似于两方属于敌对关系时,某人在敌对方阵营进行 交谈,一旦遇到特殊情况,某人便会发送信号,他的同伙接收(监 听)到他发的信号后,同伙便会做出一系列的应对策略(进攻|撤 退)。 flask中的信号使用的是一个第三方插件,叫做blinker。通过pip list看一下,如果没有安装,通过以下命令即可安装blinker
pip install blinker
自定义信号步骤
自定义信号可分为3步来完成。
第一是创建一个信号,第二是监听一个信号,第三是发送一个信 号。
以下将对这三步进行讲解:
创建信号:定义信号需要使用到blinker这个包的Namespace类来创建一个命名空间。比如定义一 个在访问了某个视图函数的时候的信号。示例代码如下:
冲突的问题
from blinker import Namespace
mysignal = Namespace()
signal1 = mysignal.signal(‘信号名称’)
监听信号:监听信号使用signal1对象的connect方法,在这个方法中需要传递一个函数,用来监听 到这个信号后做该做的事情。示例代码如下:
def func1(sender,uname):
print(sender)
print(uname)
signal1.connect(func1)
发送信号:发送信号使用signal1对象的send方法,这个方法可以传递一些其他参数过去。示例代 码如下:
signal1.send(uname=‘momo’)
Flask信号使用场景_存储用户登录日志
信号使用场景
定义一个登录的信号,以后用户登录进来以后 就发送一个登录信号,然后能够监听这个信号 在监听到这个信号以后,就记录当前这个用户登录的信息 用信号的方式,记录用户的登录信息即登录日志。
编写一个signals.py文件创建登录信号
from blinker import Namespace
from datetime import datetime
from flask import request,g
namespace = Namespace()
#创建登录信号
login_signal = namespace.signal(‘login’)
def login_log(sender):
now = datetime.now()
ip = request.remote_addr
log_data = “{uname}{now}{ip}”.format(uname=g.uname, now=now, ip=ip)
with open(‘login_log.txt’,‘a’) as f:
f.write(log_data + “\n”)
f.close()
#监听信号
login_signal.connect(login_log)
使用信号存储用户登录日志
from flask import Flask,request,g
from signals import login_signal
app = Flask(name)
@app.route(‘/login/’)
def login():
uname = request.args.get(‘uname’)
if uname:
g.uname = uname
login_signal.send()
return ‘登录成功!’
else:
return ‘请输入用户名!’
if name == ‘main’:
app.run(debug=True)
Flask内置了10个常用的信号:
1 template_rendered:模版渲染完成后的信号。
2 before_render_template:模版渲染之前的信号。
3 request_started:请求开始之前,在到达视图函数之前发送信号。
4 request_finished:请求结束时,在响应发送给客户端之前发送信号。
5 request_tearing_down:请求对象被销毁时发送的信号,即使在请求过程中发生异常也会发送信 号。
6 got_request_exception:在请求过程中抛出异常时发送信号,异常本身会通过exception传递到订 阅(监听)的函数中。一般可以监听这个信号,来记录网站异常信息。
7 appcontext_tearing_down:应用上下文被销毁时发送的信号。
8 appcontext_pushed:应用上下文被推入到栈上时发送的信号。
9 appcontext_popped:应用上下文被推出栈时发送的信号。
10 message_flashed:调用了Flask的 flash 方法时发送的信号。
WTForms介绍
这个插件库主要有两个作用。 第一个是做表单验证,将用户提交上来的数据进行验证是否符合系 统要求。 第二个是做模版渲染。 (了解即可) 官网:https://wtforms.readthedocs.io/en/latest/index.html
Flask-WTF是简化了WTForms操作的一个第三方库。WTForms表单 的两个主要功能是验证用户提交数据的合法性以及渲染模板。而 Flask-WTF还包括一些其他的功能:CSRF保护,文件上传等。 安装Flask-WTF默认也会安装WTForms,因此使用以下命令来安装 Flask-WTF和WTForms:
pip install flask-wtf
WTForms表单验证的基本使用
1 自定义一个表单类,继承自wtforms.Form类。
2 定义好需要验证的字段,字段的名字必须和模版中那些需要验证的input标签的name属性值保持一 致。
3 在需要验证的字段上,需要指定好具体的数据类型。
4 在相关的字段上,指定验证器。
5 以后在视图函数中,只需要使用这个表单类的对象,并且把需要验证的数据,也就是request.form 传给这个表单类,再调用表单类对象.validate()方法进行,如果返回True,那么代表用户输入的数 据都是符合格式要求的,Flase则代表用户输入的数据是有问题的。如果验证失败了,那么可以通 过表单类对象.errors来获取具体的错误信息。
from flask import
Flask,render_template,request
from wtforms import Form,StringField
from wtforms.validators import
Length,EqualTo
app = Flask(name)
@app.route(‘/’)
def index():
return ‘Hello! ’
class RegisterForm(Form):
uname = StringField(validators=[Length(min=2,max=10,message=‘用户名长度2-10之间’)])
pwd = StringField(validators=[Length(min=2,max=10)])
pwd2 = StringField(validators=[Length(min=2,max=10),EqualTo(‘pwd’,message=‘2次密码不一致’)])
@app.route(’/register/‘, methods=[‘GET’,‘POST’])
def register():
if request.method == ‘GET’:
return render_template(‘register.html’)
else:
form = RegisterForm(request.form)
if form.validate(): # 验证成功:True,失败:False
return ‘验证成功!’
else:
return f’验证失败!{form.errors}’
if name == ‘main’:
app.run(debug=True)
页面把数据提交上来,需要经过表单验证,进而需要借助验证器来 进行验证,以下是常用的内置验证器:
username = StringField(validators=[Length(min=3,max=10,message=“用户名长度必须在3到10位之间”)])
password_repeat = StringField(validators=[Length(min=6,max=10),EqualTo(“password”)])
email = StringField(validators=[Email()])
username = StringField(validators=[input_required()])
age = IntegerField(validators=[NumberRange(12,18)])
phone = StringField(validators=[Regexp(r’1[34578]\d{9}')])
home_page = StringField(validators=[URL()])
uuid = StringField(validators=[UUID()])
只有当WTForms内置的验证器不够使的时候,才需要使用自定义验 证器。 如果想要对表单中的某个字段进行更细化的验证,那么可以针对这 个字段进行单独的验证。
自定义验证器步骤如下:
1 定义一个方法,方法的名字规则是: validate_字段名(self,field) 。
2 在方法中,使用 field.data 可以获取到这个字段的具体的值。
3 验证时,如果数据满足条件,那么可以什么都不做。如果验证失败,那么应该抛出一个 wtforms.validators.ValidationError 的异常,并且把验证失败 的信息传到这个异常类中。
场景:验证码实现
关键代码演示:(实现验证码 验证)
from flask import session
from wtforms import
Form,StringField,IntegerField
from wtforms.validators import
Length,EqualTo,Email,InputRequired,NumberRan
ge,Regexp,URL,UUID,ValidationError
class RegisterForm2(Form):
email = StringField(validators=[Email()])
uname = StringField(validators=[InputRequired()])
age = IntegerField(validators=[NumberRange(18,40)])
phone = StringField(validators=[Regexp(r’1[34578]\d{9}')])
phomepage = StringField(validators=[URL()])
uuid = StringField(validators=[UUID()])
code = StringField(validators=[Length(4,4)])
#取到的值 和服务器上 session上存储的值对比
def validate_code(self,field):
print(field.data,session.get(‘code’))
if field.data !=session.get(‘code’):
raise ValidationError(‘验证码不一致!’)
上传文件步骤:
在模版html中,表单需要指定 enctype=‘multipart/form-data’ 才能上传文 件。
在后台如果想要获取上传的文件,那么应该使用 request.files.get(‘文件 名’) 来获取。
保存文件之前,先要使用 werkzeug.utils.secure_filename 来对上传上来的文 件名进行一个过滤。能保证不会有安全问题。
获取到上传上来的文件后,使用 文件对象.save(路径) 方法来保存文件。 路径=完整路径=路径名+文件名
upload.html页面
头像: | |
描述: | |
app.py文件
from flask import
Flask,request,render_template
import os
from werkzeug.utils import secure_filename
app = Flask(name)
UPLOAD_PATH = os.path.join(os.path.dirname(file),‘images’)
@app.route(‘/upload/’,methods=[‘GET’,‘POST’])
def upload():
if request.method == ‘GET’:
return render_template(‘upload.html’)
else:
desc = request.form.get(“desc”)
pichead = request.files.get(“pichead”)
filename = secure_filename(pichead.filename) #包装一下 保证文件安全
#pichead.save(os.path.join(UPLOAD_PATH,pichead.filename)) #可优化
pichead.save(os.path.join(UPLOAD_PATH,filename)) #已优化
print(desc)
return ‘文件上传成功’
if name == ‘main’:
app.run(debug=True)
访问文件
从服务器上读取文件,应该定义一个url与视图函数,来获取指定的 文件。 在这个视图函数中,使用 send_from_directory(文件的目录,文件名) 来获取。
from flask import Flask
import os
from flask import send_from_directory
app = Flask(name)
UPLOAD_PATH = os.path.join(os.path.dirname(file),‘images’)
@app.route(‘/images//’)
def get_image(filename):
return send_from_directory(UPLOAD_PATH,filename)
if name == ‘main’:
app.run(debug=True)
关键点:
1 定义验证表单类的时候,对文件类型的字段,需要采用 FileField 这个类型,即wtforms.FileField 2 验证器需要从 flask_wtf.file 中导入。 flask_wtf.file.FileRequired 和 flask_wtf.file.FileAllowed
3 flask_wtf.file.FileRequired 是用来验证文件上传不能为空。
4 flask_wtf.file.FileAllowed 用来验证上传的文件的后缀名, 如常见图片后缀 .jpg 和.png以及.gif等。
5 在视图函数中,需要使用 from werkzeug.datastructures import CombinedMultiDict 来把 request.form 与 request.files 来进行合并。
6 最后使用 表单验证对象.validate()进行验证。
代码如下:
upload.html页面
头像: | |
描述: | |
formscheck.py文件
from wtforms import
Form,FileField,StringField
from wtforms.validators import InputRequired
from flask_wtf.file import FileRequired,FileAllowed
class UploadForm(Form):
pichead = FileField(validators= [FileRequired(),FileAllowed([‘jpg’,‘png’,‘gif’])])
desc = StringField(validators=[InputRequired()])
app.py文件
from flask import
Flask,request,render_template
import os
from werkzeug.utils import secure_filename
from formscheck import UploadForm
from werkzeug.datastructures import
CombinedMultiDict
app = Flask(name)
UPLOAD_PATH = os.path.join(os.path.dirname(file),‘images’)
#利用flask-wtf验证上传的文件
@app.route(‘/upload/’,methods=[‘GET’,‘POST’])
def upload():
if request.method == ‘GET’:
return render_template(‘upload.html’)
else:
form = UploadForm(CombinedMultiDict([request.form,request.files]))
if form.validate():
desc = form.desc.data
pichead = form.pichead.data
filename = secure_filename(pichead.filename)
pichead.save(os.path.join(UPLOAD_PATH,filename))
print(desc)
return ‘文件上传成功’
else:
print(form.errors)
return “文件上传失败”
if name == ‘main’:
app.run(debug=True)
1.Restful接口规范
REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的 应用程序或设计就是 RESTful。 RESTful是一种软件架构风格、设计风格,而不是标准,只是提供了 一组设计原则和约束条件。 它主要用于客户端和服务器交互类的软件。基于这个风格设计的软 件可以更简洁,更有层次。 RESTful接口规范是用于在前端与后台进行通信的一套规范。使用这 个规范可以让前后端开发变得更加轻松。
**2.适用场景:**一个系统的数据库数据,展现的平台有PC端、移动 端、app端、ios端。 前端工程师:都遵循RESTful编程规范 后端工程师:都遵循RESTful编程规范 最终结果:开发效率高,便于管理。
**3.协议:**用http或者https协议。
4.数据传输格式: 数据传输的格式应该都用json格式。
**5.url链接规则:**url链接中,不能有动词,只能有名词。 并且对于一些名词,如果出现复数,那么应该在后面加s。 比如:获取新闻列表,应该使用 /news/ ,而不应该使用/get_news/
6.HTTP请求方式: GET:从服务器上获取资源。 POST:在服务器上新增或者修改一个资源。 PUT:在服务器上更新资源。(客户端提供所有改变后的数据) PATCH:在服务器上更新资源。(客户端只提供需要改变的属性) DELETE:从服务器上删除资源。
7.状态码:
1.介绍:
优势: Flask-Restful是一个专门用来写restful api的一个插件。 使用它可以快速的集成restful api接口功能。 在系统的纯api的后台中,这个插件可以帮助我们节省很多时间。
缺点: 如果在普通的网站中,这个插件就没有优势了,因为在普通的网站 开发中,是需要去渲染HTML代码的, 而Flask-Restful在每个请求中都是返回json格式的数据。
**2.安装:**pip install flask-restful
3.基本使用:
定义Restful的类视图:
从 flask_restful 中导入 Api ,来创建一个 api 对象。
写一个类视图,让他继承自 Resource 类,然后在这个里面,使用 你想要的请求方式来定义相应的方法,比如你想要将这个类视图只 能采用 post 请求,那么就定义一个 post 方法。
使用 api.add_resource 来添加类视图与 url 。
from flask import Flask,url_for
from flask_restful import Resource,Api
app = Flask(name)
api = Api(app)
class LoginView(Resource):
def get(self):
return {“flag”:True}
def post(self):
return {“flag”:False}
api.add_resource(LoginView,‘/login/’,‘/login2/’,endpoint=‘login’)
with app.test_request_context():
print(url_for(‘login’))
if name == ‘main’:
app.run(debug=True)
注意
1 如果你想返回json数据,那么就使用flask_restful,如果你是想渲染模版,那么还是采用之前 的方式,就是 app.route 的方式。
2 url还是跟之前的一样,可以传递参数。也跟之前的不一样,可以指定多个url。
3 endpoint是用来给url_for反转url的时候指定的。如果不写endpoint,那么将会使用视图的 名字的小写来作为endpoint。
4 add_resource的第二个参数是访问这个视图函数的url,这个url可以跟之前的route一样,可 以传递参数,并且还有一点不同的是,这个方法可以传递多个url来指定这个视图函数。
参数验证
参数验证也叫参数解析 Flask-Restful插件提供了类似WTForms来验证提交的数据是否合法 的包,叫做reqparse。
基本用法
1 通过 flask_restful.reqparse 中 RequestParser 建立解析器
2 通过 RequestParser 中的 add_argument 方法定义字段与解析规则
3 通过 RequestParser 中的 parse_args 来解析参数
1 解析正确,返回正确参数
2 解析错误,返回错误信息给前端
from flask import Flask
from flask_restful import Api,Resource
from flask_restful.reqparse import
RequestParser
app = Flask(name)
api = Api(app)
class RegisterView(Resource):
def post(self):
parser = RequestParser()
parser.add_argument(‘uname’,type=str,required=True,help=‘用户名验证错误’,trim=True)
args = parser.parse_args()
print(args)
return {‘msg’:‘注册成功!!’}
api.add_resource(RegisterView,‘/register/’)
if name == ‘main’:
app.run(debug=True)
add_argument方法参数详解
add_argument方法可以指定这个字段的名字,这个字段的数据类 型等,验证错误提示信息等,具体如下:
**1 default:**默认值,如果这个参数没有值,那么将使用这个参数 指定的默认值。
2 required:是否必须。默认为False,如果设置为True,那么这 个参数就必须提交上来。
**3 type:**这个参数的数据类型,如果指定,那么将使用指定的数 据类型来强制转换提交上来的值。可以使用python自带的一些 数据类型(如str或者int),也可以使用flask_restful.inputs下的一 些特定的数据类型来强制转换。
url:会判断这个参数的值是否是一个url,如果不是,那么就会抛出异常。
regex:正则表达式。
date:将这个字符串转换为datetime.date数据类型。如果转换不成功,则会抛出一个异常.
4 choices:固定选项。提交上来的值只有满足这个选项中的值才 符合验证通过,否则验证不通过。
5 help:错误信息。如果验证失败后,将会使用这个参数指定的 值作为错误信息。
**6 trim:**是否要去掉前后的空格
from flask import Flask
from flask_restful import
Api,Resource,inputs
from flask_restful.reqparse import
RequestParser
app = Flask(name)
api = Api(app)
class RegisterView(Resource):
def post(self):
parser = RequestParser()
parser.add_argument(‘uname’,type=str,required=True,trim=True,help=‘用户名不符合规范’)
parser.add_argument(‘pwd’,type=str,help=‘密码错误’,default=‘123456’)
parser.add_argument(‘age’,type=int,help=‘年龄验证错误!’)
parser.add_argument(‘gender’,type=str,choices=[‘男’, ‘女’,‘保密’],help=‘性别验证错误’)
parser.add_argument(‘birthday’,type=inputs.date,help=‘生日验证错误’)
parser.add_argument(‘phone’,type=inputs.regex(‘^1[356789]\d{9}$’),help=‘电话验证错误’)
parser.add_argument(‘homepage’,type=inputs.url,help=‘个人主页验证错误’)
args = parser.parse_args()
print(args)
return {‘msg’:‘注册成功!’}
api.add_resource(RegisterView,‘/register/’)
if name == ‘main’:
app.run(debug=True)
数据库是一个网站的基础。 比如MySQL、MongoDB、SQLite、PostgreSQL等,这里我们以 MySQL为例进行讲解。 SQLAlchemy是一个ORM框架。
对象关系映射(英语:Object Relational Mapping,简称 ORM,或O/RM,或O/R mapping),是一种程序设计技术, 用于实现面向对象编程语言里不同类型系统的数据之间的转 换。 从效果上说,它其实是创建了一个可在编程语言里使用的“虚拟 对象数据库”。
大白话 对象模型与数据库表的映射
为什么要有SQLAlchemy?
随着项目的越来越大,采用写原生SQL的方式在代码中会出现大量 重复的SQL语句,那么,问题就出现了:
1.SQL语句重复利用率不高,越复杂的SQL语句条件越多,代码越长,会出现很多相近的SQL语句。
2.很多SQL语句 是在业务逻辑中拼接出来的,如果数据库需要更改,就要去修改这些逻辑,这会容易 漏掉对某些SQL语句的修改。
3 写SQL时容易忽略web安全问题,造成隐患。
而ORM可以通过类的方式去操作数据库而不用再写原生的SQL语 句,通过把表映射成类,把行作为实例(一条数据),把字段作为属 性,ORM在执行对象操作的时候最终还是会把对象的操作转换为数 据库的原生语句,但使用ORM有许多优点:
1.易用性:使用ORM做数据库开发可以有效减少重复SQL语句的概率,写出来的模型也更加直观、清 晰
2.性能损耗小:ORM转换成底层数据库操作指令确实会有一些开销。但是从实际情况来看,这种性能 损耗很少(不足5%),只要不是针对性能有严苛的要求,综合考虑开发效率、代码阅读性,带来 的好处远大于性能损耗,而且项目越大作用越明显。
3 设计灵活:可以轻松的写出复杂的查询。
4.可移植性:SQLAlchemy封装了底层的数据库实现,支持多个关系数据库引擎,包括流行的 Mysql、PostgreSQL和SQLite,可以非常轻松的切换数据库。
使用ORM操作数据库将变得非常简单!
class Person:
name = ‘xx’
age = 18
country =‘xx’
我们会以 MySQL+ SQLAlchemy 组合进行讲解。
在操作数据库操作之前,先确保你已经安装了以下软件:
pip install pymysql
pip install SQLAlchemy
连接数据库
from sqlalchemy import create_engine
def conn_db1():
HOST = ‘192.168.30.151’ # 127.0.0.1/localhost
PORT = 3306
DATA_BASE = ‘flask_db’
USER = ‘root’
PWD = ‘123’
DB_URI = f’mysql+pymysql://{USER}:{PWD}@{HOST}:{PORT}/{DATA_BASE}’
engine = create_engine(DB_URI)
sql = ‘select 2;’
conn = engine.connect()
rs = conn.execute(sql)
print(rs.fetchone())
执行原生SQL
def conn_db2():
HOST = ‘192.168.30.151’ # 127.0.0.1/localhost
PORT = 3306
DATA_BASE = ‘flask_db’
USER = ‘root’
PWD = ‘123’
DB_URI = f’mysql+pymysql://{USER}:{PWD}@{HOST}:{PORT}/{DATA_BASE}’
‘’’
engine = create_engine(DB_URI)
sql = ‘create table t_user(id int primary key auto_increment, name varchar(32));’
conn = engine.connect()
conn.execute(sql)
‘’’
def conn_db3():
HOST = ‘192.168.30.151’ # 127.0.0.1/localhost
PORT = 3306
DATA_BASE = ‘flask_db’
USER = ‘root’
PWD = ‘123’
DB_URI = f’mysql+pymysql://{USER}:{PWD}@{HOST}:{PORT}/{DATA_BASE}’
engine = create_engine(DB_URI)
sql = ‘create table t_user1(id int primary key auto_increment, name varchar(32));’
with engine.connect() as conn:
conn.execute(sql)
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine(DB_URI)
Base = declarative_base(engine)
class Person(Base):
tablename =‘t_person’
class Person(Base):
tablename =‘t_person’
id = Column(Integer,primary_key=True,autoincrement=True)
name = Column(String(50))
age = Column(Integer)
country = Column(String(50))
Base.metadata.create_all()
注意
一旦使用 Base.metadata.create_all() 将模型映射到数据库中后,即使改变 了模型的字段,也不会重新映射了
构建session对象
所有和数据库的ORM操作都必须通过一个叫做 session 的会话对象 来实现,通过以下代码来获取会话对象
from sqlalchemy.orm import sessionmaker
engine = create_engine(DB_URI)
Base = declarative_base(engine)
session = sessionmaker(engine)()
添加对象
def create_data_one():
with Session() as session:
p1 = Person(name = ‘刘备’,age = 6 ,country=‘北京’)
session.add(p1)
session.commit()
def create_data_many():
with Session() as session:
p2 = Person(name = ‘吕布’,age = 19 ,country=‘北京’)
p3 = Person(name = ‘貂蝉’,age = 18 ,country=‘北京’)
session.add_all([p2,p3])
session.commit()
查找对象
def query_data_all():
with Session() as session:
all_person = session.query(Person).all()
for p in all_person:
print(p.name)
def query_data_one():
with Session() as session:
p1 = session.query(Person).first()
print(p1.name)
def query_data_by_params():
with Session() as session:
p1 = session.query(Person).filter(Person.name == ‘吕布’).first()
print(p1.age)
修改对象
def update_data():
with Session() as session:
p1 = session.query(Person).filter(Person.name == ‘吕布’).first()
p1.age = 20
session.commit()
删除对象
将需要删除的数据从数据库中查找出来,然后使用 session.delete 方法将 这条数据从session中删除,最后做commit操作就可以了
def delete_data():
with Session() as session:
p1 = session.query(Person).filter(Person.name == ‘貂蝉’).first()
session.delete(p1)
session.commit()
Integer:整形,映射到数据库中是int类型。
Float:浮点类型,映射到数据库中是float类型。他占据的32 位。
Double:双精度浮点类型,映射到数据库中是double类型,占 据64位 (SQLALCHEMY中没有)。 String:可变字符类型,映射到数据库中是varchar类型.
Boolean:布尔类型,映射到数据库中的是tinyint类型。
DECIMAL:定点类型。是专门为了解决浮点类型精度丢失的问 题的。在存储钱相关的字段的时候建议大家都使用这个数据类 型。
Enum:枚举类型。指定某个字段只能是枚举中指定的几个值, 不能为其他值。在ORM模型中,使用Enum来作为枚举,示例代 码如下:
class News(Base):
tablename = ‘t_news’
tag = Column(Enum(“python”,‘flask’,‘django’))
在Python3中,已经内置了enum这个枚举的模块,我们也可以 使用这个模块去定义相关的字段。示例代码如下:
class TagEnum(enum.Enum):
python = “python”
flask = “flask”
django = “django”
class News(Base):
tablename = ‘t_news’
id = Column(Integer,primary_key=True,autoincrement=True)
tag = Column(Enum(TagEnum))
Date:存储时间,只能存储年月日。映射到数据库中是date类 型。在Python代码中,可以使用 datetime.date 来指定。
DateTime:存储时间,可以存储年月日时分秒毫秒等。映射到 数据库中也是datetime类型。在Python代码中,可以使用 datetime.datetime 来指定。
Time:存储时间,可以存储时分秒。映射到数据库中也是time 类型。在Python代码中,可以使用 datetime.time 来至此那个。示例 代码如下:
class News(Base):
tablename = ‘t_news’
create_time = Column(Time)
news =News(create_time=time(hour=11,minute=11,second=11))
Text:存储长字符串。一般可以存储6W多个字符。如果超出了 这个范围,可以使用LONGTEXT类型。映射到数据库中就是text 类型。
LONGTEXT:长文本类型,映射到数据库中是longtext类型。
from sqlalchemy import create_engine,Column,Integer,String,Float,Enum,Boolean,DECIMAL,Text,Date,DateTime,Time
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.dialects.mysql import LONGTEXT
from sqlalchemy.orm import sessionmaker
import enum
from datetime import date
from datetime import datetime
from datetime import time
HOSTNAME = ‘127.0.0.1’
PORT = ‘3306’
DATABASE = ‘first_sqlalchemy’
USERNAME = ‘root’
PASSWORD = ‘root’
DB_URI =“mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8”.format(username=USERNAME,password=PASSWORD,
host=HOSTNAME,port=PORT,db=DATABASE)
engine = create_engine(DB_URI)
session = sessionmaker(engine)()
class TagEnum(enum.Enum):
python=“PYHTON2”
flask=“FLASK2”
django =“DJANGO”
Base = declarative_base(engine)
class News(Base):
tablename=‘news’
id = Column(Integer,primary_key=True,autoincrement=True)
price1 = Column(Float) #存储数据时存在精度丢失问题
price2 = Column(DECIMAL(10,4))
title = Column(String(50))
is_delete =Column(Boolean)
tag1 =Column(Enum(‘PYTHON’,‘FLASK’,‘DJANGO’)) # 枚举常规写法
tag2 =Column(Enum(TagEnum)) #枚举另一种写法
create_time1=Column(Date)
create_time2=Column(DateTime)
create_time3=Column(Time)
content1 =Column(Text)
content2 =Column(LONGTEXT)
a1 = News(price1=1000.0078,price2=1000.0078,title=‘测试数据’
,is_delete=False,tag1=“PYTHON”,tag2=TagEnum.python,
create_time1=date(2018,12,12),create_time2=datetime(2019,2,20,12,12,30),create_time3=time(hour=11,minute=12,second=13),
content1=“hello”,content2=“hello hi nihao”)
session.add(a1)
session.commit()
Column常用参数
primary_key:True设置某个字段为主键。
autoincrement:True设置这个字段为自动增长的。
default:设置某个字段的默认值。在发表时间这些字段上面经 常用。
nullable:指定某个字段是否为空。默认值是True,就是可以为 空。
unique:指定某个字段的值是否唯一。默认是False。
onupdate:在数据更新的时候会调用这个参数指定的值或者函 数。在第一次插入这条数据的时候,不会用onupdate的值,只 会使用default的值。常用于是 update_time 字段(每次更新数据的 时候都要更新该字段值)。
name:指定ORM模型中某个属性映射到表中的字段名。如果不 指定,那么会使用这个属性的名字来作为字段名。如果指定了, 就会使用指定的这个值作为表字段名。这个参数也可以当作位置 参数,在第1个参数来指定。
title = Column(String(50),name=‘title’,nullable=False)
title = Column(‘my_title’,String(50),nullable=False)
from datetime import datetime
from sqlalchemy import
Column,Integer,DateTime,String
from db_util import Base,Session
class News(Base):
tablename = ‘t_news2’
id = Column(Integer,primary_key = True,autoincrement = True)
phone = Column(String(11),unique = True)
title = Column(String(32),nullable = False)
read_count = Column(Integer,default=1)
create_time = Column(DateTime,default = datetime.now)
update_time = Column(DateTime,default = datetime.now, onupdate =datetime.now ) # 当数据更新后,参数的内容才会更改
def create_data():
new1 = News(phone=‘16866666666’,title=‘测试列参数’)
with Session() as session:
session.add(new1)
session.commit()
def create_data2():
with Session() as session:
new1 = session.query(News).first()
new1.read_count = 2
session.commit()
if name == ‘main’:
create_data2()
提示
func 上,其实没有任何聚合函数。但是因为他底层做了一些 魔术,只要mysql中有的聚合函数,都可以通过func调用
from random import randint
from sqlalchemy import
Column,Integer,String,func
from db_util import Base,Session
class Item(Base):
tablename = ‘t_item’
id = Column(Integer,primary_key = True,autoincrement = True)
title = Column(String(32))
price = Column(Integer)
def create_data():
with Session() as ses:
for i in range(10):
item = Item(title = f’产品:{i+1}',price=randint(1,100))
ses.add(item)
ses.commit()
def query_model_name():
with Session() as ses:
rs = ses.query(Item).all()
for r in rs:
print(r.title)
def query_model_attr():
with Session() as ses:
rs = ses.query(Item.title,Item.price).all()
for r in rs:
print(r.price)
def query_by_func():
with Session() as ses:
rs =ses.query(func.sum(Item.price)).first()
print(rs)
if name ==‘main’:
query_by_func()
过滤是数据提取的一个很重要的功能,以下对一些常用的过滤条件 进行解释,并且这些过滤条件都是只能通过filter方法实现的:
news=session.query(News).filter(News.title ==“title1”).first()
query(User).filter(User.name != ‘ed’)
query(User).filter(User.name.like(‘%ed%’))
query(User).filter(User.name.in_([‘ed’,‘wendy’,‘jack’]))
query(User).filter(~User.name.in_([‘ed’,‘wendy’,‘jack’]))
query(User).filter(User.name==None)
query(User).filter(User.name != None)
query(User).filter(and_(User.name==‘ed’,User.fullname==‘Ed Jones’))
query(User).filter(User.name==‘ed’,User.fullname==‘Ed Jones’)
query(User).filter(User.name==‘ed’).filter(User.fullname==‘Ed Jones’)
query(User).filter(or_(User.name==‘ed’,User.name==‘wendy’))
如果想要查看orm底层转换的sql语句,可以在filter方法后面不要再 执行任何方法直接打印就可以看到了。比如:
news =session.query(News).filter(or_(News.title==‘abc’,News.content==‘abc’))
print(news)
from random import randint
from uuid import uuid4
from sqlalchemy import
Column,Integer,String,Float,Text,and_,or_
from db_util import Base,Session
class Article(Base):
tablename = ‘t_article’
id =Column(Integer,primary_key=True,autoincrement=True)
title =Column(String(50),nullable=False)
price = Column(Float,nullable=False)
content = Column(Text)
def repr(self):
return f"<Article(title:{self.title} price:{self.price} content:{self.content})>"
def create_data():
with Session() as ses:
for i in range(10):
if i%2 == 0:
art = Article(title =f’title{i+1}‘,price=randint(1,100),content= uuid4())
else:
art = Article(title =f’TITLE{i+1}’,price=randint(1,100))
ses.add(art)
ses.commit()
def query_data():
with Session() as ses:
rs =ses.query(Article).filter(Article.id ==1).first()
print(rs)
def query_data_equal():
with Session() as ses:
rs =ses.query(Article).filter(Article.title ==‘title2’).first()
print(rs)
def query_data_not_equal():
with Session() as ses:
rs =ses.query(Article).filter(Article.title !=‘title2’).all()
print(rs)
def query_data_like():
with Session() as ses:
rs =ses.query(Article).filter(Article.title.like(‘title%’)).all()
for r in rs:
print®
def query_data_in():
with Session() as ses:
rs=ses.query(Article).filter(Article.title.in_([‘title1’,‘title3’,‘title6’])).all()
for r in rs:
print®
def query_data_not_in():
with Session() as ses:
rs=ses.query(Article).filter(~Article.title.in_([‘title1’,‘title3’,‘title6’])).all()
for r in rs:
print®
def query_data_null():
with Session() as ses:
rs =ses.query(Article).filter(Article.content== None).all()
for r in rs:
print®
def query_data_not_null():
with Session() as ses:
rs =ses.query(Article).filter(Article.content!= None).all()
for r in rs:
print®
def query_data_and():
with Session() as ses:
rs =ses.query(Article).filter(and_(Article.title !=‘title4’,Article.price >50)).all()
for r in rs:
print®
def query_data_or():
with Session() as ses:
rs =ses.query(Article).filter(or_(Article.title==‘title4’,Article.price >50) ).all()
for r in rs:
print®
if name == ‘main’:
query_data_or()
表之间的关系存在三种:一对一、一对多、多对多。 而SQLAlchemy中的ORM也可以模拟这三种关系。 因为一对一其实在SQLAlchemy中底层是通过一对多的方式模拟 的,所以先来看下一对多的关系:
外键: 使用SQLAlchemy创建外键非常简单。在从表中增加一个字段,指 定这个字段外键的是哪个表的哪个字段就可以了。从表中外键的字 段,必须和主表的主键字段类型保持一致。
class User(Base):
tablename = ‘t_user’
id =Column(Integer,primary_key=True,autoincrement=True)
uname =Column(String(50),nullable=False,name=‘name’)
class News(Base):
tablename = ‘t_news’
id =Column(Integer,primary_key=True,autoincrement=True)
title =Column(String(50),nullable=False)
content = Column(Text,nullable=False)
uid =Column(Integer,ForeignKey(‘t_user.id’,)
外键约束有以下几项:
RESTRICT:若子表中有父表对应的关联数据,删除父表对应数 据,会阻止删除。默认项
NO ACTION:在MySQL中,同RESTRICT。
CASCADE:级联删除。
SET NULL:父表对应数据被删除,子表对应数据项会设置为 NULL。
from sqlalchemy import
Column,Integer,String,Text,ForeignKey
from db_util import Base,Session
class User(Base):
tablename = ‘t_user’
id =Column(Integer,primary_key=True,autoincrement=True)
uname =Column(String(50),nullable=False,name=‘name’)
class News(Base):
tablename = ‘t_news’
id =Column(Integer,primary_key=True,autoincrement=True)
title =Column(String(50),nullable=False)
content = Column(Text,nullable=False)
uid =Column(Integer,ForeignKey(‘t_user.id’,ondelete = ‘SET NULL’)) # 发现主表数据被删除时,子表的数据列会清空
def create_data():
user = User(uname = ‘zs’)
news1 =News(title=‘python’,content=‘flask’,uid = 1)
news2 =News(title=‘MySQL’,content=‘SQL’,uid = 1)
with Session() as ses:
ses.add(user)
ses.commit()
with Session() as ses:
ses.add(news1)
ses.add(news2)
ses.commit()
if name == ‘main’:
Base.metadata.create_all()
create_data()
mysql级别的外键,还不够爽,必须拿到一个表的外键,然后通过 这个外键再去另外一张表中查找,这样太麻烦了。
SQLAlchemy提供了一个 relationship ,这个类可以定义属性,以后在访 问相关联的表的时候就直接可以通过属性访问的方式就可以访问得 到了。 另外,可以通过 backref 来指定反向访问的属性名称。newss是指有多 篇新闻。他们之间的关系是一个“一对多”的关系
from sqlalchemy import
Column,Integer,String,Text,ForeignKey
from sqlalchemy.orm import relationship
from db_util import Base,Session
class User(Base):
tablename = ‘t_user’
id =Column(Integer,primary_key=True,autoincrement=True)
uname =Column(String(50),nullable=False,name=‘name’)
def repr(self):
return f’<User: id={self.id} uname={self.uname}>’
class News(Base):
tablename = ‘t_news’
id =Column(Integer,primary_key=True,autoincrement=True)
title =Column(String(50),nullable=False)
content = Column(Text,nullable=False)
uid =Column(Integer,ForeignKey(‘t_user.id’))
user =relationship(‘User’,backref=‘news’) # 将主表的数据注入到这个字段
def repr(self):
return f’<News: id={self.id} title={self.title} content={self.content} uid={self.uid}>’
def create_data():
user = User(uname = ‘sxt’)
news1 =News(title=‘Python’,content=‘flask’,uid = 1)
news2 =News(title=‘MySQL’,content=‘SQL’,uid = 1)
with Session() as ses:
ses.add(user)
ses.commit()
with Session() as ses:
ses.add(news1)
ses.add(news2)
ses.commit()
def query_data():
with Session() as ses:
1;
news1 = ses.query(News).first()
uid = news1.uid
user = ses.query(User).first()
print(user)
def query_data2():
with Session() as ses:
news1 = ses.query(News).first()
print(news1.user)
def query_data3():
with Session() as ses:
user1 = ses.query(User).first()
print(user1.news)
if name == ‘main’:
query_data3()
在sqlalchemy中,如果想要将两个模型映射成一对一的关系,那么 应该在父模型中,指定引用的时候,要传递一个 uselist=False 这个参数 进去。 就是告诉父模型,以后引用这个从模型的时候,不再是一个列表 了,而是一个对象了
class LoginUser(Base):
tablename = ‘t_user_login’
id =Column(Integer,primary_key=True,autoincrement=True)
uname =Column(String(32),nullable=False)
passwd =Column(String(32),nullable=False)
class User(Base):
tablename = ‘t_user’
id =Column(Integer,primary_key=True,autoincrement=True)
name =Column(String(32),nullable=False,name=‘name’)
gender = Column(String(1))
address = Column(String(64))
login_id =Column(Integer,ForeignKey(‘t_user_login.id’))
login_user =relationship(‘LoginUser’,backref=backref(‘user’,uselist=False))
from sqlalchemy import
Column,Integer,String,ForeignKey
from sqlalchemy import Table
from sqlalchemy.orm import
relationship,backref
from db_util import Base,Session
news_tag = Table(
‘t_news_tag’,
Base.metadata,
Column(‘news_id’,Integer,ForeignKey(‘t_news.id’),primary_key = True),
Column(‘tag_id’,Integer,ForeignKey(‘t_tag.id’),primary_key = True),)
class News(Base):
tablename = ‘t_news’
id =Column(Integer,primary_key=True,autoincrement=True)
title =Column(String(32),nullable=False)
tags =relationship(‘Tag’,backref=‘newss’,secondary= news_tag)
def repr(self):
return f’<News: id={self.id} title={self.title}>’
class Tag(Base):
tablename = ‘t_tag’
id =Column(Integer,primary_key=True,autoincrement=True)
name = Column(String(32),nullable=False)
def repr(self):
return f’<Tag: id={self.id} name={self.name}>’
def create_data():
news1 = News(title = ‘Python更新了!’)
news2 = News(title = ‘SQLAlchemy功能又强大了!’)
tag1 = Tag(name = ‘IT新闻’)
tag2 = Tag(name = ‘科学技术’)
news1.tags.append(tag1)
news1.tags.append(tag2)
news2.tags.append(tag1)
news2.tags.append(tag2)
with Session() as ses:
ses.add(news1)
ses.add(news2)
ses.commit()
def query_data():
with Session() as ses:
news = ses.query(News).first()
print(news.tags)
if name == ‘main’:
query_data()
ORM层面删除数据注意事项
ORM层面删除数据,会无视mysql级别的外键约束。 直接会将对应的数据删除,然后将从表中的那个外键设置为NULL, 也就是数据库的 SET NULL 。 如果想要避免这种行为,应该将从表中的外键的 nullable=False 。
from sqlalchemy import Column, Integer,
String, ForeignKey
from sqlalchemy.orm import relationship
from db_util import Base, Session
class User(Base):
tablename = ‘t_user’
id = Column(Integer, primary_key=True,autoincrement=True)
name = Column(String(32))
class Article(Base):
tablename = ‘t_article’
id = Column(Integer, primary_key=True,autoincrement=True)
title = Column(String(32))
uid = Column(Integer,ForeignKey(“t_user.id”))
user =relationship(‘User’,backref=‘articles’)
def create_data():
Base.metadata.drop_all() # 删除已有的表
Base.metadata.create_all() # 创建表
user = User(name=‘zs’)
art1 = Article(title=‘Python’, uid=1)
art2 = Article(title=‘MySQL’, uid=1)
user.articles.append(art1)
user.articles.append(art2)
with Session() as ses:
ses.add(user)
ses.commit()
def delete_data():
with Session() as ses:
user = ses.query(User).first()
ses.delete(user)
ses.commit()
if name == ‘main’:
delete_data()
ORM层面的relationship方法中cascade
在SQLAlchemy,只要将一个数据添加到session中,和他相关联的 数据都可以一起存入到数据库中了。 这些是怎么设置的呢?其实是通过relationship的时候,有一个关键 字参数cascade可以设置这些属性,
cascade属性值为:
save-update:默认选项。在添加一条数据的时候,会把其他和他 相关联的数据都添加到数据库中。这种行为就是save-update属性 影响的。
delete:表示当删除某一个模型中的数据的时候,是否也删掉使用 relationship和他关联的数据。 delete-orphan:表示当对一个ORM对象解除了父表中的关联对象 的时候,自己便会被删除掉。当然如果父表中的数据被删除,自己 也会被删除。这个选项只能用在一对多上,并且还需要在子模型中 的relationship中,增加一个single_parent=True的参数。
merge:默认选项。当在使用session.merge,合并一个对象的时 候,会将使用了relationship相关联的对象也进行merge操作。
expunge:移除操作的时候,会将相关联的对象也进行移除。这个 操作只是从session中移除,并不会真正的从数据库中删除。
all:是对save-update, merge, refresh-expire, expunge, delete 几种的缩写。
from sqlalchemy import Column, Integer,
String, ForeignKey
from sqlalchemy.orm import
relationship,backref
from db_util import Base, Session
class User(Base):
tablename = ‘t_user’
id = Column(Integer, primary_key=True,autoincrement=True)
name = Column(String(32))
class Article(Base):
tablename = ‘t_article’
id = Column(Integer, primary_key=True,autoincrement=True)
title = Column(String(32))
uid = Column(Integer,ForeignKey(“t_user.id”))
user =relationship(‘User’,backref=backref(‘articles’,cascade=‘saveupdate,delete,deleteorphan’))
def create_data():
Base.metadata.drop_all() # 删除已有的表
Base.metadata.create_all() # 创建表
user = User(name=‘SXT’)
art1 = Article(title=‘Python’, uid=1)
art2 = Article(title=‘MySQL’, uid=1)
user.articles.append(art1)
user.articles.append(art2)
with Session() as ses:
ses.add(user)
ses.commit()
def delete_data():
with Session() as ses:
user = ses.query(User).first()
ses.delete(user)
ses.commit()
def delete_art():
with Session() as ses:
art = ses.query(Article).first()
ses.delete(art)
ses.commit()
def update_data():
with Session() as ses:
user = ses.query(User).first()
user.articles = []
ses.commit()
if name == ‘main’:
delete_art()
order_by方法排序:可以指定根据模型中某个属性进行排序,"模型 名.属性名.desc()"代表的是降序排序。
relationship的方法中order_by属性:在指定relationship方法的时 候,添加order_by属性来指定排序的字段。
方式1:order_by方法指定
users =ses.query(User).order_by(User.age).all()
users =ses.query(User).order_by(User.age.desc()).all()
方式2:涉及两表时,定义模型时,用relationship方法中的 order_by属性指定排序方式
from random import randint
from sqlalchemy import
Column,Integer,String,ForeignKey
from sqlalchemy.orm import
relationship,backref
from db_util import Base,Session
class User(Base):
tablename = ‘t_user’
id = Column(Integer,primary_key=True,autoincrement=True)
name = Column(String(32))
age = Column(Integer)
def repr(self):
return f’<User: id={self.id} name={self.name} age={self.age}>’
class News(Base):
tablename = ‘t_news’
id =Column(Integer,primary_key=True,autoincrement=True)
title =Column(String(32),nullable=False)
content =Column(String(32),nullable=False)
read_count = Column(Integer)
uid =Column(Integer,ForeignKey(‘t_user.id’))
user =relationship(‘User’,backref=backref(‘newss’,order_by=read_count))
def repr(self):
return f’<User: id={self.id} title={self.title} content={self.content} read_count={self.read_count}>’
def create_user():
with Session() as ses:
for i in range(10):
user = User(name = f’用户{i}',age= randint(6,20))
ses.add(user)
for i in range(10):
news = News(title = f’新闻{i}',content = ‘新闻’,read_count =randint(1,1000))
user.newss.append(news)
ses.commit()
def query_user():
with Session() as ses:
users = ses.query(User).all()
for i in users[-1].newss:
print(i)
if name == ‘main’:
query_user()
注意 __mapper_args__ 参数的1.1版本已被抛弃
from random import randint
from sqlalchemy import Column,Integer,String
from db_util import Base,Session
class News(Base):
tablename = ‘t_news’
id =Column(Integer,primary_key=True,autoincrement=True)
title =Column(String(32),nullable=False)
content =Column(String(32),nullable=False)
read_count = Column(Integer)
def repr(self):
return f’<User: id={self.id} title={self.title} content={self.content} read_count={self.read_count}>’
def create_data():
Base.metadata.drop_all()
Base.metadata.create_all()
with Session() as ses:
for i in range(10):
news = News(title=f’title{i}‘,content=f’info{i}’,read_count= randint(0,1000))
ses.add(news)
ses.commit()
def query_by_limit():
with Session() as ses:
newss = ses.query(News).limit(3).all()
for n in newss:
print(n)
def query_by_offset():
with Session() as ses:
newss = ses.query(News).offset(3).all()
for n in newss:
print(n)
def query_by_page():
with Session() as ses:
newss = ses.query(News).limit(3).offset(3).all()
for n in newss:
print(n)
def query_by_slice():
with Session() as ses:
newss = ses.query(News).slice(3,6).all()
for n in newss:
print(n)
def query_by_qiepian():
with Session() as ses:
newss = ses.query(News).all()[3:6]
for n in newss:
print(n)
if name == ‘main’:
query_by_qiepian()
在一对多,或者多对多关系的时候,如果想要获取多的一方这一部 分的数据的时候,往往能通过一个属性就可以全部获取了。 如有一个作者,想要这个作者的所有文章,通过user.articles就可 以获取所有的 但有时候我们不想获取所有的数据,如只想获取这个作者今天发表 的文章,那么这时候我们可以给relationship方法添加属性 lazy=‘dynamic’ ,以后通过 user.articles 获取到的就不是一个列表,而是一个 AppenderQuery对象了。这样就可以对这个对象再进行一层过滤和 排序等操作 通过 lazy=‘dynamic’ ,获取出来的多的那一部分的数据,就是一个 AppenderQuery 对象了。这种对象既可以添加新数据,也可以跟 Query 一 样,可以再进行一层过滤.
lazy可用的选项:
1 select : (默认) 后台会用select语句一次性加载所有数据,即访问 到属性的时候,就会全部加载该属性的数据
2 joined - 数据会被JOIN语句加载,即对关联的两个表进行join操 作,从而获取到所有相关的对象 3 subquery - 数据被用subquery子查询SQL语句加载
4 dynamic :这个也是懒加载。在访问属性的时候,并不在内存中加 载数据,而是返回一个 AppenderQuery 对象, 需要执行相应方法才可 以获取对象。适用于数据量大的时候
注意
lazy=“dynamic” 只可以用在一对多和多对对关系中,不可以用在一 对一和多对一中。 这样也合理:如果返回结果很少的话,就没必要延迟加载数据了。
from random import randint
from sqlalchemy import
Column,Integer,String,ForeignKey
from sqlalchemy.orm import
relationship,backref
from db_util import Base,Session
class User(Base):
tablename = ‘t_user’
id = Column(Integer,primary_key=True,autoincrement=True)
name = Column(String(32))
age = Column(Integer)
def repr(self):
return f’<User: id={self.id} name={self.name} age={self.age}>’
class News(Base):
tablename = ‘t_news’
id =Column(Integer,primary_key=True,autoincrement=True)
title =Column(String(32),nullable=False)
content =Column(String(32),nullable=False)
read_count = Column(Integer)
uid =Column(Integer,ForeignKey(‘t_user.id’))
user =relationship(‘User’,backref=backref(‘newss’,lazy=‘dynamic’))
def repr(self):
return f’<News: id={self.id} title={self.title} content={self.content} read_count={self.read_count}>’
def create_data():
with Session() as ses:
for i in range(10):
user = User(name =f’name{i}‘,age = randint(6, 30))
ses.add(user)
for i in range(10):
news =News(title=f’title{i}’,content=f’info{i}',read_count= randint(0,1000))
user.newss.append(news)
ses.commit()
def query_data():
with Session() as ses:
users = ses.query(User)
print(users)
print(type(users))
def query_data2():
with Session() as ses:
users = ses.query(User).all()
print(users[-1].newss)
print(type(users[-1].newss))
def query_data3():
with Session() as ses:
users = ses.query(User).all()
newss =users[-1].newss.filter(News.read_count >500).all()
print(newss)
if name == ‘main’:
query_data3()
group_by
根据某个字段进行分组。如想要根据年龄进行分组,来统计每个分 组分别有多少人
r =session.query(User.age,func.count(User.id)).group_by(User.age).all()
having having
是对分组查找结果作进一步过滤。如只想要看未成年人的人 数, 那么可以首先对年龄进行分组统计人数,然后再对分组进行having 过滤。
r =session.query(User.age,func.count(User.id)).group_by(User.age).having(User.age < 18).all()
from random import randint
from sqlalchemy import
Column,Integer,String,ForeignKey,func
from sqlalchemy.orm import
relationship,backref
from db_util import Base,Session
class User(Base):
tablename = ‘t_user’
id = Column(Integer,primary_key=True,autoincrement=True)
name = Column(String(32))
age = Column(Integer)
def repr(self):
return f’<User: id={self.id} name={self.name} age={self.age}>’
def create_data():
Base.metadata.drop_all()
Base.metadata.create_all()
with Session() as ses:
for i in range(10):
user = User(name =f’name{i}',age = randint(6, 30))
ses.add(user)
ses.commit()
def query_by_age():
with Session() as ses:
user =ses.query(User.age,func.count(User.id)).group_by(User.age).all()
print(user)
def query_by_age_gt_18():
#统计 每个年龄的人数,要求排除未成年人
with Session() as ses:
user =ses.query(User.age,func.count(User.id)).group_by(User.age).having(User.age>18).all()
print(user)
def query_by_age_lt_18():
with Session() as ses:
user =ses.query(User.age,func.count(User.id)).group_by(User.age).having(User.age<18).all()
print(user)
if name ==‘main’:
query_by_age_lt_18()
Flask-SQLAlchemy是一个插件, Flask-SQLAlchemy是对SQLAlchemy进行了一个简单的封装的一个 插件, 使得我们在flask中使用sqlalchemy更加的简单。
安装 pip install flask-sqlalchemy
Flask-SQLAlchemy的使用要点
数据库连接
数据库初始化不再是通过create_engine。
1 跟sqlalchemy一样,定义好数据库连接字符串DB_URI。
2 将这个定义好的数据库连接字符串DB_URI,通过 SQLALCHEMY_DATABASE_URI 这个key名配置到 app.config 中。
app.config[“SQLALCHEMY_DATABASE_URI”] =DB_URI
3 使用 flask_sqlalchemy.SQLAlchemy 这个类定义一个对象,并将 app 传入进 去。
db = SQLAlchemy(app)
创建ORM模型类
之前都是通过Base = declarative_base()来初始化一个基类,然后 再继承,在Flask-SQLAlchemy中更加简单了
1 还是跟使用sqlalchemy一样,定义模型。现在不再是需要使用 delarative_base 来创建一个基类。 而是使用 db.Model 来作为基类
2 在模型类中, Column 、 String 、 Integer 以及 relationship 等,都不需要导入了,直接使用 db 下面相应的属性名就可以了
3 在定义模型的时候,可以不写 __tablename__ ,那么 flask_sqlalchemy 会默认使用当前的模型的名 字转换成小写来作为表的名字
并且如果这个模型的名字用到了多个单词并且使用了驼峰命名 法,那么会在多个单词之间使用下划线来进行连接
将ORM模型映射到数据库表
写完模型类后,要将模型映射到数据库的表中,使用以下代码即可
删除数据库表: db.drop_all()
创建数据库表: db.create_all()
session的使用
以后session也不需要使用 sessionmaker 来创建了, 直接使用 db.session 就可以了, 操作这个session的时候就跟之前的 sqlalchemy 的 session 是一样一样的。
添加数据
这时候就可以在数据库中看到已经生成了对应表了 添加数据和之前的没有区别,只是session成为了一个db的属性
查询数据
1 单表查询:查询数据不再是之前的session.query方法了,而是将query属性 放在了db.Model上, 所以查询就是通过“模型名.query”的方式进行查询了, query 就跟 之前的sqlalchemy中的query方法是一样用的。
2 多表查询: 如果查找数据涉及多个模型,只能使用db.session.query(模型 名).all() 这种方式
修改数据 :修改数据和之前的没有区别,只是session成为了一个db的属性
删除数据: 删除数据跟添加数据和修改数据类似,只不过session是db的一 个属性而已
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(name)
HOST = ‘192.168.30.151’ # 127.0.0.1/localhost
PORT = 3306
DATA_BASE = ‘flask_db’
USER = ‘root’
PWD = ‘123’
DB_URI = f’mysql+pymysql://{USER}:{PWD}@{HOST}:{PORT}/{DATA_BASE}’
app.config[‘SQLALCHEMY_DATABASE_URI’] =DB_URI
app.config[‘SQLALCHEMY_TRACK_MODIFICATIONS’]= False
db = SQLAlchemy(app)
class User(db.Model):
tablename = ‘t_user’ # flask_alchemy 可以忽略不写
id = db.Column(db.Integer,primary_key =True,autoincrement = True)
name = db.Column(db.String(32))
def repr(self):
return f’’
class News(db.Model):
tablename = ‘t_news’ # flask_alchemy 可以忽略不写
id = db.Column(db.Integer,primary_key =True,autoincrement = True)
content = db.Column(db.String(100))
uid =db.Column(db.Integer,db.ForeignKey(‘t_user.id’))
user =db.relationship(‘User’,backref=‘newss’)
def repr(self):
return f’’
def create_data():
user = User(name = ‘zs’)
news = News(content = ‘Python内容’)
user.newss.append(news)
db.session.add(user)
db.session.commit()
def query_data_one():
users = User.query.all()
print(users)
def query_data_many():
rs =db.session.query(User,News.content).join(News,News.uid == User.id).all()
print(rs)
def update_data():
user = User.query.first()
user.name = ‘lisi’
db.session.commit()
def delete_data():
news = News.query.first()
db.session.delete(news)
db.session.commit()
if name == “main”:
delete_data()
介绍
flask-migrate是flask的一个扩展模块,主要是扩展数据库表结构 的。 flask-migrate是基于Alembic进行的一个封装,并集成到Flask中, 所有的迁移操作其实都是Alembic做的,他能跟踪模型的变化,并 将变化映射到数据库中。
安装 pip install flask-migrate
使用方法 模型类
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(name)
HOST = ‘192.168.30.151’ # 127.0.0.1/localhost
PORT = 3306
DATA_BASE = ‘flask_db’
USER = ‘root’
PWD = ‘123’
DB_URI = f’mysql+pymysql://{USER}:{PWD}@{HOST}:{PORT}/{DATA_BASE}’
app.config[‘SQLALCHEMY_DATABASE_URI’] =DB_URI
app.config[‘SQLALCHEMY_TRACK_MODIFICATIONS’]= False
db = SQLAlchemy(app)
class User(db.Model):
tablename = ‘t_user’
id = db.Column(db.Integer,primary_key =True,autoincrement = True)
name = db.Column(db.String(32))
age = db.Column(db.Integer)
def repr(self):
return f’’
from flask_migrate import Migrate
Migrate(app,db)
注意 创建Migrate(app,db)对象
创建迁移仓库
这个命令会创建migrations文件夹,所有迁移文件都放在里面。
flask db init
生成脚本文件
flask db migrate
更新数据库
flask db upgrade
返回以前的版本
flask db downgrade version_
一个良好的项目结构努力可以清晰的看出来各个模块的作用,方便 扩展,易于修改 虽然Flask并没有强制要求开发者项目的目录层次结构应该是怎么样 的,但是如果我们以包和模块的形式组织项目的话,后期的开发会 非常的有条理。
项目结构
基本结构如下:可根据实际需求做微小调整。
|project_name
|–pro_name # 整个程序的包目录
|----init.py # 项目包文件
|----templates # 模板文件
|------common # 通用模板
|------errors # 错误页面
|------user # 用户模板
|------email # 邮件模板
|----static # 静态资源文件
|------js # JS脚本
|------css # 样式表
|------img # 图片
|------favicon.ico # 网站图表
|----user # 用户模块
|------init.py # 用户模块-包文件
|------views.py # 用户模块-视图文件
|----item # 产品模块
|------init.py # 产品模块-包文件
|------views.py # 产品模块-视图文件
|----models.py # 数据模型
|–app.py # 项目启动控制文件
|–config.py # 配置文件
|–requirements.txt # 依赖包列表
|–migrations # 数据库迁移目录
注意****1
整个程序的包目录名不能为 app ,不然会报 Error: Failed to find Flask application or factory in module ‘app’. Use ‘FLASK_APP=app:name’ to specify one.
注意2
项目启动控制文件名为 app.py ,不然会报 Error: Could not locate a Flask application. You did not provide the “FLASK_APP” environment variable, and a “wsgi.py” or “app.py” module was not found in the current directory.
解决方案2 使用.env文件解决
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
uery.first()
user.name = ‘lisi’
db.session.commit()
def delete_data():
news = News.query.first()
db.session.delete(news)
db.session.commit()
if name == “main”:
delete_data()
介绍
flask-migrate是flask的一个扩展模块,主要是扩展数据库表结构 的。 flask-migrate是基于Alembic进行的一个封装,并集成到Flask中, 所有的迁移操作其实都是Alembic做的,他能跟踪模型的变化,并 将变化映射到数据库中。
安装 pip install flask-migrate
使用方法 模型类
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(name)
HOST = ‘192.168.30.151’ # 127.0.0.1/localhost
PORT = 3306
DATA_BASE = ‘flask_db’
USER = ‘root’
PWD = ‘123’
DB_URI = f’mysql+pymysql://{USER}:{PWD}@{HOST}:{PORT}/{DATA_BASE}’
app.config[‘SQLALCHEMY_DATABASE_URI’] =DB_URI
app.config[‘SQLALCHEMY_TRACK_MODIFICATIONS’]= False
db = SQLAlchemy(app)
class User(db.Model):
tablename = ‘t_user’
id = db.Column(db.Integer,primary_key =True,autoincrement = True)
name = db.Column(db.String(32))
age = db.Column(db.Integer)
def repr(self):
return f’’
from flask_migrate import Migrate
Migrate(app,db)
注意 创建Migrate(app,db)对象
创建迁移仓库
这个命令会创建migrations文件夹,所有迁移文件都放在里面。
flask db init
生成脚本文件
flask db migrate
更新数据库
flask db upgrade
返回以前的版本
flask db downgrade version_
一个良好的项目结构努力可以清晰的看出来各个模块的作用,方便 扩展,易于修改 虽然Flask并没有强制要求开发者项目的目录层次结构应该是怎么样 的,但是如果我们以包和模块的形式组织项目的话,后期的开发会 非常的有条理。
项目结构
基本结构如下:可根据实际需求做微小调整。
|project_name
|–pro_name # 整个程序的包目录
|----init.py # 项目包文件
|----templates # 模板文件
|------common # 通用模板
|------errors # 错误页面
|------user # 用户模板
|------email # 邮件模板
|----static # 静态资源文件
|------js # JS脚本
|------css # 样式表
|------img # 图片
|------favicon.ico # 网站图表
|----user # 用户模块
|------init.py # 用户模块-包文件
|------views.py # 用户模块-视图文件
|----item # 产品模块
|------init.py # 产品模块-包文件
|------views.py # 产品模块-视图文件
|----models.py # 数据模型
|–app.py # 项目启动控制文件
|–config.py # 配置文件
|–requirements.txt # 依赖包列表
|–migrations # 数据库迁移目录
注意****1
整个程序的包目录名不能为 app ,不然会报 Error: Failed to find Flask application or factory in module ‘app’. Use ‘FLASK_APP=app:name’ to specify one.
注意2
项目启动控制文件名为 app.py ,不然会报 Error: Could not locate a Flask application. You did not provide the “FLASK_APP” environment variable, and a “wsgi.py” or “app.py” module was not found in the current directory.
解决方案2 使用.env文件解决
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
[外链图片转存中…(img-PtqFtPaX-1715809493288)]
[外链图片转存中…(img-idQoqQF2-1715809493288)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。