Python全栈工程师(慕K)——Flask入门基础记录

1、flask安装

pip安装:pip install flask

源码安装:python steup.py install

验证:

cmd
python
import flask flask.__version__

 

 

 

 

2、启动服务器

_1.设置环境变量

Windows:set FLASK_APP=app.py

Linux:export FLASK_APP=app.py

 

_2.flask run 启动内置web服务器

指定IP及端口:

flask run --host=0.0.0.0 --port=8001

flask run -h 0.0.0.0 -p 8001

 

 

 

 

3、开启调试模式(代码修改后服务器自动重启)

_1.设置环境变量(P.S.1:生产模式:production)

Windows:set FLASK_ENV=development

Linux:set FLASK_ENV=development

 

_2.flask run启动内置web服务器

 

 

 

 

4、Flask扩展

一搜、二看、三用

 

 

 

 

5、路由配置

_1.

方式一:使用装饰器

@app.route(url_name, methods)

e.g.:@app.route('/login, methods=['GET', 'POST'])

 

方式二:使用API配置

app.add_url_rule(url, url_name, view_name)

e.g.:app.add_url_rule('/home', 'home', hello_world)

 

_2.路由匹配规则

1)匹配整个文字:

@app.route('/login‘)

2)传递参数:

@app.route('/login/<username>')

3)指定参数类型(string、int、float、path、uuid):

@app.route('/post/<int:post_id>')

 

_3.查看URL路由规则列表

app.url_map

 

_4.URL逆向解析(根据名称解析成URL字符串)

1)url_for(url_name, **kwargs)

2)静态文件(js/css/图片)引用:url_for('static', filename='style.css')

 

_5.视图获取页面传值

1)URL中的值

@app.route('/user/<page>')
def list_user(page):
  return '您是第{}页用户'.format(page)

2)URL中的值为可选

@app.route('/user/')
@app.route('/user/<page>') def list_user(page=1):   return '您是第{}页用户'.format(page)

 

 

 

 

6、上下文对象

_1.应用上下文对象

1)current_app:当前应用的实例

2)g:处理请求时的临时存储对象,每次请求都会重设这个变量

 

_2.请求上下文对象

1)request:请求对象,封装了客户端发出的HTTP请求中的内容

2)session:用户会话(dict),各请求之间的数据共享

 

 

 

 

7、请求报文

_1.请求报文常用参数

1)method:请求的类型(GET/POST/OPTIONS等)

2)form:POST请求数据dict

3)args:GET请求数据dict

4)values:POST请求和GET请求数据集合dict

5)files:上传文件数据dict

6)cookies:请求中的cookie dict

7)headers:HTTP请求头

@app.route('/test/req')
def test_request():
    """请求报文练习"""
    get_args = request.args
    print(get_args)

    # 页码一定是正整数
    page = get_args.get('page', 1)
    print(page)

    # 服务器所在的主机地址
    get_headers = request.headers
    print(get_headers)
    host = get_headers.get('Host')
    print('主机地址{}'.format(host))
    print(request.host)

    # 获取ip地址
    get_remote_addr = request.remote_addr
    print('远程IP地址{}'.format(get_remote_addr))

    # 获取User-agent
    get_user_agent = request.user_agent
    print(get_user_agent)

    return 'request success'

 

_2.请求钩子

1)before_first_reqiest:服务器初始化后第一个请求到达前执行

2)before_request:每一个请求到达前执行

3)after_request:每次请求处理完成后执行,如果请求过程产生了异常,则不执行

4)teardown_request:每次请求处理完成后执行,如果请求过程产生了异常也执行

@app.before_first_request
def first_request():
    """请求钩子"""
    """服务器启动后第一个请求到达前"""
    print('before_first_request')


@app.before_request
def per_request():
    """每一个请求到达前"""
    print('before_request')

 

 

 

 

8、响应报文

_1.响应字符串

 

_2.响应元组

1)response:响应内容

2)status:响应状态码

3)headers:响应头信息

 

_3.使用make_response代替

@app.route('/test/resp')
def test_response():
    """测试响应"""
    # return 'response success', 201, {
    #     'user': 'Jim',
    # }

    # 构造一个响应对象
    resp = make_response('这是make_response响应对象', 403, {
        'token': 'abc123'
    })
    resp.headers['user_id'] = 'myid_123'

    resp.status_code = 200

    # 响应HTML
    html = '<html><body><h2 style="color:red;">make_response-显示HTML</h2></body></html>'
    resp = make_response(html)

    return resp


@app.route('/test/resp/html')
def test_resp_html():
    # 从文件中响应HTML

    ren_temp_html = render_template('index.html')
    # return ren_temp_html

    resp = make_response(ren_temp_html, 403, {
        'token': '123'
    })
    return resp

 

 

 

 

9、重定向等内部视图

_1.redirect():实现重定向

 

_2.abort():处理错误

@app.route('/')
def hello_world():
    # return 'Hello World!'

    # 访问“/”时从定向到“/index”页面
    # return redirect('/index')
    ip_list = ['127.0.0.2']
    ip = request.remote_addr
    if ip in ip_list:
        abort(403)
    return 'Hello'


@app.errorhandler(403)
def forbidden_page(err):
    print(err)
    return '您没有权限访问,请联系管理员开通权限'

 

 

 

 

10、全局函数

_1.url_for():URL解析函数

 

_2.get_flashed_messages():会话消息

{% for category, message in get_flashed_messages(with_categories=true, category_filter=['error']) %}
<p>{{ category }} -- {{message }}</p>
{% endfor %}


————————————


from flask import flash

@app.route('/')
def test():
    flash('错误提示', 'error')
    flash('欢迎回来', 'success')

 

_3.range([start], stop, [step])

 

_4.dict(**items)

 

_5.cycler(*items):可用于css类名的循环

 

_6.joiner(sep=','):可用于字符串拼接

 

 

 

 

11、扩展

# 为模板引擎添加扩展,支持break/continue语法
app.jinja_env.add_extension('jinja2.ext.loopcontrols')

 

 

 

 

 12、转义显示

_1.视为字符串

{{ '{{}} {%%}' }}

 

_2.使用raw标签

{% raw %}
    {% for key, value in data,items %}
        {{ key }}: {{ value }}
    {% endfor %}
{% endraw %}

 

 

 

 

13、过滤器

_1.用管道符号(|)

 

_2.使用标签

{% filter upper %}
    This text becomes uppercase
{% endfilter %}

 

_3.自定义过滤器

1)使用装饰器注册

@app.template_filter('reverse')
def reverse_filter(s):
    return s[::-1]

2)调用函数注册

def reverse_filter(s):
    return s[::-1]
app.jinja_env.filters['reverse'] = reverse_filter

 

 

 

 

14、模板中的宏

_1.html中使用宏

{% macro input(name, type='text', value='') %}
<div>
    <input class="input-control" type="{{ type }}" name="{{ name }}" value="{{ value }}" />
</div>
{% endmacro %}
{{ input('username') }}
{{ input('password', type='password') }}

 

_2.文件中宏的使用

1)将定义好格式的宏保存为forms.html

2)导入:

{% import 'forms.html' as forms %}
{% from 'forms.html' import input %}

3)使用:

<p>{{ forms.input('username') }}</p>

 

 

 

 

15、flask-sqlalchemy安装及配置

_1.安装

pip安装:pip install -U Flask-SQLAlchemy

源码安装:python setup.py install

安装依赖:pip install mysqlclient

 

_2.配置

1)数据库URI:SQLALCHEMY_DATABASE_URI

# 配置数据库的连接参数
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:root@localhost/test_flask'

2)多个数据库支持

SQLALCHEMY_BINDS = {
    'users':        'mysqldb://localhost/users',
    'appmeta':      'sqlite:path/to/appmeta.db'
}

 

 

 

 

16、数据库模型设计

_1.绑定到Flask对象

db = SQLAlchemy(app)

 

_2.ORM模型创建

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)

 

_3.指定表的名称

__tablename__='weibo_user'

 

_4.创建和删除表

1)创建表

db.create_all(bind='db1')

2)删除表

db.drop_all()

 

from flask import Flask
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:root@localhost/test_flask'
db = SQLAlchemy(app)


class User(db.Model):
    # 用户信息
    __tablename__ = 'weibo_user'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), nullable=False)
    password = db.Column(db.String(256), nullable=False)
    birth_day = db.Column(db.Date, nullable=True)
    age = db.Column(db.Integer, nullable=True)


class UserAddress(db.Model):
    # 用户的地址
    __tablename__ = 'weibo_user_addr'
    id = db.Column(db.Integer, primary_key=True)
    addr = db.Column(db.String(256), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('weibo_user.id'), nullable=False)
    user = db.relationship('User', backref=db.backref('address', lazy=True))


from app import db
db.drop_all()
db.create_all()

 

 

 

 

17、ORM插入,修改,删除,查询数据

_1.插入

1)构造ORM模型对象

user = User('admin', 'password')

2)添加到db.session(P.S.2:可添加多个对象)

db.session.add(user)

3)添加到数据库

 db.session.commit()

 

_2.修改

user.username = 'root'

db.session.add(user)

db.session.commit()

 

_3.删除

user = User.query.filter_by(username='Jim').first()

db.session.delete(user)

db.session.commit()

 

_4.查询,返回结果集

1)查询所有数据

User.query.all()

2)按条件查询

User.query.filter_by(username='zhangsan')

User.query.filter(User.nickname.endswith('三')).all()

3)排序

User.query.order_by(User.username)

4)查询TOP10

User.query.limit(10).all()

5)根据pk查询

User.query.get(1)

6)获取第一条记录

User.query.first()

7)视图快捷函数:有则返回,无则返回404

first_or_404()

get_or_404()

8)多表关联查询

db.session.query(User).join(Address)

User.query.join(Address)

9)分页

-1>方式一:使用offset和limit

.offset(offset).limit(limit)

-2>方式二:paginate分页支持,返回Pagination的对象

.paginate(page=2, per_page=4)

has_prev/has_next——是否有上/下一页

items——当前页的数据列表

prev_num/next_num——上/下一页的页码

total——总记录数

pages——总页数

@app.route('/user/<int:page>/')
def list_user(page):
    """ 用户分页 """
    per_page = 10    # 每一页的数据大小
    # 1.查询用户信息
    sql = User.query
    # 2.准备分页的数据
    user_page_data = sql.paginate(page, per_page=per_page)
    return render_template('list_user.html', user_page_data =user_page_data)


————————————————————————


html
总共有{{ user_page_data.total }}用户
当前在第{{ user_page_data.page }}页
总共有{{ user_page_data.pages }}页

{% for user in user_page_data.items %}
{{ user.username }}
{% endfor %}

{% if user_page_data.has_prev %}
{{ url_for('list_user', page=user_page_data.prev_num) }}
{% endif %}
{% if user_page_data.has_next %}
{{ url_for('list_user', page=user_page_data.next_num) }}
{% endif %}

 

 

 

 

18、wtf表单

_1.安装:

pip安装:pip install flask-wtf

源码安装:python setup.py install

 

_2.配置(CSRF保护)

WTF_CSRF_SECRET_KEY = 'a random string'

 

_3.表单模型

1)label:label标签

2)default:表单默认值

3)validators:表单验证规则

4)widget:定制界面显示方式

5)description:帮助文字

表单输入区域:{{ form.username }}
表单label:{{ form.username.label.text }}
from forms import LoginForm


app.config['WTF_CSRF_SECRET_KEY'] = 'ABC123'
app.config['SECRET_KEY'] = 'ABC123'


@app.route('/form', methods=['GET', 'POST'])
def page_form():
    form = LoginForm()
    return render_template('page_form.html', form=form)


——————————————————————————————


from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField


class LoginForm(FlaskForm):
    username = StringField(label='用户名:')
    password = PasswordField(label='密码:')
    submit_btn = SubmitField(label='登录')


——————————————————————————————


<form action="" method="post">
    {{ form.username.label.text }}
    {{ form.username }}
    {{ form.password.label.text }}
    {{ form.password }}
    {{ form.submit_btn }}
</form>

 

_4.通过表单保存数据

1)检测表单是否已经通过验证

form.validate_on_submit()

2)获取表单中传递过来的值

form.field_name.data

3)业务逻辑代码编写(可结合ORM)

4)CSRF表单保护

-1>默认开启CSRF保护

// 同步请求CSRF保护
{{ form.csrf_token }}
or <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
// 异步请求CSRF保护
<!-- 第一步:模板中添加csrf_token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<script type="text/javascript">
    let csrftoken = $("meta[name=csrf-token]").attr("content")

or

<script type="text/javascript">
    let csrftoken = "{{ csrf_token() }}"

<!-- 第二步:添加X-CSRFToken头 -->
    $.ajaxSetup({
        beforeSend: function (xhr, settings) {
            if(!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken)
            }
        }
    })
</script>

-2>关闭单个表单CSRF保护

form = RegisterForm(csrf_enabled=False)

@app.route('/user/register', methods=['GET', 'POST'])
def page_register():
    form = RegisterForm(csrf_enabled=False)
    

——————————————————————————


class RegisterForm(FlaskForm):
    def __init__(self, csrf_enabled, *args, **kwargs):
        super().__init__(csrf_enabled=csrf_enabled, *args, **kwargs)

-3>全局关闭

WTF_CSRF_ENABLED = False

class RegisterForm(FlaskForm):
    username = StringField(label='用户名:')
    password = PasswordField(label='密码:')
    re_password = PasswordField(label='确认密码:')
    birth_day = DateField(label='生日:')
    age = IntegerField(label='年龄:')
    submit_btn = SubmitField(label='注册')


——————————————————————————


@app.route('/user/register', methods=['GET', 'POST'])
def register_form():
    form = RegisterForm()
    if form.validate_on_submit():
        username = form.username.data
        password = form.password.data
        birth_day = form.birth_day.data
        age = form.age.data

        user = User(username=username, password=password, birth_day=birth_day, age=age)
        db.session.add(user)
        db.session.commit()

        return redirect(url_for('index'))
    else:
        print(form.errors)
    return render_template('page_register.html', form=form)


——————————————————————————


<form action="{{ url_for('register_form') }}" method="post">
    {{ form.csrf_token }}
    {{ form.username.label }}
    {{ form.username }}
    {{ form.password.label }}
    {{ form.password }}
    {{ form.re_password.label }}
    {{ form.re_password }}
    {{ form.birth_day.label }}
    {{ form.birth_day }}
    {{ form.age.label }}
    {{ form.age }}
    {{ form.submit_btn }}
</form>

 

_5.表单验证

1)导入内置的表单验证器(或自定义)

from wtforms.validators import DataRequired

-1>内置表单验证器

DataRequired/InputRequired、Email/URL/UUID、Length(min=-1, max=-1, message=None)、EqualTo(fieldname, message=None)

2)配置到表单字段

username = StrinField('用户名', validators=[DataRequired('请输入用户名'), my_validator])

3)自定义表单验证

-1>只在当前表单使用

class RegisterForm(FlaskForm):
    username = StringField(label='用户名'def validate_username(self, field):
        username = field.data
        pattern = r'^1[0-9]{10}$'
        if not re.search(pattern, username):
            raise ValidationError('请输入手机号码')
        return field


——————————————————————————————————————————————


{% if form.username.errors %}
  {% for err in form.username.errors %}
    {{ err }}
  {% endfor %}
{% endif %}

-2>在多个表单中使用

def phone_required(form, field):
    username = field.data
    pattern = r'1[0-9]{10}$'
    if not re.search(pattern, username):
        raise ValidationError('请输入手机号码')
    return field


————————————————————————


class LoginForm(FlaskForm):
    username = StringField(label='用户名', validators=[phone_required])

e.g.:

import re

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, DateField, IntegerField, validators

from wtforms.validators import ValidationError


def phone_required(form, field):
    username = field.data
    pattern = r'^1[0-9]{10}$'
    if not re.search(pattern, username):
        raise ValidationError('请输入正确的手机号')
    return field


class LoginForm(FlaskForm):
    username = StringField(label='用户名:', validators=[validators.DataRequired('手机号不能为空'), phone_required])
    password = PasswordField(label='密码:')
    submit_btn = SubmitField(label='登录')


class RegisterForm(FlaskForm):
    username = StringField(label='用户名:', validators=[validators.DataRequired()])
    password = PasswordField(label='密码:', validators=[validators.DataRequired(), validators.EqualTo('re_password', message='两次密码不一致')])
    re_password = PasswordField(label='确认密码:')
    birth_day = DateField(label='生日:')
    age = IntegerField(label='年龄:')
    submit_btn = SubmitField(label='注册')

    def validate_username(self, field):
        username = field.data
        pattern = r'^1[0-9]{10}$'
        if not re.search(pattern, username):
            raise ValidationError('请输入正确的手机号')
        return field


———————————————————————


from flask import Flask, render_template, redirect, url_for
from flask_sqlalchemy import SQLAlchemy

from forms import LoginForm, RegisterForm

app = Flask(__name__)
# 为模板引擎添加扩展,支持break/continue语法
app.jinja_env.add_extension('jinja2.ext.loopcontrols')

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:root@localhost/test_flask'

app.config['WTF_CSRF_SECRET_KEY'] = '22'
app.config['SECRET_KEY'] = '11'


db = SQLAlchemy(app)


@app.route('/form', methods=['GET', 'POST'])
def login_form():
    form = LoginForm()
    if form.validate_on_submit():
        return redirect(url_for('index'))
    else:
        print(form.errors)
    return render_template('page_form.html', form=form)


@app.route('/user/register', methods=['GET', 'POST'])
def register_form():
    form = RegisterForm()
    if form.validate_on_submit():
        username = form.username.data
        password = form.password.data
        birth_day = form.birth_day.data
        age = form.age.data

        user = User(username=username, password=password, birth_day=birth_day, age=age)
        db.session.add(user)
        db.session.commit()
        return redirect(url_for('index'))
    else:
        print(form.errors)
    return render_template('page_register.html', form=form)


————————————————————————————————


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户注册</title>
</head>
<body>
<form action="" method="post">
    {{ form.csrf_token }}
    {{ form.username.label }}
    {{ form.username }}
    {% if form.username.errors %}
        {% for err in form.username.errors %}
            {{ err }}
        {% endfor %}
    {% endif %}
    {{ form.password.label }}
    {{ form.password }}
    {{ form.re_password.label }}
    {{ form.re_password }}
    {% if form.password.errors %}
        {% for err in form.password.errors %}
            {{ err }}
        {% endfor %}
    {% endif %}
    {{ form.birth_day.label }}
    {{ form.birth_day }}
    {{ form.age.label }}
    {{ form.age }}
    {{ form.submit_btn }}
</form>

</body>
</html>


——————————————————————————————————


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
<form action="" method="post">
    {{ form.csrf_token }}
    {{ form.username.label }}
    {{ form.username }}
    {% if form.username.errors %}
        {% for err in form.username.errors %}
            {{ err }}
        {% endfor %}
    {% endif %}
    {{ form.password.label }}
    {{ form.password }}
    {{ form.submit_btn }}
</form>
</body>
</html>

 

_6.图片上传

1)不使用wtf

-1>设置<form>的enctype

enctype='multipart/form-data'

-2>在视图函数中获取文件对象

request.files

-3>保存文件

f.save(file_path)

2)文件名称格式化

werkzeug.utils.secure_filename

@app.route('/img/upload', methods=['GET', 'POST'])
def img_upload():
    """ 不使用wtf实现文件上传 """
    file_path = os.path.join(os.path.dirname(__file__), 'medias')
    if request.method == 'POST':
        # 获取文件列表
        files = request.files
        file1 = files.get('file1', None)
        if file1:
            # 保存文件
            f_name = secure_filename(file1.filename)     # 文件名
            print('filename:', f_name)
            file_name = os.path.join(file_path, f_name)     # 路径
            file1.save(file_name)
            print('保存成功')
        return redirect(url_for('img_upload'))
    return render_template('img_upload.html')


————————————————————————————


<form action="{{ url_for('img_upload') }}" method="post" enctype="multipart/form-data">
    <input type="file" name="file1">
    <input type="file" name="file2">
    <input type="submit" value="上传">
</form>

3)使用FileField并添加类型验证

-1>FileRequired文件上传必须验证

-2>FileAllowed文件类型验证

from werkzeug.utils import secure_filename


app.config['WTF_CSRF_SECRET_KEY'] = '1111'
app.config['SECRET_KEY'] = '1111'
app.config['UPLOAD_PATH'] = os.path.join(os.path.dirname(__file__), 'medias')


@app.route('/atatar/upload', methods=['GET', 'POST'])
def avatar_upload():
    """ 头像上传 """
    form = UserAvatarForm()
    if form.validate_on_submit():
        # 获取图片对象
        img = form.avatar.data
        f_name = secure_filename(img.filename)
        file_name = os.path.join(app.config['UPLOAD_PATH'], f_name)
        img.save(file_name)
        print('保存成功')
        return redirect(url_for('index'))
    else:
        print(form.errors)
    return render_template('avatar_upload.html', form=form)


————————————————————————————


class UserAvatarForm(FlaskForm):
    """ 用户头像上传 """
    avatar = FileField(label='上传头像', validators=[FileRequired('前选择头像文件'), FileAllowed(['png'], '仅支持PNG图片上传')])


————————————————————————————


<form action="{{ url_for('avatar_upload') }}" method="post" enctype="multipart/form-data">
    {{ form.csrf_token }}
    {{ form.avatar.label }}
    {{ form.avatar }}
    <input type="submit" value="提交">
</form>

4)扩展Flask-Uploads

 

posted @ 2021-05-01 12:46  嘆世殘者——華帥  阅读(253)  评论(0编辑  收藏  举报