flask

一: flask简介

  Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助 jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。

二: flask安装

pip3 install flask

三: flask基本使用

from flask import Flask

app = Flask(__name__)


@app.route('/')
def HelloWorld():
    return "hello world"


if __name__ == '__main__':
    app.run()

四: flask的四种响应

from flask import Flask, render_template, redirect, jsonify
# __name__表示所在文件
app = Flask(__name__)


@app.route('/index')
def index():
    # 直接返回字符串
    return "ok"

    # 返回HTML
    info = {'name': "Tom", "age": 19, "gender": "male"}
    dict = {'name': "Tom"}
    return render_template('index.html', name="hello world", info=info, name_dict=dict)

    # 跳转页面
    return redirect('/inner')

    # 返回json数据
    name_dict = [{'name': "jason-gdx"}, {'name': "tank-sb"}]
    return jsonify(name_dict)

@app.route('/inner')
def inner():
    return 'hello world'

if __name__ == '__main__':
    app.run()

五: flak的配置文件

from flask import Flask

app = Flask(__name__)

# 配置文件
# app.debug = True
# app.secret_key = '123123'

# 以字典的形式
# app.config['DEBUG'] = True

# 以文件的方式
# app.config.from_pyfile('settings.py')

# 以类的形式(推荐使用, 比如: 上线和线下所用的配置文件不一样)
app.config.from_object('settings.DevelopmentConfig')


@app.route('/login')
def login():
    print("hello world")
    return 'ok'


if __name__ == '__main__':
    app.run()

  各种配置

 {
        'DEBUG':                                get_debug_flag(default=False),  是否开启Debug模式
        'TESTING':                              False,                          是否开启测试模式
        'PROPAGATE_EXCEPTIONS':                 None,                          
        'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
        'SECRET_KEY':                           None,
        'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
        'USE_X_SENDFILE':                       False,
        'LOGGER_NAME':                          None,
        'LOGGER_HANDLER_POLICY':               'always',
        'SERVER_NAME':                          None,
        'APPLICATION_ROOT':                     None,
        'SESSION_COOKIE_NAME':                  'session',
        'SESSION_COOKIE_DOMAIN':                None,
        'SESSION_COOKIE_PATH':                  None,
        'SESSION_COOKIE_HTTPONLY':              True,
        'SESSION_COOKIE_SECURE':                False,
        'SESSION_REFRESH_EACH_REQUEST':         True,
        'MAX_CONTENT_LENGTH':                   None,
        'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
        'TRAP_BAD_REQUEST_ERRORS':              False,
        'TRAP_HTTP_EXCEPTIONS':                 False,
        'EXPLAIN_TEMPLATE_LOADING':             False,
        'PREFERRED_URL_SCHEME':                 'http',
        'JSON_AS_ASCII':                        True,
        'JSON_SORT_KEYS':                       True,
        'JSONIFY_PRETTYPRINT_REGULAR':          True,
        'JSONIFY_MIMETYPE':                     'application/json',
        'TEMPLATES_AUTO_RELOAD':                None,
    }

六: 路由

  1. 创建路由的两种方式

from flask import Flask

app = Flask(__name__)

@app.route('/index')
def index():
    pass
# view_func必填
app.add_url_rule('/index', view_func=index)

if __name__ == '__main__':
    app.run()

  2. 路由参数

from flask import Flask

app = Flask(__name__)

"""
rule: 路由
methods: 请求方式
endpoint: 起别名, 用于反向解析. url_for('index')
strict_slashes: 设置路由是否为严格模式, True为严格模式, False为非严格模式, 默认为True
redirect_to: 重定向到指定路径
"""
@app.route('/index', methods=["GET", "POST"], endpoint='index', strict_slashes=True, redirect_to='/login')
def index():
    return "index"

# app.add_url_rule('/index', view_func=index)
"""
self.add_url_rule(rule, endpoint, f, **potion)参数如下

self: Flask类产生的对象(app)
rule: 路由
endpoint: 取别名, 如果为空, 用当前的函数名
methods: 请求方式
view_func: 取别名指向的函数(请求该路径, 要响应的函数)
"""


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


if __name__ == '__main__':
    app.run()

 

  3. 路由跳转

from flask import Flask, url_for, redirect

app = Flask(__name__)
app.debug = True

def login():
    return "hello world"

app.add_url_rule('/login', view_func=login, endpoint='login')

def index():
    real_url = url_for('login')
    return redirect(real_url)

app.add_url_rule('/index', view_func=index)

if __name__ == '__main__':
    app.run()

 

  4.自定义URL匹配正则表达式

"""
自定义URL匹配正则表达式
    1. 导入from werkzeug.routing import BaseConverter
    2. 先写一个类, 继承BaseConverter
        实现: __init__(), to_python(), to_url()方法
    3. app.url_map.converters['regex'] = RegexConverter
    4. 我们在路由里面@app.route('/index/'),regex1("正则表达式")
    5. regex("正则表达式")匹配出来的结果,传给to_python,一定要return
    6. 当我们做反向解析的解析的时候,我们的参数,会传递给to_url,return的结果才是我们拼接到我们路由上(真正的路由)
"""
from flask import Flask, url_for
from werkzeug.routing import BaseConverter

app = Flask(import_name=__name__)
app.debug = True

class RegexConverter(BaseConverter):

    def __init__(self, map, regex):
        super(RegexConverter, self).__init__(map)
        self.regex = regex

    def to_python(self, value):
        """
        路由匹配时, 匹配成功后传递给视图函数中参数的值
        value: 正则匹配出来的结果
        """
        print(value, type(value),2222222)
        return int(value)

    def to_url(self, value):
        """
        使用url_for反向解析生成URl时, 传递的参数经过该方法处理,
        返回的值用于生成URL中的参数
        """
        val = super(RegexConverter, self).to_url(value)
        print(val,3333333)
        return val

# '/index/' ":"后面不能有空格
app.url_map.converters['regex'] = RegexConverter
@app.route('/index/', endpoint="index")
def index(nid):
    print(nid, type(nid),4444444)
    print(url_for('index', nid='999'),55555555)
    # /index/999 55555555
    return "index"

if __name__ == '__main__':
    app.run()

  5. 路由(起别名)

from flask import Flask, url_for, redirect, render_template

app = Flask(__name__)
app.debug = True

USERS = {
    1:{'name':'张三','age':18,'gender':'','text':"道路千万条"},
    2:{'name':'李四','age':28,'gender':'','text':"安全第一条"},
    3:{'name':'王五','age':18,'gender':'','text':"行车不规范"},
}

# @app.route('/detail/',methods=['GET'],endpoint='detail')
def detail(nid):
    print(nid, type(nid))
    return render_template('index.html', info=USERS)

app.add_url_rule('/detail/', view_func=detail)


if __name__ == '__main__':
    app.run()

 

  装饰器

 # 装饰器函数
 def route(self, rule, **options):

        def decorator(f):
            endpoint = options.pop("endpoint", None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f

        return decorator

# 被装饰函数
@app.route('/login',methods=['POST', "GET"], endpoint='index')
def login():
    return "hello world"

装饰器: 先执行装饰器, 运行里面的代码, 返回一个函数名
        然后加括号, 把装饰器装饰的函数当成参数传递给函数,
        返回一个被装饰器装饰的同名函数

七: flask的CBV

from flask import Flask, url_for, views

app =Flask(__name__)
app.debug = True

# class IndexView(views.View):
#     methods = ['GET', "POST"]
#
#     def dispatch_request(self):
#         print('index')
#         return "index"

class IndexView(views.MethodView):

    def get(self):
        return 'index get'

    def post(self):
        return 'index post'

app.add_url_rule('/index', view_func=IndexView.as_view(name='index'))
"""
执行as_view()函数里面的view函数,
在执行MethodView类中的dispatch_request()函数,
根据请求的方式,进行任务的分发,默认get的请求.

为什么as_view()中药传参: 
    如果不传参, 都是view_func=view, 路由多要报错
    
相当于endpoint
"""

if __name__ == '__main__':
    app.run()

八: .py文件中的标签渲染页面的两种方式

  .py文件

"""
一种: .py文件渲染标签, 导入Markup
二种: 前端页面{{ html|safe }}
"""
from flask import Flask, Markup, render_template

app = Flask(__name__)
app.debug = True

def func(st1, st2):
    return Markup(f"

{st1} {st2}!

") @app.route('/index') def index(): html = "

you are beautiful!

" return render_template('index.html', func=func, html=html) if __name__ == '__main__': app.run()

  html页面

{{ func('hello', 'world') }}
{{ html|safe}}

九: 请求与响应

from flask import Flask
from flask import request
from flask import make_response
from flask import render_template
from flask import redirect

  1. 请求

    request.args  # get请求提交的数据
    request.form  # post请求提交的数据
    request.values  # post和get提交的数据都在
    request.cookies  # 客户端携带cookie
    request.headers  # 请求头
    request.path  # 不带域名, 请求路径如: /index
    request.full_path  # 不带域名, 带参数的请求路径
    request.script_root
    request.url           带域名带参数的请求路径
    request.base_url      带域名请求路径
    request.url_root      域名
    request.host_url      域名
    request.host          127.0.0.1:500
    request.files
    obj = request.files['the_file_name']
    obj.save('/var/www/uploads/' + secure_filename(f.filename))

  2. 响应

    return "字符串"
    return render_template('html模板路径',**{})
    return redirect('/index.html')
    return jsonify({'k1':'v1'})

    response = make_response(render_template('index.html'))
    response.delete_cookie('key')
    response.set_cookie('key', 'value')
    response.headers['X-Something'] = 'A value'
   return response
    """
    1. 导入make_response
    2. response=make_response(4剑客)
    3. 操作response
    4. return response
    """

十: session和cookie

"""
session和cookie
app.session_interface这里面看 存session: 1. 调用save_session,将我们的session加密的val,读取配置文件['SESSION_COOKIE_NAME']得到key 2. 将1种的key,val存储到cookies 取session; 1. 获取request里面的cookies,获取里面key,这个key就是['SESSION_COOKIE_NAME'],值就是加密的值 2. 对该值进行解密
""" from flask import Flask, session app = Flask(__name__) app.debug = True # 在浏览器存储的key名字 app.config['SESSION_COOKIE_NAME'] = 'session' # 秘钥 app.secret_key = 'ahsodhfb' # @app.route('/index') def index(): session['hello'] = 'world' return 'ok' # @app.route('/login') def login(): print(session.get('hello')) return 'I am fine' if __name__ == '__main__': app.run()

  flask_session

"""
flask_session
安装: pip3 install flask_session
"""
from flask import Flask, session
from flask_session import RedisSessionInterface, Session
import redis
app = Flask(__name__)

# app.secret_key = "qwerdf"
# conn = redis.Redis(host='127.0.0.1', port=6379)
# # use_signer是否对key签名   permanent浏览器关闭session是否失效
# app.session_interface = RedisSessionInterface(conn, key_prefix='hello', use_signer=True, permanent=False)

app.config['SESSION_TYPE'] = 'Redis'
app.config['SESSION_REDIS'] = redis.Redis(host='127.0.0.1', port=6379)
app.config['SESSION_KEY_PREFIX'] = 'hello'
Session(app)

@app.route('/index')
def index():
    session['world'] = 'hello'
    return "hello world!"

@app.route('/login')
def login():
    print(session.get('world'))
    return 'OK'

if __name__ == '__main__':
    app.run()

 

十一: 闪现

"""
闪现: 可用于记录日志

  1. 如果要用flash就必须设置app.secret_key = 'asdfasdf'
  2. 只能取一次,在取就没有了
  3. 我们可以通过 flash('普通信息',category="info"),对信息做分类
  4. get_flashed_messages(with_categories=True,category_filter=("error",)),with_categories以键值对的形式获取
    我们设置闪现,category_filter=("error",)进行分类信息的过滤

"""
from flask import Flask, flash, get_flashed_messages, jsonify

app = Flask(__name__)
app.debug = True
app.secret_key = 'xhjjjg'

@app.route('/index')
def index():
  # flash(message, category) flash(
"超时错误", category="error") flash('普通信息',category="info") return "ssdsdsdfsd" @app.route('/inner') def inner(): # with_categories=False 表示只显示信息, 不显示分类 data = get_flashed_messages(with_categories=False, category_filter=("error", "info")) data1 = get_flashed_messages(with_categories=True, category_filter=("error", "info")) print(data) return "信息有误" if __name__ == '__main__': app.run()

十二: 中间键

from flask import Flask

app = Flask(__name__)
app.debug = True

class MyMiddleware:

    def __init__(self, wsgi_app):
        self.wsgi_app = wsgi_app

    def __call__(self, environ, start_response):
        print('之前做操作')
        res = self.wsgi_app(environ, start_response)
        print('之后做操作')
        return res

@app.route('/index')
def index():
    return "ok"

if __name__ == '__main__':
    # app.__call__()
    app.run()

十三: request扩展

  总结: 请求之前有return, 有return的函数执行, 响应函数不执行, 请求之后的函数执行,

     执行的顺序和django的中间件一样, 请求之后的函数必须return response

from flask import Flask, render_template, request

app = Flask(__name__)
app.debug = True

@app.before_request
def before1():
    print(request)
    print('请求1')
    return "111"

@app.before_request
def before2():
    print(request)
    print('请求2')
    return "222"

@app.after_request
def after1(response):
    print("我是请求之后1")
    return response

@app.after_request
def after2(response):
    print("我是请求之后2")
    return response


# 有没有异常都会执行该函数, 没有e为None, 有e为错误信息
@app.teardown_request
def tear(e):
    print(e)

# 捕获异常, 如果出现异常, 而且状态是@app.errorhandler(404)
# 可以做404页面
@app.errorhandler(404)
def error_404(args):
    print(args)
    return "404错误"

# 标签(前端页面向后端函数传数据): template_global
@app.template_global()
def add(a, b):
    return a + b
# {{ add(2, 3) }}

# 过滤器: template_filter
@app.template_filter
def filter(x, y, z):
    return x + y + z
# {{ 2|filter(3, 4) }}

@app.route('/index')
def index():
    print('视图函数')
    return render_template('index.html')

if __name__ == '__main__':
    app.run()

 十四: 请求上下文

  线程安全: 存用唯一标识, 来标记你存的值, 取的时候,按标识去取

  偏函数:   有几个参数固定, 有几个不固定, 比默认函数好的是, 固定函数也可以改变

from  flask import Flask,request

app=Flask(__name__)

@app.route("/")
def index():
    print(request.form)

    return "1"



if __name__ == '__main__':
    #self,是app,app(),--->Flask对象,Flask的__call__
    app.__call__
    app.run()


'''
1     app.__call__
2     wsgi_app(environ, start_response) 
2.1 ctx = self.request_context(environ)
    2.1.1 return RequestContext(self, environ)
        这里的self是app,environ请求相关
    2.1.2 return RequestContext(self, environ)
    得到了RequestContext的对象,而且有request属性
2.2  2.1中的ctx就是RequestContext的对象  

2.3  ctx.push()执行这个,就是RequestContext的对象的push方法
     2.3.1  #执行这个,self-->ctx
        _request_ctx_stack.push(self) 
        2.3.1.1 我们发现_request_ctx_stack = LocalStack()
        他的push方法的源码:
                def push(self, obj):
                    rv = getattr(self._local, "stack", None)
                    if rv is None:     
                        # self._local=>stack-->storage['线程id']['stack']=[ctx,]
                        self._local.stack = rv = []
                    rv.append(obj)
                    return rv
                    
3在请求中获取request.form
3.1 request是LocalProxy的对象,当获取属性的时候会走__getattr__
    def __getattr__(self, name):
        if name == "__members__":
            return dir(self._get_current_object())
        #name-->form,
        #self._get_current_object()===>ctx.request,form
        #_get_current_object()---》self.__local()
        
        return getattr(self._get_current_object(), name)
        
    3.1.1 self._get_current_object():源码:最终:partial(_lookup_req_object, "request")
     def _get_current_object(self):
     
        if not hasattr(self.__local, "__release_local__"):
                #local==>partial(_lookup_req_object, "request")
                #def __init__(self, local, name=None):
                    # object.__setattr__(self, "_LocalProxy__local", local)
            #self.__local()===>local()
            return self.__local()
        try:
            return getattr(self.__local, self.__name__)
        except AttributeError:
            raise RuntimeError("no object bound to %s" % self.__name__)
4 partial(_lookup_req_object, "request")偏函数的源码
    def _lookup_req_object(name):
        #name是request
        #ctx
        top = _request_ctx_stack.top
        if top is None:
            raise RuntimeError(_request_ctx_err_msg)
        #ctx-->request
        return getattr(top, name)
    4.1中_request_ctx_stack.top
       @property
        def top(self):
        
        try:
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None
View Code

 

十五: 蓝图(flask项目目录)

  1. 简单应用程序目录示列

在pro_flask文件夹中
pro_flask |-- static | |-- img | |-- templates | |-- index.html | |-- login.html |
|--view |  |-- account.py| 
|  |-- blog.py|  
|  |-- user.py
| |-- __init__.py | run.py

   run.py

from pro_flask import app


if __name__ == '__main__':
    app.run()

  __init__.py

#!/usr/bin/env python
# -*-coding :utf-8 -*-

from flask import Flask

app = Flask(__name__, template_folder='templates', static_folder='statics', static_url_path='/static')

from .views.account import account
from .views.blog import blog
from .views.user import user

app.register_blueprint(account)
app.register_blueprint(blog)
app.register_blueprint(user)

  account.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Blueprint
from flask import render_template
from flask import request

account = Blueprint('acc', __name__)


@account.route('/login.html', methods=['GET', "POST"])
def login():
    return render_template('login.html')

  blog.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Blueprint,url_for

blog = Blueprint('blog', __name__)

@blog.before_request
def a():
    print("wo我是blog 的请求扩展")



@blog.route("/index")
def index():
    print(url_for("acc.login"))
    return "ok"

  

 

  2. 大型应用目录示列

在pro_flask文件夹中
pro_flask
|-- admin
|      |-- static   
|      |      |-- img
|      |-- templates
|      |      |-- index.html
|      |      
|      |-- views.py
|      |
|      | -- __init__.py
|
|--web 
|      |-- static   
|      |      |-- img
|      |-- templates
|      |      |-- index.html
|      |      
|      |-- views.py
|      |
|      | -- __init__.py
|
|-- __init__.py
|     
run.py

  __init__.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask
from .admin import admin
from .web import web

app = Flask(__name__)
app.debug = True

# 访问admin内的路径需加前缀admin app.register_blueprint(admin, url_prefix
='/admin') app.register_blueprint(web)

  admin/__init__.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Blueprint

admin = Blueprint(
    'admin',
    __name__,
    template_folder='templates',
    static_folder='static'
)
from . import views

  admin/views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from . import admin


@admin.route('/index')
def index():
    return 'Admin.Index'

十六: g对象

"""
g对象的特性:
    当前请求内你设置就可以取, 必须先设置, 然后取, 当前请求可以无限取
    就算你当前请求, 设置了, 如果不去取, 其它请求过来, 也取不到
"""
from flask import Flask, request, g, redirect

app = Flask(__name__)

@app.before_request
def before():
    g.name = 'hello'

def get_g():
    g.name = 'hello'

@app.route('/')
def index():
    # get_g()
    # print(g.name)
    return redirect('/login')

@app.route('/login')
def login():
    print(g.name)
    return "world"

if __name__ == '__main__':
    app.run()

十七: 信号

  Flask框架中的信号基于blinker,其主要就是让开发者可在flask请求过程中定制一些用户行为

  安装: 

pip3 install blinker

  1. 内置信号

request_started = _signals.signal('request-started')                # 请求到来前执行
request_finished = _signals.signal('request-finished')              # 请求结束后执行
 
before_render_template = _signals.signal('before-render-template')  # 模板渲染前执行
template_rendered = _signals.signal('template-rendered')            # 模板渲染后执行
 
got_request_exception = _signals.signal('got-request-exception')    # 请求执行出现异常时执行
 
request_tearing_down = _signals.signal('request-tearing-down')      # 请求执行完毕后自动执行(无论成功与否)
appcontext_tearing_down = _signals.signal('appcontext-tearing-down')# 应用上下文执行完毕后自动执行(无论成功与否)
 
appcontext_pushed = _signals.signal('appcontext-pushed')            # 应用上下文push时执行
appcontext_popped = _signals.signal('appcontext-popped')            # 应用上下文pop时执行
message_flashed = _signals.signal('message-flashed')                # 调用flask在其中添加数据时,自动触发

  内置信号的使用

from flask import Flask, signals, render_template

app = Flask(__name__)

def func(*args, **kwargs):
    print('hello', *args, **kwargs)

# 与该信号绑定
signals.request_started.connect(func)
# 触发信号: 由flask完成, signals.request_started.send()

@app.before_first_request
def before_first1(*args,**kwargs):
    print("before_first_request")


@app.before_request
def before_first2(*args,**kwargs):
    print("before_request")

@app.route('/index')
def index():
    print('world')
    return "world"

if __name__ == '__main__':
    app.run()

  2. 自定义信号

from flask import Flask, current_app, flash, render_template
from flask.signals import _signals

app = Flask(__name__)

# 自定义信号
xxx = _signals.signal("xxx")

def func(sender):
    print(sender, "我是自定义信号函数")

# 在自定义信号中注册函数
xxx.connect(func)

@app.route('/index')
def index():
    # 触发信号: 想在哪执行自定义信号, 就在哪执行下面代码
    print('你是大哥')
    xxx.send('hello')
    print('我是二哥')
    return "ok"

if __name__ == '__main__':
    app.run()

 十八: wtforms

  和django中的form组件相似(前段发送来的和后端返回的)

  安装:

pip3 install wtforms

  使用在flask的项目中

十九: flask_script

  1. 作用

用于实现类似于django中 python3 manage.py runserver ...类似的命令

  2. 安装

pip3 install flask_script

  3. 使用

from flask_script import Manager
app = Flask(__name__)
manager=Manager(app)

@manager.command
def custom(arg,a):
    """
    自定义命令
    python manage.py custom 123
    """
    print(arg,a) # 结果 custom 123

@manager.option('-n', '--name', dest='name')
@manager.option('-u', '--url', dest='url')
def cmd(name, url):
    """
    自定义命令(-n也可以写成--name)
    执行: python manage.py  cmd -n lqz -u http://www.oldboyedu.com
    执行: python manage.py  cmd --name lqz --url http://www.oldboyedu.com
    """
    print(name, url)

if __name__ == '__main__':
    manager.run()
#以后在执行,直接:python3 manage.py runserver
#python3 manage.py runserver --help

 二十: flask项目

  只有登录注册功能

  注意点: settings配置文件必须和manage文件在同一目录下, 不然会报错

   1. flask项目目录

flask_第1张图片

  settings.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-

# 不能导入
from pro_flask import create_app

class BaseConfig(object):
    # SESSION_TYPE = 'redis'  # session类型为redis
    # SESSION_KEY_PREFIX = 'session:'  # 保存到session中的值的前缀
    # SESSION_PERMANENT = True  # 如果设置为False,则关闭浏览器session就失效。
    # SESSION_USE_SIGNER = False  # 是否对发送到浏览器上 session:cookie值进行加密

    SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:[email protected]:3306/flask?charset=utf8"
    SQLALCHEMY_POOL_SIZE = 5
    SQLALCHEMY_POOL_TIMEOUT = 30
    SQLALCHEMY_POOL_RECYCLE = -1

    # 追踪对象的修改并且发送信号
    SQLALCHEMY_TRACK_MODIFICATIONS = False


class ProductionConfig(BaseConfig):
    pass


class DevelopmentConfig(BaseConfig):
    pass


class TestingConfig(BaseConfig):
    pass

  manage.py

 

#!/usr/bin/env python
# -*- coding:utf-8 -*-

# 导入app
from pro_flask import create_app
app = create_app()

from pro_flask import db

# 用命令操作文件
from flask_script import Manager
manager = Manager(app)

# 数据库迁移命令, 创建数据库
from flask_migrate import Migrate, MigrateCommand
# 为了实现迁移
Migrate(app, db)
# 注册命令
manager.add_command('db', MigrateCommand)


if __name__ == '__main__':
    # app.run()
    manager.run()

  templates

login.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>

<h1>登录h1>
<form method="post">
    <p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}p>
    <p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}p>
    <input type="submit" value="提交">
form>

body>
html>
register.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>

<h1>用户注册h1>
<form method="post" novalidate style="padding:0  50px">
    {% for field in form %}
    <p>{{field.label}}: {{field}} {{field.errors[0] }}p>
    {% endfor %}
    <input type="submit" value="提交">
form>

body>
html>

  views/user.py

from flask import Blueprint, request, render_template
user = Blueprint('user', __name__)

# 对前段发送的数据进行校验
from wtforms import Form
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets

from pro_flask import models


class RegisterForm(Form):
    name = simple.StringField(
        label='用户名',
        validators= [
            validators.DataRequired(message='密码不能为空'),
            validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
        ],
        widget=widgets.TextInput(),
        render_kw={'class': 'form-control'},
        default=''
    )
    pwd = simple.PasswordField(
        label='密码',
        validators=[
            validators.DataRequired(message='密码不能为空.'),
            validators.Length(min=8, message='用户名长度必须大于%(min)d'),
            validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
                              message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )

    pwd_confirm = simple.PasswordField(
        label='重复密码',
        validators=[
            validators.DataRequired(message='重复密码不能为空.'),
            #validators.EqualTo('pwd', message="两次密码输入不一致")
        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )

    email = html5.EmailField(
        label='邮箱',
        validators=[
            validators.DataRequired(message='邮箱不能为空.'),
            validators.Email(message='邮箱格式错误')
        ],
        widget=widgets.TextInput(input_type='email'),
        render_kw={'class': 'form-control'}
    )

    gender = core.RadioField(
        label='性别',
        choices=(
            (1, ''),
            (2, ''),
        ),
        coerce=int # “1” “2”
     )
    city = core.SelectField(
        label='城市',
        choices=(
            ('bj', '北京'),
            ('sh', '上海'),
        )
    )

    hobby = core.SelectMultipleField(
        label='爱好',
        choices=(
            (1, '篮球'),
            (2, '足球'),
        ),
        coerce=int
    )

    favor = core.SelectMultipleField(
        label='喜好',
        choices=(
            (1, '篮球'),
            (2, '足球'),
        ),
        widget=widgets.ListWidget(prefix_label=False),
        option_widget=widgets.CheckboxInput(),
        coerce=int,
        default=[1, 2]
    )
    
    def __init__(self, *args, **kwargs):
        super(RegisterForm, self).__init__(*args, **kwargs)
        # 用来控制字段属性, 不能控制default
        self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))
        self.favor.data=[1,]

    def validate_pwd_confirm(self, field):
        """
        自定义pwd_config字段规则, 判断其是否与pwd一致
        """
        # 最开始初始化时, self.data中已有所有的数据
        print(field.data, 1111111)
        if field.data != self.data.get('pwd'):
            # raise validators.ValidationError("密码不一致")  # 继续验证
            raise validators.StopValidation('密码不一致')  # 不在继续验证


class LoginForm(Form):
    # 字段(内部包含正则表达式)
    name = simple.StringField(
        label='用户名',
        validators=[
            validators.DataRequired(message='用户名不能为空.'),
            # validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
        ],
        widget=widgets.TextInput(), # 页面上显示的插件(input框)
        render_kw={'class': 'form-control'}  # 添加类属性class
    )
    # 字段(内部包含正则表达式)
    pwd = simple.PasswordField(
        label='密码',
        validators=[
            validators.DataRequired(message='密码不能为空.'),
            # validators.Length(min=8, message='用户名长度必须大于%(min)d'),
            # validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
            #                   message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')

        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )


@user.route('/register', methods=['GET', "POST"])
def register():
    if request.method == "GET":
        # 初始值, 默认值
        form = RegisterForm(data={'gender': 2, 'hobby': [1,]})
        return render_template('register.html', form=form)
    else:
        form = RegisterForm(formdata=request.form)
        if form.validate():
            print('数据正确')
            return "ok"
        else:
            print(form.errors)
            return render_template('register.html', form=form)


@user.route('/login', methods=['GET', "POST"])
def login():
    if request.method == 'GET':
        form = LoginForm()
        return render_template('login.html', form=form)
    else:
        form = LoginForm(formdata=request.form)
        if form.validate():
            print('数据格式正确')
            return "ok"
        else:
            print(form.errors)
            return render_template('login.html', form=form)

  __init__.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask

# sqlalchemy提供orm, flask_sqlalchemy与flask进行连接
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

from . import models
from .views import user


def create_app():
    # 生成Flask对象
    app = Flask(__name__,
                template_folder='templates',
                static_folder='statics',
                static_url_path='/static')
    # 导入配置文件
    app.config.from_object('settings.DevelopmentConfig')
    # 将db注册到app中
    db.init_app(app)
    # 注册蓝图
    app.register_blueprint(user.user)
    # 返回app
    return app

  models.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from . import db


class User(db.Model):
    """用户表"""
    __tablename__ = 'user'  # 表名
    id = db.Column(db.Integer, primary_key=True)  # 主键
    name = db.Column(db.String(80), index=True, unique=True, nullable=False)  # index 索引,nullable不可为空
    pwd = db.Column(db.String(120), unique=True, nullable=False)
  # datetime.datetime.now 不能加括号, 加括号后, 以后都是当前时间
# email = db.Column(db.String(120), unique=True, nullable=False)
  # ctime = db.Column(DateTime, default=datetime.datetime.now)
  # extra = db.Column(Text, nullable=True)
def __repr__(self): return self.username

 

你可能感兴趣的:(flask)