二 Flask学习笔记

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)
keykeya={{a}} b={{b}} c={{c}}
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=http://www.kingceram.com/post/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
now is :{{now|handle_time}}
6.for循环(for ,else ,,if),不可以使用和break表达式来控制循环的执行
{% for user in users %}

  • {{ user.username}}
  • {% else %}
  • no users found
  • {% endfor %}{% for y in range(1,10) if 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')
    {% for x in range(1,10) %}{% for y in range(1,x+1) %}{{y}}*{{x}}={{x*y}}{%endfor%}{%endfor%}
    7.宏:模板中的宏跟中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个变量 。
    {% macro INPUT(name="",value="",type="text") %}{# 定义一个INPUT函数 #}{% endmacro %}用户名{{INPUT("username")}}密码{{INPUT("password",type="password")}}{{INPUT(value="http://www.kingceram.com/post/提交",type="submit")}}
    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='你好' %}用户名:{{ username }}

    11.with语句:定义局部变量 。
    {% with classroom = '你好' %}班级:{{ classroom }}
    {% endwith %}

    {% with %}{% set classroom = '你好' %}班级:{{ classroom }}
    {% endwith %}

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

    {% block body_block %}{{ super() }}我是子模板中的代码
    {% endblock %}

    如果想要在模版中使用其他模版中的其他代码 。那么可以通过{{ self.其他block名字() }}就可以了 。
    {% block title %}首页{% endblock %}{% block body_block %}{{ self.title() }}我是子模板中的代码
    {% 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,jsonifyclass 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,textHOSTNAME = '127.0.0.1'PORT = '3306'DATABASE = 'lessons'USERNAME = 'root'PASSWORD = 'xxx'# dialect+driver://username:password@host:port/databaseDB_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_baseBase = 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)
    【二Flask学习笔记】1.4.1.3用做数据的增删改查操作
    1.构建对象:所有数据库的ORM操作都必须通过一个叫做的会话对象来实现.
    from sqlalchemy.orm import sessionmakersession = 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也可以作用于一个Queryquery.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学习笔记

    文章插图
    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 Flaskfrom flask_sqlalchemy import SQLAlchemyHOSTNAME='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_URIdb=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=userwith 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_enginefrom sqlalchemy.ext.declarative import declarative_baseHOSTNAME='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,ossys.path.append(os.path.dirname(os.path.dirname(__file__))+"/ALCH/")#orm定义文件alembic_sql.py的文件夹地址fromalembic_sql import Base#这是我的ORM的py文件...target_metadata=http://www.kingceram.com/post/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’tby ‘’
    数据库中存的版本号不在迁移脚本文件中
    删除数据库的表中的数据,重新执行head
    2.flask-下使用
    from flask import Flaskfrom flask_sqlalchemy import SQLAlchemyHOSTNAME='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_URIdb=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,ossys.path.append(os.path.dirname(os.path.dirname(__file__))+"/ALCH/")#orm定义文件alembic_sql.py的文件夹地址import fla_sql_aql #这是我的ORM的py文件...target_metadata=http://www.kingceram.com/post/fla_sql_aql.db.Model.metadata#创建设置模型的元类
    orm生成迁移脚本
    alembic revision --autogenerate -m "my_first_commit"
    迁移脚本映射到数据库
    alembic upgrade head
    1.4.2.-
    Flask-的作用是可以通过命令行的形式来操作Flask 。例如通过命令跑一个开发版本的服务器、设置数据库,定时任务等 。要使用Flask-,可以通过pipflask-安装最新版本 。(这里降低了flask的版本pip3flask==1.1.2)
    @manager.commanddef 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_managermanager.add_command("db",db_manager)
    1.4.2.-
    from flask_script import Managerfrom f_m import appfrom exts import dbfrom flask_migrate import Migrate,MigrateCommandfrom models import Usermanager=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_templatefrom wtforms import Form,StringFieldfrom wtforms.validators import Length,EqualToclass 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)
    {{form.username.label}}{{form.username()}}
    1.5.1.3文件上传
    1.上传文件
    import osfrom werkzeug.utils import secure_filenameUPLOAD_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 "文件上传成功"
    上传文件
    头像:描述:

    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,StringFieldfrom wtforms.validators import InputRequiredfrom flask_wtf.file import FileAllowed,FileRequiredclass UploadForm(Form):avatar=FileField(validators=[FileRequired(),FileAllowed(['jpg','png','gif'])])desc=StringField(validators=[InputRequired()])
    from forms import UploadFormfrom 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,requestbp=Blueprint('cook',__name__,subdomain='cook')@bp.route('/')def index():username=request.cookies.get('username')return username or "没有获取到cookie"
    fromcookie_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,sessionimport osfrom datetime import timedeltaapp=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 theends
    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 CSRFProtectCSRFProtect(app)

    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 Threadrequest='123'class Mythread(Thread):def run(self):global requestrequest='abc'print("子线程:",request)#子线程: abcmythread=Mythread()mythread.start()mythread.join()print("主线程:",request)#主线程: abc
    2.绑定在Local对象上的属性,在每个线程中都是隔离的
    from threading import Threadfrom werkzeug.local import Locallocal=Local()local.request='123'class Mythread(Thread):def run(self):local.request='abc'print("子线程:",local.request)#子线程: abcmythread=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:',extrarequest_started.connect(log_request_started,app)
    flask.:请求结束时,在响应发送给客户端之前发送,可以传递,示例代码如下:
    def log_request_finished(sender,response,*args):print 'response:',responserequest_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 Flaskfrom flask_restful import Api,Resourceapp = Flask(__name__)# 用Api来绑定appapi = 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#valueSTOREDget username#2.get key:获取此key对应的value值VALUE username 0 3zoeENDadd age 0 120 2#3.add key [0,1](是否压缩) timeout(0=forever) value_length(数据长度)18STOREDciERRORincr age 2#4.incr key num:给key的value数值加上几20get ageVALUE age 0 220ENDdecr age 4#5.decr key num:给key的value数值减去几 16get ageVALUE age 0 216ENDdelERRORdelete age#6.delete key:删除键值对DELETEDget ageENDget usernameVALUE username 0 3zoeENDflush_all#7.flush_all:删除memcached中的所有数据 。OKget usernameENDstats#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:forevermc.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# 关闭防火墙
    ufwdeny # 防火墙以禁止的方式打开,默认是关闭那些没有开启的端口
    ufw deny 端口号 # 关闭某个端口
    ufw allow 端口号 #开启某个端口
    1.5. 3.错误
    1.:‘’ for email.
    解决办法:降低的版本
    pip install wtforms==2.2.1
    参考:
    2.Nonamed ‘flask.’
    解决办法:改为
    from flask_script._compat import text_type
    3.name ‘’ from ‘’
    解决办法:
    pip3 install flask==1.1.2
    参考: