- __mro__
- 应用:wtforms中meta使用(meta作用定制csrf token)
示例
from flask import Flask,request,render_template,session,current_app,g,redirect from wtforms import Form from wtforms.csrf.core import CSRF from wtforms.fields import simple from hashlib import md5 from wtforms import widgets from wtforms import validators app = Flask(__name__) class MyCSRF(CSRF): """ Generate a CSRF token based on the user‘s IP. I am probably not very secure, so don‘t use me. """ def setup_form(self, form): self.csrf_context = form.meta.csrf_context() self.csrf_secret = form.meta.csrf_secret return super(MyCSRF, self).setup_form(form) def generate_csrf_token(self, csrf_token): gid = self.csrf_secret + self.csrf_context token = md5(gid.encode(‘utf-8‘)).hexdigest() return token def validate_csrf_token(self, form, field): print(field.data, field.current_token) if field.data != field.current_token: raise ValueError(‘Invalid CSRF‘) class LoginForm(Form): name = simple.StringField( validators=[ validators.DataRequired(message=‘用户名不能为空.‘), ], widget=widgets.TextInput(), render_kw={‘placeholder‘:‘请输入用户名‘} ) pwd = simple.PasswordField( validators=[ validators.DataRequired(message=‘密码不能为空.‘), ], render_kw={‘placeholder‘:‘请输入密码‘} ) class Meta: # -- CSRF # 是否自动生成CSRF标签 csrf = True # 生成CSRF标签name csrf_field_name = ‘csrf_token‘ # 自动生成标签的值,加密用的csrf_secret csrf_secret = ‘xxxxxx‘ # 自动生成标签的值,加密用的csrf_context csrf_context = lambda x: request.url # 生成和比较csrf标签 csrf_class = MyCSRF ‘‘‘ LoginForm._unbound_fields = None LoginForm._wtforms_meta = None LoginForm.name = UnboundField(creation_counter=1,simple.StringField) LoginForm.pwd = UnboundField(creation_counter=1,simple.PasswordField) UnboundField的作用:通过creation_counter的值进行排序 ‘‘‘ @app.route(‘/login‘,methods=[‘GET‘,‘POST‘]) def login(): if request.method == "GET": form = LoginForm() ‘‘‘ 实例化的时候执行FormMeta的__call__方法 LoginForm._unbound_fields = [ (‘name‘,UnboundField(creation_counter=1,simple.StringField),), (‘pwd‘,UnboundField(creation_counter=2,simple.PasswordField),), ] LoginForm._wtforms_meta = class Meta(Meta,DefaultMeta):pass ‘‘‘ return render_template(‘login.html‘,form=form) form = LoginForm(formdata=request.form) if form.validate(): print(form.data) return redirect(‘https://www.luffycity.com/home‘) else: return render_template(‘login.html‘, form=form) if __name__ == ‘__main__‘: app.run()
login.html
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <form method="post" novalidate> {{form.csrf_token}} <p>用户名:{{form.name}} {{form.name.errors[0]}}</p> <p>密码:{{form.pwd}} {{form.pwd.errors[0]}} </p> <p><input type="submit" value="提交" ></p> </form> </body> </html>
源码:
# form.py class FormMeta(type): def __init__(cls, name, bases, attrs): type.__init__(cls, name, bases, attrs) cls._unbound_fields = None cls._wtforms_meta = None (1) def __call__(cls, *args, **kwargs): if cls._unbound_fields is None: fields = [] for name in dir(cls): ‘‘‘ fields = [ (‘name‘,UnboundField(creation_counter=1,simple.StringField),), (‘pwd‘,UnboundField(creation_counter=2,simple.PasswordField),), ] ‘‘‘ if not name.startswith(‘_‘): unbound_field = getattr(cls, name) if hasattr(unbound_field, ‘_formfield‘): fields.append((name, unbound_field)) # We keep the name as the second element of the sort # to ensure a stable sort. 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 = [] ‘‘‘ [ DefaultMeta, ] ‘‘‘ for mro_class in cls.__mro__: if ‘Meta‘ in mro_class.__dict__: # DefaultMeta bases.append(mro_class.Meta) # cls._wtforms_meta = type(‘Meta‘, (Meta,DefaultMeta), {}) cls._wtforms_meta = type(‘Meta‘, tuple(bases), {}) return type.__call__(cls, *args, **kwargs) ‘‘‘ class NewBase(BaseForm,metaclass=FormMeta): pass class Form(NewBase) ‘‘‘ 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) super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix) for name, field in iteritems(self._fields): # Set all the fields to attributes so that they obscure the class # attributes with the same names. setattr(self, name, field) self.process(formdata, obj, data=data, **kwargs) #…… # meta.py class DefaultMeta(object): # …… csrf = False csrf_field_name = ‘csrf_token‘ csrf_secret = None csrf_context = None csrf_class = None #……