238.14.flask-简单项目
1.目录结构
2.元类
# coding=utf-8
from wtforms.compat import with_metaclass
class MyType(type):
# def __init__(cls, name, bases, attrs):
# type.__init__(cls, name, bases, attrs)
def __call__(cls, *args, **kwargs):
print("__call__")
return type.__call__(cls, *args, **kwargs)
class BaseFoo(object):
def __init__(self):
print("base foo")
class Foo(with_metaclass(MyType, BaseFoo)): # 先执行元类的__call__->__new__->__init__
def __init__(self, name):
print("__init__")
def __new__(cls, *args, **kwargs):
print("__new__")
return super(Foo, cls).__new__(cls)
# obj = Foo("abc")
"""1. 创建类的两种方式"""
###########################################################################
# class Foo2(object):
# CITY = "bj"
# def func(self, x):
# return x + 1
#
#
# Foo3 = type("Foo", (object, ), {"CITY": "bj", "func": lambda self, x: x+1})
"""2. 由自定的type创建"""
###########################################################################
# class MyType(type):
# def __init__(self, *args, **kwargs):
# print("创建类之前")
# super(MyType, self).__init__(*args, **kwargs)
# print("创建类之后")
#
#
# # 通过__metaclass__指定当前类由那个类创建
# # Foo2 = type(MyType, (object, ), {"CITY": "bj", "func": lambda self, x: x+1})
# class Foo2(object):
# __metaclass__ = MyType # 有MyType()创建类
# CITY = "bj"
#
# def func(self, x):
# return x + 1
#
#
# # 当一个类继承的基类中由metaclass创建, 那么当前类也是由metaclass类创建
# # Bar = type(MyType, (Foo2, object, ), {"CITY": "bj", "func": lambda self, x: x+1})
# class Bar(Foo2):
# pass
"""3. 变化"""
###########################################################################
# class MyType(type):
# def __init__(self, *args, **kwargs):
# print("创建类之前")
# super(MyType, self).__init__(*args, **kwargs)
# print("创建类之后")
#
#
# def with_metaclass2(arg):
# return MyType("Base", (arg, ), {})
#
#
# class Foo2(with_metaclass2(object)):
# CITY = "bj"
#
# def func(self, x):
# return x + 1
"""4.类实例化"""
###########################################################################
class MyType(type):
def __init__(self, *args, **kwargs):
super(MyType, self).__init__(*args, **kwargs)
class Foo2(object):
__metaclass__ = MyType
"""
Foo2 = type(MyType, (object, ), {});
obj = Foo2() 执行MyType的__call__方法
因此执行流程:
Foo2类创建时
0.MyType的__init__
obj = Foo2()
1.MyType的__call__
2.Foo2的__new__方法
3.Foo2的__init__方法
总结:
默认一个类由type实例化创建
某个类指定了metaclass=MyType, 那么当前类的所有派生类都由MyType创建
实例化对象
- type.__init__
- type.__call__
- 类.__new__
- 类.__init__
"""
obj = Foo2()
3.类继承查找顺序
class A(object):
pass
class B(object):
pass
class C(A, B):
pass
c = C()
print(C.mro())
4.数据库链接池
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pymysql
# 创建连接
conn = pymysql.connect(host='xxxx', port=33006, user='root', passwd='x', db='xxxx')
# 创建游标
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 执行SQL,并返回收影响行数
effect_row = cursor.execute("select * from user where name=%s and pwd=%s", ["alex", 123])
obj = cursor.fetchone()
# 执行SQL,并返回受影响行数
# effect_row = cursor.execute("update hosts set host = '1.1.1.2' where nid > %s", (1,))
# 执行SQL,并返回受影响行数
# effect_row = cursor.executemany("insert into hosts(host,color_id)values(%s,%s)", [("1.1.1.11",1),("1.1.1.11",2)])
# 提交,不然无法保存新建或者修改的数据
# conn.commit()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
print(obj)
5.数据库链接池2
# coding=utf-8
# dbutils数据库链接池 https://www.cnblogs.com/wupeiqi/articles/8184686.html
# import pymysql
# from dbutils.persistent_db import PersistentDB
#
"""方式 1"""
# POOL = PersistentDB(
# creator=pymysql, # 使用链接数据库的模块
# maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制
# setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
# ping=0,
# # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
# closeable=False,
# # 如果为False时, conn.close() 实际上被忽略,供下次使用,再线程关闭时,才会自动关闭链接。如果为True时, conn.close()则关闭链接,那么再次调用pool.connection时就会报错,因为已经真的关闭了连接(pool.steady_connection()可以获取一个新的链接)
# threadlocal=None, # 本线程独享值得对象,用于保存链接对象,如果链接对象被重置
# host='127.0.0.1',
# port=3306,
# user='root',
# password='123',
# database='pooldb',
# charset='utf8'
# )
#
#
# def func():
# conn = POOL.connection(shareable=False)
# cursor = conn.cursor()
# cursor.execute('select * from tb1')
# result = cursor.fetchall()
# cursor.close()
# conn.close()
#
#
# func()
"""模式 2"""
import time
import pymysql
import threading
# from DBUtils.PooledDB import PooledDB, SharedDBConnection
from dbutils.pooled_db import PooledDB
POOL = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
maxcached=5, # 链接池中最多闲置的链接,0和None不限制
maxshared=3, # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制
setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
ping=0,
# ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host='xxxx',
port=33006,
user='root',
password='xxx',
database='xxx',
charset='utf8'
)
def func():
# 检测当前正在运行连接数的是否小于最大链接数,如果不小于则:等待或报raise TooManyConnections异常
# 否则
# 则优先去初始化时创建的链接中获取链接 SteadyDBConnection。
# 然后将SteadyDBConnection对象封装到PooledDedicatedDBConnection中并返回。
# 如果最开始创建的链接没有链接,则去创建一个SteadyDBConnection对象,再封装到PooledDedicatedDBConnection中并返回。
# 一旦关闭链接后,连接就返回到连接池让后续线程继续使用。
conn = POOL.connection()
# print(th, '链接被拿走了', conn1._con)
# print(th, '池子里目前有', pool._idle_cache, '\r\n')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute('select * from user')
result = cursor.fetchall()
conn.close()
return result
print(func())
6.wtforms组件
# coding=utf-8
"""
wtforms 组件
https://www.cnblogs.com/wupeiqi/articles/8202357.html
"""
from flask import request
from wtforms import Form
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets
"""
执行流程:
1.Form方式继承是通过metaclass实现的
class Form(with_metaclass(FormMeta, BaseForm)): == class Form(BaseForm, metaclass =FormMeta)
def with_metaclass(meta, base=object):
return meta("NewBase", (base,), {}) # 执行meta的__init__方法
2.此时执行meta的__init__方法
def __init__(cls, name, bases, attrs):
type.__init__(cls, name, bases, attrs) # 调用type方法的__init__
cls._unbound_fields = None # 增加两个属性_unbound_fields和_wtforms_meta
cls._wtforms_meta = None
3.此时多了几个属性
LoginForm._unbound_fields = None
LoginForm._wtforms_meta = None
LoginForm.name = simple.StringField(xxx) # 一个类实例化一个对象时先执行__new__在执行__init__
StringField的基类的__new__方法如下
def __new__(cls, *args, **kwargs):
if '_form' in kwargs and '_name' in kwargs:
return super(Field, cls).__new__(cls)
else: # 因为是实例化所以没有_form和_name属性, 因此返回UnboundField对象
return UnboundField(cls, *args, **kwargs)
所以一开都是UnboundField对象
4.UnboundField(cls, *args, **kwargs)
class UnboundField(object):
_formfield = True
creation_counter = 0 # 计数器, 类初始化的时候执行
def __init__(self, field_class, *args, **kwargs):
# 每次实例化都+1的计数器, 也就是说每个字段的计数器都是累增的
UnboundField.creation_counter += 1
self.field_class = field_class
self.args = args
self.kwargs = kwargs
self.creation_counter = UnboundField.creation_counter
validators = kwargs.get('validators')
if validators:
self.field_class.check_validators(validators)
5. form = LoginForm() # 先执行LoginForm的基类metaclass的__call__方法因为
LoginForm = meta("NewBase", (base,), {}) 说明已经初始化完成了, 此时LoginForm时meta的一个实例化对象
form = LoginForm()也就是对象加()表示执行当前实例化改对象的类的__call__方法, 也就是mate的__call__方法
# 下面看他的__call__方法
class FormMeta(object):
def __call__(cls, *args, **kwargs):
if cls._unbound_fields is None:
fields = []
for name in dir(cls):
if not name.startswith('_'): # 排除私有以及内置属性
unbound_field = getattr(cls, name)
if hasattr(unbound_field, '_formfield'): # 排除其他的静态属性, 只要_formfield
fields.append((name, unbound_field))
# 根据每个filed的UnboundField计数器和x[0]对字段进行排序
fields.sort(key=lambda x: (x[1].creation_counter, x[0]))
cls._unbound_fields = fields
# Create a subclass of the 'class Meta' using all the ancestors.
if cls._wtforms_meta is None:
bases = [] # 找到当前类的所有基类同事包含当前类
# 因此bases=[]
# cls.__mro__ = [LoginForm, Form, FormMeta, BaseForm, ....] 每个基类都一直向上找知道object
for mro_class in cls.__mro__:
# 哪个类由Meta就将这个Meta放到bases中, 其实可以发现只有Form.Meta = DefaultMeta, bases=[DefaultMeta]
# 假设我们自己在LoginForm中定义一个Meta, 此时bases=[DefaultMeta, XXX]
if 'Meta' in mro_class.__dict__:
bases.append(mro_class.Meta)
# 下面这行代码更神奇的就是串讲一个对象cls._wtforms_meta, 这个对象的类继承自cls._wtforms_meta(DefaultMeta, XXX)
# cls._wtforms_meta = class Meta(DefaultMeta, XXX)
cls._wtforms_meta = type('Meta', tuple(bases), {})
return type.__call__(cls, *args, **kwargs)
6.接下来继续执行LoginForm的__new__方法, 因为要实例化对象了
但是基类以及自己中都没有发现__new__方法
7.执行LoginForm的__init__方法
# 找到最近的基类From的__init__方法
class Form(with_metaclass(FormMeta, BaseForm)):
Meta = DefaultMeta
def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):
meta_obj = self._wtforms_meta()
if meta is not None and isinstance(meta, dict):
meta_obj.update_values(meta)
# 将定义的所有字段传到Form的基类中其实就是baseForm.__init__()
super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)
# (先看7.1)当在这里将self._fields遍历, 此时才是给LoginForm对象赋值
# LoginForm对象.name = simple.StringField() ...
for name, field in iteritems(self._fields):
setattr(self, name, field)
self.process(formdata, obj, data=data, **kwargs)
7.1 BaseForm的__init__方法更有意思
class BaseForm(object):
def __init__(self, fields, prefix='', meta=DefaultMeta()):
if prefix and prefix[-1] not in '-_;:/.':
prefix += '-'
self.meta = meta
self._prefix = prefix
self._fields = OrderedDict()
if hasattr(fields, 'items'):
fields = fields.items()
translations = self._get_translations()
extra_fields = []
if meta.csrf:
self._csrf = meta.build_csrf(self)
extra_fields.extend(self._csrf.setup_form(self))
# itertools.chain很简单, 找两个二元组a=['11', '12', '13'], b=['a1', 'b2', 'c3'], 例子做下就可以明白
for name, unbound_field in itertools.chain(fields, extra_fields):
options = dict(name=name, prefix=prefix, translations=translations)
# 这个地方比较牛逼, 实际上就是调用UnboundField.bind方法, 他里面有一个属性叫做filed_class, 按照下面的LoginForm的name为例
# 就是filed_class=simple.StringField, 去实例化对象
# UnboundField(cls, *args, **kwargs), 这个cls就是当时传入的filed_class=simple.StringField, 变了个名称而已
field = meta.bind_field(self, unbound_field, options)
self._fields[name] = field
# 这个迭代完成只有
self._fields = {
"name" = simple.StringField(),
....
}
8.form = LoginForm(formdata={})form.user # 执行form的__str__
# 发现直接执行self()也就执行自己的__call__方法
def __str__(self):
return self()
# __call__方法
def __call__(self, **kwargs):
return self.meta.render_field(self, kwargs)
# render_field
def render_field(self, field, render_kw):
other_kw = getattr(field, 'render_kw', None)
if other_kw is not None:
render_kw = dict(other_kw, **render_kw)
return field.widget(field, **render_kw)
# StringField中的widget, 也就是说widget是一个TextInput类对象. 加()又需要执行__call__方法
widget = widgets.TextInput()
class Input(object):
def __call__(self, field, **kwargs):
kwargs.setdefault('id', field.id)
kwargs.setdefault('type', self.input_type)
if 'value' not in kwargs:
kwargs['value'] = field._value()
if 'required' not in kwargs and 'required' in getattr(field, 'flags', []):
kwargs['required'] = True
# 打印input框, 这就是form对象可以生成html标签的原因
return Markup('<input %s>' % self.html_params(name=field.name, **kwargs))
9. form = LoginForm(formdata={}) for item in form: print(item) 能for循环就是有__iter__方法
class BaseForm(object):
def __iter__(self):
# itervalues表示拿到所有value值, 也就每个字段 name = simple.StringField()
return iter(itervalues(self._fields))
此时print(item)表示执行他的__str__方法, 也就是打印html标签
10. form = LoginForm(formdata={}) 执行流程
1.调用process传递formdata数据
self.process(formdata, obj, data=data, **kwargs)
2.调用filed的process函数
def process(self, formdata=None, obj=None, data=None, **kwargs):
formdata = self.meta.wrap_formdata(self, formdata)
for name, field, in iteritems(self._fields):
if obj is not None and hasattr(obj, name):
field.process(formdata, getattr(obj, name))
elif name in kwargs:
field.process(formdata, kwargs[name])
else:
field.process(formdata)
3.filed的process函数
def process(self, formdata, data=unset_value):
self.process_errors = []
if data is unset_value:
try:
data = self.default()
except TypeError:
data = self.default # data等于默认值
if formdata is not None:
if self.name in formdata: # 如果字段名称在formdata中
self.raw_data = formdata.getlist(self.name) # 获取row_data
else:
self.raw_data = []
try:
self.process_formdata(self.raw_data)
except ValueError as e:
self.process_errors.append(e.args[0])
4.StringField的process_formdata函数, 发现其实他只取第一个输入, 设置给self.data, 至此self.data设置完毕
class StringField(Field):
def process_formdata(self, valuelist):
if valuelist:
self.data = valuelist[0]
elif self.data is None:
self.data = ''
11. form.validate() 执行流程
1.Form的validate方法
class Form(with_metaclass(FormMeta, BaseForm)):
def validate(self, extra_validators=None):
# extra_validators 发现可以定义一些扩展的validators验证器
if extra_validators is not None:
extra = extra_validators.copy()
else:
extra = {}
# 同时也可以定义一些以validate_%s 开头的验证函数, 都会被加入到extra中
for name in self._fields:
inline = getattr(self.__class__, 'validate_%s' % name, None)
if inline is not None:
extra.setdefault(name, []).append(inline)
# 之后调用Form的基类的validate方法
return super(Form, self).validate(extra)
2.BaseForm的validate方法
class BaseForm(object):
def validate(self, extra_validators=None):
success = True
# 迭代每个filed对象
for name, field in iteritems(self._fields):
if extra_validators is not None and name in extra_validators:
extra = extra_validators[name]
else:
extra = tuple()
# 调用每个filed对象各自的validate方法
if not field.validate(self, extra):
success = False
return success
3.StringField的validate方法
class StringField:
def validate(self, form, extra_validators=tuple()):
try:
# 在进行validate之前可以进行一些操作, 我们可以重写这个方法
self.pre_validate(form)
except StopValidation as e:
if e.args and e.args[0]:
self.errors.append(e.args[0])
stop_validation = True
except ValueError as e:
self.errors.append(e.args[0])
# 这个才是执行真正的验证的地方
if not stop_validation:
# 将self.validators 和 extra_validators 合并, self.validators等于StringField初始化时的数组
chain = itertools.chain(self.validators, extra_validators)
# 执行验证操作, 你会发现只要stop_validation这个变量不是true就会一直验证,
# 因此如果我们只想验证一部分参数可以再某些参数验证完成之后将其变成true
stop_validation = self._run_validation_chain(form, chain)
# Call post_validate
try:
# 在进行validate之后可以进行一些操作, 我们可以重写这个方法
self.post_validate(form, stop_validation)
except ValueError as e:
self.errors.append(e.args[0])
return len(self.errors) == 0
4. 执行验证操作
def _run_validation_chain(self, form, validators):
# 一个一个验证器向下进行validator(form, self)其实就是执行验证的__call__方法
for validator in validators:
try:
validator(form, self)
except StopValidation as e:
if e.args and e.args[0]:
self.errors.append(e.args[0])
return True
except ValueError as e:
# self.errors可以通过变量errors数组拿到所有异常报错
self.errors.append(e.args[0])
return False
5. 以validators.DataRequired(message='用户名不能为空.')为例
class DataRequired(object):
def __call__(self, form, field):
# 如果filed.data没值, 或者它是一个字符串类型, 同时去掉两边空格之后为空则, 就验证失败
if not field.data or isinstance(field.data, string_types) and not field.data.strip():
if self.message is None:
message = field.gettext('This field is required.')
else:
message = self.message
field.errors[:] = []
raise StopValidation(message)
# 其实通过这里就发现其实自定义一个验证器很简单, 只要定义好__init__和__call__就可以了
"""
class LoginForm(Form):
class Meta:
pass
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'}
)
request.form
form = LoginForm(formdata={})
form.user # 执行form的__str__
form.validate()
项目地址: https://gitee.com/maxhope8/file_store.git 15.flask-session.zip