首页 >> 大全

Flask学习笔记(二)

2023-12-19 大全 19 作者:考证青年

8.(url,code=301)#默认是302

1. 1.3.知识点

1.在渲染模版的时候,默认会从项目根目录下的目录下查找模版。也可以在Flask初始化的时候指定来指定模版的路径。

app=Flask(__name__,template_folder="xxx")

2.当需要渲染的参数比较多时,通过关键字传参方式,给html渲染传参。

@app.route("/key")
def key():context={"a":1,"b":2,"c":3}return render_template("key.html",**context)#==render_template("key.html","a"=1,"b"=2,"c"=3)

<html><head>key</head><title>key</title><body><h1>a={{a}}</h1> <h1>b={{b}}</h1> <h1>c={{c}}</h1> </body>
</html>

3.html中,{{ … }}:用来装载变量,或者字典的key。

{% … %}:用来装载控制语句。

{# … #}:用来装载注释

4.过滤器:通过管道符号(|)进行使用,过滤器相当于是一个函数,把当前的变量传入到过滤器中,然后过滤器根据自己的功能,再返回相应的值,之后再将结果渲染到页面中。过滤器官网。

abs(value):返回一个数值的绝对值。 例如:-1|abs。

(value,,=false):如果当前变量没有值,则会使用参数中的值来代替。name|(‘’)——如果name不存在,则会使用来替代。=False默认是在只有这个变量为的时候才会使用中的值,如果想使用的形式判断是否为false,则可以传递=true。也可以使用or来替换。name

or ‘’

(value)或e:转义字符,会将等符号转义成HTML中的符号。例如:|或|e。({% off/on%} XXXXX{%%}关掉或开启局部转义)

first(value):返回一个序列的第一个元素。names|first。

(value,*arags,**):格式化字符串。例如以下代码:{{ “%s” -> “%s”|(‘Hello?’,“Foo!”) }}将输出:? - Foo!

last(value):返回一个序列的最后一个元素。示例:names|last。

(value):返回一个序列或者字典的长度。示例:names|。

join(value,d=u’'):将一个序列用d这个参数的值拼接成字符串。

safe(value):如果开启了全局转义,那么safe过滤器会将变量关掉转义。示例:|safe。

int(value):将值转换为int类型。 float(value):将值转换为float类型。

lower(value):将字符串转换为小写。 upper(value):将字符串转换为小写。

(value,old,new): 替换将old替换为new的字符串。

(value,=255,=False):截取长度的字符串。

(value):删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格。

trim:截取字符串前面和后面的空白字符。 (value):将变量转换成字符串。

(s):计算一个长字符串中单词的个数。

5.自定义过滤器(两种方式)

app.config['TEMPLATES_AUTO_RELOAD']=True
@app.template_filter('cut')
def cut(value):value=value.replace("hello",'')return value

def datetime_format(value,format="%Y年%m月%d日 %H:%M"):return value.strftime(format)
app.add_template_filter(datetime_format,"dformat")#将自定义的datetime_format,以dformat为名添加到app的过滤器中

自定义时间过滤器

@app.route("/time_filter")
def time_filter():now=datetime(2023,6,25,10,53,20)return render_template("key.html",now=now)
@app.template_filter("handle_time")
def handle_time(time):if isinstance(time,datetime):#首先判断是否时时间类型now=datetime.now()timestamp=(now-time).total_seconds()#时间间距if timestamp<60:return "刚刚"elif timestamp>=60 and timestamp<60*60:minutes=timestamp/60return "%s分钟前"%int(minutes)elif timestamp>=60*60 and timestamp<60*60*24:hours=timestamp/(60*60)return "%s小时前"%int(hours)elif timestamp>=60*60*24 and timestamp<60*60*24*30:days=timestamp/(60*60*24)return "%s天前"%int(days)else:return time.strftime('%Y/%m/%d %H:%M')else:return time

<html><body><h1>now is :{{now|handle_time}}</h1> </body>
</html>

6.for循环(for ,else ,,if),不可以使用和break表达式来控制循环的执行

   {% for user in users %}<li>{{ user.username}}</li>{% else %}<li>no users found</li>{% endfor %}{% for y in range(1,10) if y<x%}{#y从1开始循环,当不满足y

参数说明

loop.index

当前迭代的索引(从1开始)

loop.

当前迭代的索引(从0开始)

loop.first

是否是第一次迭代,返回True或False

loop.last

是否是最后一次迭代,返回True或False

loop.

序列的长度

用for循环写一个99乘法表。

@app.route("/mul")
def mul():return render_template('/mul.html')

<table border="1"><tbody>{% for x in range(1,10) %}<tr>{% for y in range(1,x+1) %}<td>{{y}}*{{x}}={{x*y}}</td>{%endfor%}</tr>{%endfor%}</tbody>
</table>

7.宏:模板中的宏跟中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个变量。

{% macro INPUT(name="",value="",type="text") %}{# 定义一个INPUT函数 #}<input name="{{name}}" value="{{value}}" type="{{type}}">
{% endmacro %}<table><tr><td>用户名</td><td>{{INPUT("username")}}</td></tr><tr><td>密码</td><td>{{INPUT("password",type="password")}}</td></tr><tr><td></td><td>{{INPUT(value="提交",type="submit")}}</td></tr>
</table>

8.导入宏

{% import 'forms.html' as forms %}
{% from 'forms.html' import input as input_field, textarea %}

如果你想要导入一个需要访问当前上下文变量的宏,有两种可能的方法:

A.显式地传入请求或请求对象的属性作为宏的参数。

B.与上下文一起(with )导入宏。

{% from '_helpers.html' import my_macro with context %}

9.语句可以把一个模板引入到另外一个模板中,类似于把一个模板的代码copy到另外一个模板的指定位置

{% include 'header.html' %}主体内容
{% include 'footer.html' %}

10.set语句:在模版中,可以使用set语句来定义全局变量。

{% set username='你好' %}
<p>用户名:{{ username }}</p>

11.with语句:定义局部变量。

{% with classroom = '你好' %}
<p>班级:{{ classroom }}</p>
{% endwith %}

{% with %}{% set classroom = '你好' %}<p>班级:{{ classroom }}</p>
{% endwith %}

12.继承:默认情况下,子模板如果实现了父模版定义的block。那么子模板block中的代码就会覆盖掉父模板中的代码。如果想要在子模板中仍然保持父模板中的代码,那么可以使用{{ super() }}来实现。

{% block body_block %}<p style="background: red;">这是父模板中的代码</p>{% endblock %}

{% block body_block %}{{ super() }}<p style="background: green;">我是子模板中的代码</p>
{% endblock %}

如果想要在模版中使用其他模版中的其他代码。那么可以通过{{ self.其他block名字() }}就可以了。

{% block title %}首页
{% endblock %}{% block body_block %}{{ self.title() }}<p style="background: green;">我是子模板中的代码</p>
{% endblock %}

1.3.2豆瓣列表页

1.3.3视图知识点

1.添加url与视图函数的映射: app.(rule,=None,=None)。如果没有填写,那么默认会使用的名字作为。以后在使用的时候,就要看在映射的时候有没有传递参数,如果传递了,那么就应该使用指定的字符串,如果没有传递,那么就应该使用的名字。 @app.route(rule,**)装饰器:这个装饰器底层,其实也是使用来实现url与视图函数映射的。

2.类视图:支持继承,但是不能跟函数视图一样,写完类视图还需要通过app.(,)来进行注册。必须继承自flask.views.View.,必须实现方法,以后请求过来后,都会执行这个方法。

标准类视图:

from flask import views,jsonify
class JSONView(views.View):#父类,格式化输出为json格式def get_data(self):raise NotImplementedErrordef dispatch_request(self):return jsonify(self.get_data())class lei_view(JSONView):#子类def get_data(self):return {"username":"xiaoming","password":"123"}app.add_url_rule("/lei/",endpoint="lei",view_func=lei_view.as_view('lei'))

基于请求方法的类视图:继承于views.。

class LoginView(views.MethodView):def get(self,error=None):return render_template("macro.html",error=error)def post(self):username=request.form.get('username')password=request.form.get('password')print(username,password)if username=="xiaoming" and password=="123":return  "登陆成功"else:return self.get(error="用户名或密码错误")
app.add_url_rule("/mylogin/",view_func=LoginView.as_view('my_login'))

3 类视图中的装饰器

函数视图:自己定义的装饰器必须放在app.route下面。否则这个装饰器就起不到任何作用。

类视图:类内定义类属性,这个类属性是一个列表或者元组都可以,里面装的就是所有的装饰器。

4.蓝图

from flask import Blueprintuser_bp = Blueprint('user',__name__)
from blueprints.user import user_bpapp.regist_blueprint(user_bp)```
#如果想要某个蓝图下的所有url都有一个url前缀,那么可以在定义蓝图的时候,指定url_prefix参数
user_bp = Blueprint('user',__name__,url_prefix='/user/')

A.在定义的时候,要注意后面的斜杠,如果给了,那么以后在定义url与视图函数的时候,就不要再在url前面加斜杠了。

B.如果项目中的文件夹中有相应的模版文件,就直接使用了。如果没有,那么就到在定义蓝图的时候指定的路径中寻找。并且蓝图中指定的路径可以为相对路径,相对的是当前这个蓝图文件所在的目录。

news_bp = Blueprint('news',__name__,url_prefix='/news',template_folder='xxx')

C.蓝图中静态文件的查找规则:

* 在模版文件中,加载静态文件,如果使用(‘’),那么就只会在app指定的静态文件夹目录下查找静态文件。

* 如果在加载静态文件的时候,指定的蓝图的名字,比如news.,那么就会到这个蓝图指定的下查找静态文件(相对于这个蓝图位置的地址)。

news_bp = Blueprint('news',__name__,url_prefix='/news',template_folder='xxx',static_folder='blue_static')

D.反转蓝图中的视图函数为url:

* 如果使用蓝图,那么以后想要反转蓝图中的视图函数为url,那么就应该在使用的时候指定这个蓝图。比如news.。否则就找不到这个。在模版中的同样也是要满足这个条件,就是指定蓝图的名字。

* 即使在同一个蓝图中反转视图函数,也要指定蓝图的名字。

E.蓝图实现子域名:

需要在主app文件中,需要配置app.的参数。app.[''] = ':5000'

在创建蓝图对象的时候,需要传递一个参数,来指定这个子域名的前缀。例如:

= ('cms',,='ccc')。

在C:\\\\etc下,找到hosts文件,然后添加域名与本机的映射。

域名和子域名都需要做映射。

127.0.0.1

127.0.0.1

1.4数据库 1.4. 1.4.1.1使用去连接数据库

1.使用一些配置信息,然后将他们组合成满足条件的字符串

from sqlalchemy import create_engine,text
HOSTNAME = '127.0.0.1'
PORT = '3306'
DATABASE = 'lessons'
USERNAME = 'root'
PASSWORD = 'xxx'
# dialect+driver://username:password@host:port/database
DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8".format(username=USERNAME,password=PASSWORD,host=HOSTNAME,port=PORT,db=DATABASE)

2.然后使用创建一个引擎,然后再调用这个引擎的方法,就可以得到这个对象,然后就可以通过这个对象对数据库进行操作

engine = create_engine(DB_URI)
conn = engine.connect()

3.判断是否连接成功

result = conn.execute(text("select 1"))
print(result.fetchone())

1.4.1.2将ORM模型映射到数据库中

(对象模型与数据库表的映射)

1.用创建一个ORM基类(base)

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

2.用这个Base类作为基类来写自己的ORM类。

class Person(Base):__tablename__ = 'person'id = Column(Integer,primary_key=True,autoincrement=True)name = Column(String(50))age = Column(Integer)country = Column(String(50))

3.使用Base..()根据来将模型映射到数据库中。一旦使用Base..()将模型映射到数据库中后,即使改变了模型的字段,也不会重新映射了。

Base.metadata.create_all(engine)

1.4.1.3用做数据的增删改查操作

1.构建对象:所有数据库的ORM操作都必须通过一个叫做的会话对象来实现.

from sqlalchemy.orm import sessionmaker
session = sessionmaker(engine)()

2.增

def add_data():p1 = Person(name='zhiliao1',age=19,country='china')p2 = Person(name='zhiliao2',age=20,country='china')session.add_all([p1,p2])session.commit()

3.删

def delete_data():person = session.query(Person).first()session.delete(person)session.commit()

4.改

def update_data():person = session.query(Person).first()person.name = 'ketang'session.commit()

5.查

def search_data():person = session.query(Person).first()print(person)

1.4.1.常用数据类型 :整形,映射到数据库中是int类型。Float:浮点类型,映射到数据库中是float类型。他占据的32位。:双精度浮点类型,映射到数据库中是类型,占据64位。:可变字符类型,映射到数据库中是类型.:布尔类型,映射到数据库中的是类型。:定点类型。是专门为了解决浮点类型精度丢失的问题的。在存储钱相关的字段的时候建议大家都使用这个数据类型。并且这个类型使用的时候需要传递两个参数,第一个参数是用来标记这个字段总能能存储多少个数字,第二个参数表示小数点后有多少位。Enum:枚举类型。指定某个字段只能是枚举中指定的几个值,不能为其他值。Date:存储时间,只能存储年月日。映射到数据库中是date类型。在代码中,可以使用.date来指定。:存储时间,可以存储年月日时分秒毫秒等。映射到数据库中也是类型。Time:存储时间,可以存储时分秒。映射到数据库中也是time类型。在代码中,可以使用.:存储长字符串。

一般可以存储6W多个字符。如果超出了这个范围,可以使用类型。映射到数据库中就是text类型。:长文本类型,映射到数据库中是类型。 1.4.1.常用参数 :设置某个字段为主键。:设置这个字段为自动增长的。:设置某个字段的默认值。在发表时间这些字段上面经常用。:指定某个字段是否为空。默认值是True,就是可以为空。:指定某个字段的值是否唯一。默认是False。:在数据更新的时候会调用这个参数指定的值或者函数。在第一次插入这条数据的时候,不会用的值,只会使用的值。常用的就是(每次更新数据的时候都要更新的值)。name:指定ORM模型中某个属性映射到表中的字段名。如果不指定,那么会使用这个属性的名字来作为字段名。如果指定了,就会使用指定的这个值作为参数。这个参数也可以当作位置参数,在第1个参数来指定。 1.4.1.可用参数: 模型对象。指定查找这个模型中所有的对象。模型中的属性。可以指定只查找某个模型的其中几个属性。聚合函数。 1.4.1.过滤条件 :

  article = session.query(Article).filter(Article.title == "title0").first()

not :

query.filter(User.name != 'ed')

like:(ilike)不区分大小写

query.filter(User.name.like('%ed%'))

in_:

query.filter(User.name.in_(['ed','wendy','jack']))
# 同时,in也可以作用于一个Query
query.filter(User.name.in_(session.query(User.name).filter(User.name.like('%ed%'))))

not in:

query.filter(~User.name.in_(['ed','wendy','jack']))

is null:

query.filter(User.name==None)
# 或者是
query.filter(User.name.is_(None))

is not null:

Flask学习笔记(二)__Flask学习笔记(二)

query.filter(User.name != None)
# 或者是
query.filter(User.name.isnot(None))

and_:

from sqlalchemy import and_
query.filter(and_(User.name=='ed',User.fullname=='Ed Jones'))
# 或者是传递多个参数
query.filter(User.name=='ed',User.fullname=='Ed Jones')
# 或者是通过多次filter操作
query.filter(User.name=='ed').filter(User.fullname=='Ed Jones')

or:

from sqlalchemy import or_  query.filter(or_(User.name=='ed',User.name=='wendy'))

如果想要查看orm底层转换的sql语句,可以在方法后面不要再执行任何方法直接打印就可以看到了

1.4.1.8外键

从表中外键的字段,必须和父表的主键字段类型保持一致

外键约束有以下几项():

:父表数据被删除,会阻止删除。默认就是这一项。NO :在MySQL中,同。:级联删除。SET NULL:父表数据被删除,子表数据会设置为NULL。 1.4.1.9ORM关系以及一对多

class User(Base):__tablename__ = 'user'id = Column(Integer,primary_key=True,autoincrement=True)username = Column(String(50),nullable=False)# articles = relationship("Article")def __repr__(self):return "" % self.usernameclass Article(Base):__tablename__ = 'article'id = Column(Integer,primary_key=True,autoincrement=True)title = Column(String(50),nullable=False)content = Column(Text,nullable=False)uid = Column(Integer,ForeignKey("user.id"))author = relationship("User",backref="articles")

另外,可以通过来指定反向访问的属性名称。是有多个。他们之间的关系是一个一对多的关系。

1.4.1.10ORM关系以及一对一

如果想要将两个模型映射成一对一的关系,那么应该在父模型中,指定引用的时候,要传递一个=False这个参数进去。就是告诉父模型,以后引用这个从模型的时候,不再是一个列表了,而是一个对象了。

class User(Base):__tablename__ = 'user'id = Column(Integer,primary_key=True,autoincrement=True)username = Column(String(50),nullable=False)extend = relationship("UserExtend",uselist=False)def __repr__(self):return "" % self.usernameclass UserExtend(Base):__tablename__ = 'user_extend'id = Column(Integer, primary_key=True, autoincrement=True)school = Column(String(50))uid = Column(Integer,ForeignKey("user.id"))user = relationship("User",backref="extend")

当然,也可以借助.orm.来简化代码:

class User(Base):__tablename__ = 'user'id = Column(Integer,primary_key=True,autoincrement=True)username = Column(String(50),nullable=False)# extend = relationship("UserExtend",uselist=False)def __repr__(self):return "" % self.usernameclass UserExtend(Base):__tablename__ = 'user_extend'id = Column(Integer, primary_key=True, autoincrement=True)school = Column(String(50))uid = Column(Integer,ForeignKey("user.id"))user = relationship("User",backref=backref("extend",uselist=False))

1.4.1.11ORM关系以及多对多

多对多的关系需要通过一张中间表来绑定他们之间的关系。在两个需要做多对多的模型中随便选择一个模型,定义一个属性,来绑定三者之间的关系,在使用的时候,需要传入一个=中间表。

先把两个需要做多对多的模型定义出来

class Article(Base):__tablename__ = 'article'id = Column(Integer,primary_key=True,autoincrement=True)title = Column(String(50),nullable=False)# tags = relationship("Tag",backref="articles",secondary=article_tag)def __repr__(self):return "" % self.titleclass Tag(Base):__tablename__ = 'tag'id = Column(Integer, primary_key=True, autoincrement=True)name = Column(String(50), nullable=False)articles = relationship("Article",backref="tags",secondary=article_tag)def __repr__(self):return "" % self.name

使用Table定义一个中间表,中间表一般就是包含两个模型的外键字段就可以了,并且让他们两个来作为一个“复合主键”。

article_tag = Table("article_tag",Base.metadata,Column("article_id",Integer,ForeignKey("article.id"),primary_key=True),Column("tag_id",Integer,ForeignKey("tag.id"),primary_key=True)
)

1.4.1.12ORM层面的删除数据:

ORM层面删除数据,会无视mysql级别的外键约束。直接会将对应的数据删除,然后将从表中的那个外键设置为NULL。如果想要避免这种行为,应该将从表中的外键的=False。

在,只要将一个数据添加到中,和他相关联的数据都可以一起存入到数据库中了。这些是怎么设置的呢?其实是通过的时候,有一个关键字参数可以设置这些属性:

save-:默认选项。在添加一条数据的时候,会把其他和他相关联的数据都添加到数据库中。这种行为就是save-属性影响的。:表示当删除某一个模型中的数据的时候,是否也删掉使用和他关联的数据。-:表示当对一个ORM对象解除了父表中的关联对象的时候,自己便会被删除掉。当然如果父表中的数据被删除,自己也会被删除。这个选项只能用在一对多上,不能用在多对多以及多对一上。并且还需要在子模型中的中,增加一个=True的参数。merge:默认选项。当在使用.merge,合并一个对象的时候,会将使用了相关联的对象也进行merge操作。:移除操作的时候,会将相关联的对象也进行移除。这个操作只是从中移除,并不会真正的从数据库中删除。all:是对save-, merge, -, , 几种的缩写。 1.4.1.13排序:

:可以指定根据这个表中的某个字段进行排序,如果在前面加了一个-,代表的是降序排序。

在模型定义的时候指定默认排序:有些时候,不想每次在查询的时候都指定排序的方式,可以在定义模型的时候就指定排序的方式。有以下两种方式:

= {

“”: title

即可让文章使用标题来进行排序。

正序排序与倒序排序:默认是使用正序排序。如果需要使用倒序排序,那么可以使用这个字段的desc()方法,或者是在排序的时候使用这个字段的字符串名字,然后在前面加一个负号。

1.4.1.、和切片操作: limit:可以限制每次查询的时候只查询几条数据。:可以限制查找数据的时候过滤掉前面多少条。切片:可以对Query对象使用切片操作,来获取想要的数据。可以使用slice(start,stop)方法来做切片操作。也可以使用[start:stop]的方式来进行切片操作。一般在实际开发中,中括号的形式是用得比较多的。希望大家一定要掌握。示例代码如下:

articles = session.query(Article).order_by(Article.id.desc())[0:10]

1.4.1.15懒加载

在一对多,或者多对多的时候,如果想要获取多的这一部分的数据的时候,往往能通过一个属性就可以全部获取了。比如有一个作者,想要或者这个作者的所有文章,那么可以通过user.就可以获取所有的。但有时候我们不想获取所有的数据,比如只想获取这个作者今天发表的文章,那么这时候我们可以给传递一个lazy=‘’,以后通过user.获取到的就不是一个列表,而是一个对象了。这样就可以对这个对象再进行一层过滤和排序等操作。

通过lazy='',获取出来的多的那一部分的数据,就是一个对象了。这种对象既可以添加新数据,也可以跟Query一样,可以再进行一层过滤。

总而言之一句话:如果你在获取数据的时候,想要对多的那一边的数据再进行一层过滤,那么这时候就可以考虑使用lazy=''。

lazy可用的选项:

:这个是默认选项。还是拿user.的例子来讲。如果你没有访问user.这个属性,那么就不会从数据库中查找文章。一旦你访问了这个属性,那么就会立马从数据库中查找所有的文章,并把查找出来的数据组装成一个列表返回。这也是懒加载。:这个就是我们刚刚讲的。就是在访问user.的时候返回回来的不是一个列表,而是对象。

根据某个字段进行分组。比如想要根据性别进行分组,来统计每个分组分别有多少人,那么可以使用以下代码来完成:

session.query(User.gender,func.count(User.id)).group_by(User.gender).all()

1.4.1.:

是对查找结果进一步过滤。比如只想要看未成年人的数量,那么可以首先对年龄进行分组统计人数,然后再对分组进行过滤。示例代码如下:

result = session.query(User.age,func.count(User.id)).group_by(User.age).having(User.age >= 18).all()

1.4.1.17 join: join分为left join(左外连接)和right join(右外连接)以及内连接(等值连接)。参考的网页:在中,使用join来完成内连接。在写join的时候,如果不写join的条件,那么默认将使用外键来作为条件连接。query查找出来什么值,不会取决于join后面的东西,而是取决于query方法中传了什么参数。就跟原生sql中的 后面那一个一样。

比如现在要实现一个功能,要查找所有用户,按照发表文章的数量来进行排序。示例代码如下:

result = session.query(User,func.count(Article.id)).join(Article).group_by(User.id).order_by(func.count(Article.id).desc()).all()

1.4.1.

子查询可以让多个查询变成一个查询,只要查找一次数据库,性能相对来讲更加高效一点。不用写多个sql语句就可以实现一些复杂的查询。那么在中,要实现一个子查询,应该使用以下几个步骤:

将子查询按照传统的方式写好查询代码,然后在query对象后面执行方法,将这个查询变成一个子查询。在子查询中,将以后需要用到的字段通过label方法,取个别名。在父查询中,如果想要使用子查询的字段,那么可以通过子查询的返回值上的c属性拿到。

整体的示例代码如下:

stmt = session.query(User.city.label("city"),User.age.label("age")).filter(User.username=='李A').subquery()
result = session.query(User).filter(User.city==stmt.c.city,User.age==stmt.c.age).all()

1.4. 1.4.2.-

pip install flask-sqlalchemy

1.链接数据库

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
HOSTNAME='127.0.0.1'
PORT='3306'
DATABASE='lessons_flask_sqlalchemy'
USERNAME='root'
PASSWORD='xxx'
DB_URI="mysql+pymysql://{username}:{password}@{host}:{port}/{database}?charset=utf8mb4".format(username=USERNAME,password=PASSWORD,host=HOSTNAME,port=PORT,database=DATABASE)
app=Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI']=DB_URI
db=SQLAlchemy(app)

2.创建表类,并映射到数据库中

class User(db.Model):__tablename__='user'#默认类名小写后的名字id=db.Column(db.Integer,primary_key=True,autoincrement=True)username=db.Column(db.String(50),nullable=False)
class Article(db.Model):__tablename__='article'id=db.Column(db.Integer,primary_key=True,autoincrement=True)title=db.Column(db.String(50),nullable=False)uid=db.Column(db.Integer,db.ForeignKey("user.id"))author=db.relationship("User",backref="articles")
with app.app_context():#在试图函数里运行时就不要,在试图函数外运行时需要db.drop_all()db.create_all()

3.利用会话将数据添加到数据表中。

user=User(username="张三")
article=Article(title='title1')
article.author=user
with app.app_context():#在试图函数里运行时就不要,在试图函数外运行时需要db.session.add(article)db.session.commit()

4.查

with app.app_context():users=User.query.all()#4.查询print(users)

5.改

with app.app_context():user=User.query.filter(User.username=="张三").first()user.username="王五"db.session.commit()

6.删

with app.app_context():user=User.query.filter(User.username=="王五").first()db.session.delete(user)db.session.commit()

1.4.2.

用来做orm模型与数据库的迁移和映射

pip install alembic

1.下使用

from sqlalchemy import Column,String,Integer,create_engine
from sqlalchemy.ext.declarative import declarative_base
HOSTNAME='127.0.0.1'
PORT='3306'
DATABASE='alembic1'
USERNAME='root'
PASSWORD='xxx'
DB_URI="mysql+pymysql://{username}:{password}@{host}:{port}/{database}?charset=utf8mb4".format(username=USERNAME,password=PASSWORD,host=HOSTNAME,port=PORT,database=DATABASE)
engine=create_engine(DB_URI)
Base=declarative_base()
class User(Base):__tablename__="user"id=Column(Integer,primary_key=True,autoincrement=True)username=Column(String(50),nullable=False)
Base.metadata.create_all()

在工程文件夹下初始化仓库:

alembic init alembic

修改.ini文件:

sqlalchemy.url = mysql+pymysql://root:xxx@localhost/alembic1

修改env.py文件:

import sys,os
sys.path.append(os.path.dirname(os.path.dirname(__file__))+"/ALCH/")#orm定义文件alembic_sql.py的文件夹地址
from  alembic_sql import Base#这是我的ORM的py文件
...
target_metadata=Base.metadata#创建设置模型的元类

orm生成迁移脚本

alembic revision --autogenerate -m "my_first_commit"

迁移脚本映射到数据库

alembic upgrade head

命令和参数解释:

init:创建一个仓库。

:创建一个新的版本文件。

–:自动将当前模型的修改,生成迁移脚本。

-m:本次迁移做了哪些修改,用户可以指定这个参数,方便回顾。

:将指定版本的迁移文件映射到数据库中,会执行版本文件中的函数。如果有多个迁移脚本没有被映射到数据库中,那么会执行多个迁移脚本。

[head]:代表最新的迁移脚本的版本号。

:会执行指定版本的迁移文件中的函数。

heads:展示head指向的脚本文件版本号。

:列出所有的迁移版本及其信息。

:展示当前数据库中的版本号。

错误描述原因解决办法

: is not up to date.

主要是heads和不相同。落后于heads的版本。

将移动到head上。 head

: Can’t by ‘’

数据库中存的版本号不在迁移脚本文件中

删除数据库的表中的数据,重新执行 head

2.flask-下使用

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
HOSTNAME='127.0.0.1'
PORT='3306'
DATABASE='alem3'
USERNAME='root'
PASSWORD='xxx'
DB_URI="mysql+pymysql://{username}:{password}@{host}:{port}/{database}?charset=utf8mb4".format(username=USERNAME,password=PASSWORD,host=HOSTNAME,port=PORT,database=DATABASE)
app=Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI']=DB_URI
db=SQLAlchemy(app)
class User(db.Model):__tablename__='user'id=db.Column(db.Integer,primary_key=True,autoincrement=True)username=db.Column(db.String(50),nullable=False)
@app.route("/")
def hello():return "hello"
if __name__=="__main__":app.run()

在工程文件夹下初始化仓库:

alembic init alembic

修改.ini文件:

sqlalchemy.url = mysql+pymysql://root:xxx@localhost/alem3

修改env.py文件:

import sys,os
sys.path.append(os.path.dirname(os.path.dirname(__file__))+"/ALCH/")#orm定义文件alembic_sql.py的文件夹地址
import fla_sql_aql #这是我的ORM的py文件
...
target_metadata=fla_sql_aql.db.Model.metadata#创建设置模型的元类

orm生成迁移脚本

alembic revision --autogenerate -m "my_first_commit"

迁移脚本映射到数据库

alembic upgrade head

1.4.2.-

Flask-的作用是可以通过命令行的形式来操作Flask。例如通过命令跑一个开发版本的服务器、设置数据库,定时任务等。要使用Flask-,可以通过pip flask-安装最新版本。(这里降低了flask的版本pip3 flask==1.1.2)

@manager.command
def hello():#1.自定义commandprint("你好")
@manager.option("-u","--username",dest="username")
@manager.option("-a","--age",dest="age")
def u_a(username,age):#2.定义传参commandprint("当前用户是:%s,年龄是:%s"%(username,age))

from db_script import db_manager
manager.add_command("db",db_manager)

1.4.2.-

from flask_script import Manager
from f_m import app
from exts import db
from flask_migrate import Migrate,MigrateCommand
from models import User
manager=Manager(app)
Migrate(app,db)
manager.add_command("db",MigrateCommand)if __name__=="__main__":manager.run()

1.5知识点补充 1.5.-WTF

Flask-WTF是简化了操作的一个第三方库。表单的两个主要功能是验证用户提交数据的合法性以及渲染模板。当然还包括一些其他的功能:CSRF保护,文件上传等。安装Flask-WTF默认也会安装.

pip install flask-wtf

1.5.1.1表单验证

from flask import Flask,request,render_template
from wtforms import Form,StringField
from wtforms.validators import Length,EqualTo
class RegisterForm(Form):username=StringField(validators=[Length(min=3,max=10,message="用户名要3-10位长")])password=StringField(validators=[Length(min=6,max=10)])password_repeat=StringField(validators=[Length(min=6,max=10),EqualTo("password")])
app=Flask(__name__)
@app.route("/reg/",methods=['GET','POST'])
def reg():if request.method=='GET':return render_template('regist.html')else:form=RegisterForm(request.form)if form.validate():return "success"else:print(form.errors)return "fail"
if __name__=="__main__":app.run(debug=True)

常用的验证器:

数据发送过来,经过表单验证,因此需要验证器来进行验证,以下对一些常用的内置验证器进行讲解:

验证器功能

Email

验证上传的数据是否为邮箱。

验证上传的数据是否和另外一个字段相等,常用的就是密码和确认密码两个字段是否相等。

原始数据的需要验证。如果不是特殊情况,应该使用。

长度限制,有min和max两个值进行限制。

数字的区间,有min和max两个值限制,如果处在这两个数字之间则满足。

自定义正则表达式。

URL

必须要是URL的形式。

UUID

验证UUID。

自定义验证器:

class IndexForm(Form):captcha=StringField(validators=[Length(6,6)])def validate_captcha(self,field):#自定义函数(validate_)field=captchaif field.data!="123456":raise ValidationError("验证码错误")

1.5.1.2模版渲染

class SettingsForm(Form):username=StringField("用户名",validators=[InputRequired()])

@app.route("/settings/",methods=['GET','POST'])
def settings():if request.method=='GET':form =SettingsForm()return render_template("settings.html",form=form)else:form=SettingsForm(request.form)

<tr><td>{{form.username.label}}</td><td>{{form.username()}}</td>
</tr>

1.5.1.3文件上传

1.上传文件

import os
from werkzeug.utils import secure_filename
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")#获取描述信息avatar=request.files.get("avatar")#获取文件filename=secure_filename(avatar.filename)#上传文件的名字做安全处理avatar.save(os.path.join(UPLOAD_PATH,filename))#保存文件print(desc)return "文件上传成功"

<html><head><meta charset="UTF-8"><title>上传文件</title></head><body><form action="" method="post" enctype="multipart/form-data"><table><tbody><tr><td>头像:</td><td><input type="file" name="avatar"></td></tr><tr><td>描述:</td><td><input type="text" name="desc"></td></tr><tr><td></td><td><input type="submit" value="提交"></td></tr></tbody></table></form></body>
</html>

2.返回文件

from flask import end_from_directory
@app.route("/images//")
def get_image(filename):return send_from_directory(UPLOAD_PATH,filename)

3.上传文件验证

from wtforms import Form,FileField,StringField
from wtforms.validators import InputRequired
from flask_wtf.file import FileAllowed,FileRequired
class UploadForm(Form):avatar=FileField(validators=[FileRequired(),FileAllowed(['jpg','png','gif'])])desc=StringField(validators=[InputRequired()])

from forms import UploadForm
from werkzeug.datastructures import CombinedMultiDict
@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获取描述信息avatar=form.avatar.data#获取文件filename=secure_filename(avatar.filename)#上传文件的名字做安全处理avatar.save(os.path.join(UPLOAD_PATH,filename))#保存文件print(desc)return "文件上传成功"else:print(form.errors)return "fail"

1.5.和 1.5.2.

1.设置,在对象上使用set-方法。

@app.route("/")
def coo():resp=Response("hello")resp.set_cookie("username","zoe")#键,值return resp

2.删除,在对象上使用方法。

@app.route('/dele/')
def dele():resp=Response("done")resp.delete_cookie('username')#键return resp

3.设置的有效期

默认是浏览会话结束,即浏览器关闭后失效。

resp.set_cookie("username","zoe",max_age=60)#max_age(有效期多少秒)

from datetime import datetime,timedeltadue=datetime.now()+timedelta(days=30,hours=16)#使用格林尼治时间,即比北京时间少8小时resp.set_cookie("username","zoe",expires=due)#使用expires设置截止时间

4.设置的有效域名

resp.set_cookie("username","zoe",domain='.my.com')#y.com下的所有子域名都可用此cookie

from flask import Blueprint,request
bp=Blueprint('cook',__name__,subdomain='cook')
@bp.route('/')
def index():username=request.cookies.get('username')return username or "没有获取到cookie"

from  cookie_view import bp 
app=Flask(__name__)
app.register_blueprint(bp)
app.config['SERVER_NAME']='my.com:5000'

设置host的域名和端口映射

1.5.2.

1.设置

from flask import Flask,session
import os
from datetime import timedelta
app=Flask(__name__)
app.config['SECRET_KEY']=os.urandom(24)#对session内容进行加密,然后放到cookie中。
@app.route('/')
def index():session['username']='zoe'return 'hello'

2.获取

@app.route('/get_session/')
def get_session():username=session.get('username')return username or '没有拿到session'

3.删除

@app.route('/del_session/')
def del_session():# session.pop['username']session.clear()#删除session中的所有信息return '删除成功'

4…设置有效期,默认是When the ends

app.config['PERMANENT_SESSION_LIFETIME']=timedelta(days=2)#比31天延长多久
@app.route('/set_session/')
def set_session():session['username']='zoe'session.permanent=True#默认保留31天,设置app.config['PSERMANENT_SESSION_LIFETIME']后按延长时间return 'hello'

1.5.3CSRF攻击与防御

1.CSRF(Cross Site , 跨站域请求伪造)是一种网络的攻击方式,如果你访问了一个别有用心或病毒网站,这个网站可以在网页源代码中插入js代码,使用js代码给其他服务器发送请求(比如ICBC的转账请求)。那么因为在发送请求的时候,浏览器会自动的把发送给对应的服务器,这时候相应的服务器(比如ICBC网站),就不知道这个请求是伪造的,就被欺骗过去了。从而达到在用户不知情的情况下,给某个服务器发送了一个请求(比如转账)。

from flask_wtf import CSRFProtect
CSRFProtect(app)

<input type="hidden" name="csrf_token" value="{{csrf_token()}}">

2.Ajax处理csrf

	'_ajaxSetup': function() {$.ajaxSetup({'beforeSend':function(xhr,settings) {if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {var csrftoken = $('meta[name=csrf-token]').attr('content');xhr.setRequestHeader("X-CSRFToken", csrftoken)}}});}

1.5.上下文

1.单线程

from threading import Thread
request='123'
class Mythread(Thread):def run(self):global requestrequest='abc'print("子线程:",request)#子线程: abc
mythread=Mythread()
mythread.start()
mythread.join()
print("主线程:",request)#主线程: abc

2.绑定在Local对象上的属性,在每个线程中都是隔离的

from threading import Thread
from werkzeug.local import Local
local=Local()
local.request='123'
class Mythread(Thread):def run(self):local.request='abc'print("子线程:",local.request)#子线程: abc
mythread=Mythread()
mythread.start()
mythread.join()
print("主线程:",local.request)#主线程: 123

3.应用上下文

with app.app_context():print(current_app.name)#context_flask

4.请求上下文

with app.test_request_context():print(url_for('m_l'))

5.g对象

g全局使用,多线程。

1.5.5钩子函数

:处理第一次请求之前执行。例如以下代码:

  @app.before_first_requestdef first_request():print 'first time request'

:在每次请求之前执行。通常可以用这个装饰器来给视图函数增加一些变量。例如以下代码:

  @app.before_requestdef before_request():if not hasattr(g,'user'):setattr(g,'user','xxxx')

:不管是否有异常,注册的函数都会在每次请求之后执行。

  @app.teardown_appcontextdef teardown(exc=None):if exc is None:db.session.commit()else:db.session.rollback()db.session.remove()

:在使用模板的时候自定义过滤器。比如可以增加一个upper的过滤器(当然已经存在这个过滤器,本示例只是为了演示作用):

@app.template_filterdef upper_filter(s):return s.upper()

:上下文处理器。返回的字典中的键可以在模板上下文中使用。必须返回一个字典,无参数就返回{}空字典,例如:

  @app.context_processorreturn {'current_user':'xxx'}

:接收状态码,可以自定义返回这种状态码的响应的处理方法。例如:

  @app.errorhandler(404)def page_not_found(error):return 'This page does not exist',404

1.5.信号

flask中的信号使用的是一个第三方插件,叫做。

1.自定义信号

2.内置信号

flask.template_rendered:模版渲染完毕后发送,示例如下:from flask import template_rendereddef log_template_renders(sender,template,context,*args):print 'sender:',senderprint 'template:',templateprint 'context:',contexttemplate_rendered.connect(log_template_renders,app)

flask.:请求开始之前,在到达视图函数之前发送,订阅者可以调用之类的标准全局代理访问请求。示例如下:

def log_request_started(sender,**extra):print 'sender:',senderprint 'extra:',extra
request_started.connect(log_request_started,app)

flask.:请求结束时,在响应发送给客户端之前发送,可以传递,示例代码如下:

def log_request_finished(sender,response,*args):print 'response:',response
request_finished.connect(log_request_finished,app)

flask.n:在请求过程中抛出异常时发送,异常本身会通过传递到订阅的函数。示例代码如下:

def log_exception_finished(sender,exception,*args):print 'sender:',senderprint type(exception)
got_request_exception.connect(log_exception_finished,app)

flask.:请求被销毁的时候发送,即使在请求过程中发生异常,也会发送,示例代码如下:

def log_request_tearing_down(sender,**kwargs):print 'coming...'
request_tearing_down.connect(log_request_tearing_down,app)

flask.own:在应用上下文销毁的时候发送,它总是会被调用,即使发生异常。示例代码如下:

def log_appcontext_tearing_down(sender,**kwargs):print 'coming...'
appcontext_tearing_down.connect(log_appcontext_tearing_down,app)

1.5.-插件

pip install flask-restful

1.定义的视图

如果使用Flask-,那么定义视图函数的时候,就要继承自.类,然后再根据当前请求的来定义相应的方法。比如期望客户端是使用get方法发送过来的请求,那么就定义一个get方法。类似于。

from flask import Flask
from flask_restful import Api,Resource
app = Flask(__name__)
# 用Api来绑定app
api = Api(app)
class IndexView(Resource):def post(self):return {"username":"zoe"}
api.add_resource(IndexView,'/',endpoint='index')
if __name__ == '__main__':app.run(debug=True)

用工具来测试:

2.参数解析:Flask-插件提供了类似来验证提交的数据是否合法的包,叫做。

1.5.

1.安装启动:

:管理员身份

安装:.exe -d 。

启动:.exe -d start。

可能出现的问题: 提示你没有权限:在打开cmd的时候,右键使用管理员身份运行。

提示缺少.dll文件:将.dll文件拷贝到/.

不要放在含有中文的路径下面。

-d:这个参数是让在后台运行。

-m:指定占用多少内存。以M为单位,默认为64M。

-p:指定占用的端口。默认端口是11211。

-l:别的机器可以通过哪个ip地址连接到我这台服务器。如果是通过 start的方式,那么只能通过本机连接。如果想要让别的机器连接,就必须设置-l 0.0.0.0。

如果想要使用以上参数来指定一些配置信息,那么不能使用 start,而应该使用/usr/bin/的方式来运行。比如/usr/bin/ -u -m 1024 -p 11222 start。

3.操作:

可能出现的问题:‘’ 不是内部或外部命令,也不是可运行的程序或批处理文件。将功能打开。

set和add的区别:add是只负责添加数据,不会去修改数据。如果添加的数据的key已经存在了,则添加失败,如果添加的key不存在,则添加成功。而set不同,如果中不存在相同的key,则进行添加,如果存在,则替换。

set username 0 120 3       #1.set key [0,1](是否压缩) timeout(缓存过期时间) value_length(数据长度)
zoe      #value
STORED
get username       #2.get key:获取此key对应的value值
VALUE username 0 3
zoe
END
add age 0 120 2         #3.add key [0,1](是否压缩) timeout(0=forever) value_length(数据长度)
18
STORED
ci
ERROR
incr age 2         #4.incr key num:给key的value数值加上几    
20
get age
VALUE age 0 2
20
END
decr age 4           #5.decr key num:给key的value数值减去几 
16
get age
VALUE age 0 2
16
END
del
ERROR
delete age                   #6.delete key:删除键值对
DELETED
get age
END
get username
VALUE username 0 3
zoe
END
flush_all               #7.flush_all:删除memcached中的所有数据。
OK
get username
END
stats          #8.stats:查看memcached的当前状态

4.操作:

安装

pip install python-memcached

连接

 import memcachemc = memcache.Client(['127.0.0.1:11211','192.168.174.130:11211'],debug=True)# 在连接之前,一定要切记先启动memcached

操作数据

mc.set('username','hello world',time=60*5)#默认time=0:forever
mc.set_multi({'email':'xxx@qq.com','telphone':'111111'},time=60*5)#set多条键值对mc.get('telphone')mc.delete('email')mc.incr('age',delta=10)#默认是1mc.decr('read_count')

5.的安全性

的操作不需要任何用户名和密码,只需要知道服务器的ip地址和端口号即可。因此使用的时候尤其要注意他的安全性。这里提供两种安全的解决方案。

A.使用-l参数设置为只有本地可以连接:这种方式,就只能通过本机才能连接,别的机器都不能访问,可以达到最好的安全性。

B.使用防火墙,关闭11211端口,外面也不能访问。

ufw # 开启防火墙

ufw # 关闭防火墙

ufw deny # 防火墙以禁止的方式打开,默认是关闭那些没有开启的端口

ufw deny 端口号 # 关闭某个端口

ufw allow 端口号 #开启某个端口

1.5. 3.错误

1.: ‘’ for email .

解决办法:降低的版本

pip install wtforms==2.2.1

参考:

2.No named ‘flask.’

解决办法:改为

from flask_script._compat import text_type

3. name ‘’ from ‘’

解决办法:

pip3 install flask==1.1.2

参考:

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了