一、前奏

1.修改debug、host、port

debug模式:1.修改代码保存后自动运行,不需重启;

2.出现bug时,在网页上显示bug内容。

host参数:默认为127.0.0.1(本机)。修改为0.0.0.0,网页会被局域网的其他设备访问到。

port参数:端口号,默认5000。当有多个网页项目时可更改端口号。

2.建立路由(url)与视图函数之间的映射

@app.route('/path')
def path():
    return "Helloworld"

二、渲染模版

1.render_template('html文件名')

使用render_template('html文件名')将html渲染出来,用return返回渲染好的html模版。

from flask import Flask,render_template
@app.route('/path')
def path():
    return render_template('index.html')

注:html中的常用标签:

①<h1></h1> 一级标题

②<p></p> 正文文字

③<div></div> 换行输出

④<ul>

<a href="path"></a>

</ul> 标签,href是标签地址

⑤<img src="path" alt=""> 图片,src是图片地址。没有</img>哦

⑥<link rel="stylesheet" href="path" > 加载css。没有</link>

⑦<script src=" "> </script> 加载js。有</script>

2.传入参数

方法1:request

from flask import Flask,render_template,request
@app.route('/path')
def path():
    page=request.args.get(key="page",default=1,type=int)
    return render_template('index.html',page=page)

访问网址:'127.0.0.1:/5000/path?page=2'

注:1.调用request.args.get()选择是否省略type。区别在于,网址为'127.0.0.1:/5000/path?page=a'省略后依然可访问,而不省略则无法访问网址,减少出bug的可能。

2.若访问'127.0.0.1:/5000/path',即不加任何参数,最后得到的是page=1的内容。

方法2:从path路径中访问

@app.route('/path/<int:page>')
def path(page):
    return render_template('index.html',page=page)

访问网址:'127.0.0.1:5000/path/2'

注:可直接<page>而不定义page的类型,效果参考方法1。

将参数传入html后,可在html调用。

<body>
    <h1>Class photo</h1>
    <div>您正在查看第{{ page }}页。</div>
</body>

3.传入对象和字典

传入字典列表

@app.route('/path')
def path():
    books=[{
            "bookname":"白夜行",
            "author":"东野圭吾"
    },{
            "bookname":"愿你的青春不服梦想",
            "author":"俞敏洪"
    }]
    return render_template('index.html',books=books)

在html中的访问

<body>
    <h1>Books</h1>
    {% for book in books %}
        <div>{{book.bookname}}-{{book.author}} </div>
    {% endfor %}
    <div>{{books[0]['bookname']}}-{{books[0]['author']</div>
</body>

注:在html中,访问字典更常用dir.key的方式。

传入对象

class User:
    def __init__(self,name,password):
        self.name=name
        self.password=password
@app.route('/path')
def path():
    user=User('法外狂徒张三','111111')
    return render_template('index.html',user=user)

在html中访问

<body>
    <div>{{user.name}}-{{user.password}}</div>
</body>

4.html模版和继承

html父模版

...
<head>
    <title>{% block title%}-{% endblock %}</title>
</head>
<body>
    <ul>
        <a href="/">首页</a>
        <a href="/blog">博客</a>
    </ul>
    {% block body %}{% endblock %}
</body>
..

html子文件

{% extend 'base.html' %}
{% block title%}博客{% endblock %}
{% block body%} 
    <div> 我是子文件的文字 </div>
{% endblock %}

注:{% extend 'base.html' %}中,父文件需要加引号。

5.加载static文件

加载图片

<img scr="{{ url_for('static',filename='path') }}" alt="">

注:1.path指图片路径(默认从static开始);

2.url_for()作用:将文件目录转换为路由地址(不要丢了双大括号);

3.注意<img>没有</img>,也没有中间的文字内容。

加载css文件

<link rel="stylesheet" href="{{url_for('static','path')}}" >

注:<link>没有</link>

css改变浏览器背景颜色

body{
    background-color:pink;
}

加载javascript文件

<script scr="{{ url_for('static','path') }}"></script>

注:<script>三个静态文件中唯一需要</script>的,但用处不大(似乎)。

js文件发送信息框

alert("我是js执行的!");

注:css文件和js文件的句末都需要带等号。

6.html的判断和循环语句

判断语句

<body>
    {% if age>18%}
        <div>您已满18岁,可以进入网吧。</div>
    {% elif age < 18%}
        <div>您未满18岁,不能进入网吧</div>
    {% else %}
        <div>您刚满18岁,需在成年人陪同下进入网吧</div>
    {% endif %}
</body>

循环语句

<body>
    <h1>Books</h1>
    {% for book in books %}
        <div>{{book.bookname}}-{{book.author}} </div>
    {% endfor %}
</body>

注:1.html中的判断和循环都必须有end语句;

2.html中的for循环语句不能break;

7.过滤器

.自带filter

<body>
    <div>{{user_id}}-{{ user_id | length }}</div>
</body>

注:user_id | length可获取user_id的长度。

.自定义filter

from datetime import datetime
def datetime_format(value,format="%Y-%m-%d %H:%M"):
    return value.strftime(format)
app.add_template_filter(datetime_format,"dformat")

@app.route('/time')
def get_time():
    mytime=datetime.now()
    return render_template('time.html',mytime=mytime)

注:1.app.add_template_filter(函数名,过滤器名)。将函数生成了过滤器。其中第一个参数value为'|'前的参数。

2.若想调用多个参数,则需要调用过滤器时,在过滤器名后加()添加第2到n个参数。

<body>
    <div>now time:{{mytime | dformat}}</div>
</body>

注:采用mytime | dformat("%Y-%m-%d"),可直接输出年月日。

三、数据库

1.数据库连接

.先安装Flask-SQLAlchemy这个包。

pymysql可操作数据库,SQLAlechemy建立flask和数据库的连接。其中pymysql被封装在了SQLAlchemy中,使用时只导入SQLAlchemy即可。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
...
hostname='127.0.0.1'
port=3306
username='root'
password='password'
database='database_learn'

app.config['SQLALCHEMY_DATABASE_URI']=f"mysql+pymysql://{username}:{password}@{hostname}:{port}/{database}?charset=utf8bm4"
db=SQLAlchemy(app)

注:1.app.config['SQLALCHEMYL_DATABASE_URI']=...容易写错,多写;

2.db为database的简称,SQLAlchemy建立了一个alchemy对象。

3.定义的变量中只有一个是数字而不是字符型的,即port=3306。数据库默认端口3306

.检测是否连接成功

with app.app_context():
    with dp.enging.connect() as conn:
        rs=conn.execute('select 1')
        print(rs.fetchone())

注:1.如打印(1,),则说明连接成功;

2.需在Navicat Premium中先添加数据库在连接。

2.创建orm模型

.orm模型实际上是继承自db.Model的类,建立python类和数据库的映射。

class User(db.Model):
    __tablename__='user'
    id=db.Column(db.Integer,primary_key=1,autoincrement=True)
    username=db.Column(db.String(100),nullable=False)
    password=db.COlumn(db.String(100),nullable=False)

注:1.建立类与数据库的映射,实际上是建立元素与数据库的映射。db.Column(db.type,...)实现了这一点。

2.注意不要丢__tablename__='',其值为数据库table的标题。

3.每一个orm对象都需要一个id变量。

.为了使数据库与orm模型实现同步,可使用以下代码:

with app.app_context():
    db.create_all()

注:使用上述代码后,可在数据库中看到新建的数据类型和信息。但db.create_all()无法做到在元素增加时同步更新。

3.数据的增、查、改、删除

.添加数据

@app.route('/user/add')
def user_add():
    user=User(username='法外狂徒张三',password='111111')
    dp.session.add(user)
    dp.commit()
    return "添加数据成功!"

注:1.若添加多个元素,可使用db.session.add_all([user1,user2])。注意参数为列表。

2.dp.session.commit()的作用:将db的修改同步到数据库。千万不要丢。

.查找数据

@app.route('/user/query')
def user_query():
    user1=User.query.get(1)
    user2=User.query.filter_by(username='法外狂徒张三').first()
    print(f'username:{user1.username}--password:{user1.password}')
    print(f'username:{user2.username}--password:{user2.password}')
    return "查找数据成功!"

注:1.User.query来自父类db.Model,User.query查找的返回值是user对象;

2.User.query.get()根据id查找,数据唯一;

3.User.query.filter_by()根据任意值查找,返回列表,列表,列表!

取元素的方式有两种:①User.query.filter_by(username='法外狂徒张三')[0]

②User.query.filter_by(username='法外狂徒张三').first()

其中first()在查找结果为空的情况下不会报错,使用起来更稳定,一般采用这种方式。

.修改数据

@app.route('/user/change')
def user_change()
    user=User.query.get(1)
    user.password='222222'
    db.session.commit()
    return "修改数据成功!"

注:没什么好注的,记住db.session.commit()同步数据。

.删除数据

@app.route('/user/delete')
def user_delete()
    user=User.query.get(1)
    db.session.delete(user)
    db.session.commit()
    return "数据删除成功!"

注:删除db.session.delete(orm对象)。

4.表与表之间的连接

.建立外键

class User(db.Model):
    __tablename__='user'
    id=db.Column(db.Integer,primary_key=1,autoincrement=True)
    username=db.Column(db.String(100),nullable=False)
    password=db.COlumn(db.String(100),nullable=False)
    #authors=db.relationship('Article',back_populates='Users')

class Article(db.Model):
    __tablename__='article'
    id=db.Column(db.Integer,primary_key=1,autoincrement=True)
    article_name=db.Column(db.String(100),nullable=False)
    article_text=db.Column(db.Text,nullable=False)
    author_id=db.Column(db.Integer,db.ForeignKey('user.id'))
    #author=db.relationship("User",back_poplulates='articles')
    author=db.relationship('User',backref='articles')

注:对author_id=db.Column(db.Integer,db.ForeignKey('user.id'))的理解:

  1. 将Article中的author_id元素映射成db中的Integer类型,且以user.id建立外键。此后可根据User.query.get(author_id)访问作者信息;
  2. 作用:将Article和User两个表连接起来。

.建立relationship

使用db.relationship=('orm对象',backref/back_polulates='orm对象的访问元素'),建立两对象的连接。

方法1:back_populates=...

代码如注释部分。

方法2:backref=...

代码如上。

注:1.a_id=...同a=db.relationship()中的a,即建立的外键和relationship必须统一。本质上relationship是通过外键建立的;

2.作用:可通过user访问所有article。

.通过user访问article

@app.route('/article/query')
def query_article():
    user=User.query.get(1)
    for article in user.articles:
        print(f'{article.title}-{article.text}')
    return "文章查找成功!"

注:通过user访问article时,能通过外键author_id访问到所有作者为user的文章,返回类型为列表。