Meta

- __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
    
    #……
   
原文地址:https://www.cnblogs.com/zh-xiaoyuan/p/13267695.html