【问题标题】:Why __set_name__ does not get called for a StringField/flask_wtf为什么 __set_name__ 不会为 StringField/flask_wtf 调用
【发布时间】:2019-07-31 10:03:09
【问题描述】:

任何人都知道为什么没有为 StringField/flask_wtf 类调用钩子 set_name(由装饰函数添加)。虽然它被称为“简单构造”的类?如下代码所示:

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField, SelectField, IntegerField
from wtforms.validators import DataRequired, Email, ValidationError, StopValidation
from flask import Flask, render_template, flash, redirect, url_for, request, jsonify, session

def AddSetName(Cl) :
    class ClsWithSetName(Cl):
        def __set_name__(self, owner, name):
            print("Set Name ", self, owner, name,super(Cl,self))
#            super(Cl,self).__set_name__(self,owner,name)
            self.key = name
            try:
                owner.KeyFields.append(name)
            except :
                owner.KeyFields = [name]

    return ClsWithSetName

class Trait(object):
    def __init__(self, minimum, maximum):
        self.minimum = minimum
        self.maximum = maximum

    def __get__(self, instance, owner):
        print("get called ", self, instance, owner)
        return instance.__dict__[self.key]

    def __set__(self, instance, value):
        print("Set called ", self, instance, value)
        if self.minimum < value < self.maximum:
            instance.__dict__[self.key] = value
        else:
            raise ValueError("value not in range")

Trait = AddSetName(Trait)
StringField = AddSetName(StringField)

class T1:
    Val1=Trait(0,10)
    Val2=Trait(0,5)

class FrmLogin(FlaskForm):
    username=StringField('username', validators=[DataRequired])
    password=StringField('password', validators=[DataRequired])

if __name__ == '__main__':
    pass
    print(dir(StringField))
#    x = T1()
#    x.Val1 = 8
#    x.Val2 = 4
#    print(x)
#    print(T1.KeyFields)

>>> Set Name  <__main__.AddSetName.<locals>.ClsWithSetName object at 0x0000018E2A0FDD88> <class '__main__.T1'> Val1 <super: <class 'Trait'>, <ClsWithSetName object>>
>>>Set Name  <__main__.AddSetName.<locals>.ClsWithSetName object at 0x0000018E2A0FDE08> <class '__main__.T1'> Val2 <super: <class 'Trait'>, <ClsWithSetName object>>
>>>['__call__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__html__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__unicode__', '__weakref__', '_formfield', '_run_validation_chain', '_translations', '_value', 'do_not_call_in_templates', 'errors', 'gettext', 'ngettext', 'populate_obj', 'post_validate', 'pre_validate', 'process', 'process_data', 'process_errors', 'process_formdata', 'raw_data', 'validate', 'validators', 'widget']

输出没有显示对 StringField 的 set_name 的任何调用!

有什么“重新激活”或模仿这个电话的建议吗?

【问题讨论】:

    标签: python flask flask-wtforms python-3.7


    【解决方案1】:

    有一种情况可能导致类的“未调用 set_name”:当类包含不返回实例的 new 定义时(按预期返回对象的 None)。如果没有实例,python 不会调用该类的 initset_name

    我尝试使用 StringField 组件来检查这个案例:

    from flask_wtf import FlaskForm
    from wtforms import StringField
    from wtforms.validators import DataRequired
    from flask import Flask
    
    
    app = Flask(__name__ )
    
    @app.route('/', methods=['GET', 'POST'])
    def MainPage():
        Frm = FrmLogin()
    
    
    def AddSetName(Cl) :
        class ClsWithSetName(Cl):
            def __new__(cls, *args, **Kargs):
                if len(Kargs) == 0 :
                    inst = super(ClsWithSetName, cls).__new__(cls)
                else:
                    inst = super(ClsWithSetName, cls).__new__(cls, *args, **Kargs)
                print("new ", inst)
                return inst
    
            def __init__(self, *args, **Kargs):
                super(ClsWithSetName, self).__init__(*args, **Kargs)
                print("init get called")
    
            def __set_name__(self, owner, name):
                print("Set Name ", self, owner, name,super(Cl,self))
    #            super(Cl,self).__set_name__(self,owner,name)
                self.key = name
                try:
                    owner.KeyFields.append(name)
                except :
                    owner.KeyFields = [name]
    
        return ClsWithSetName
    
    class Trait(object):
        def __init__(self, minimum, maximum):
            self.minimum = minimum
            self.maximum = maximum
    
        def __get__(self, instance, owner):
            print("get called ", self, instance, owner)
            return instance.__dict__[self.key]
    
        def __set__(self, instance, value):
            print("Set called ", self, instance, value)
            if self.minimum < value < self.maximum:
                instance.__dict__[self.key] = value
            else:
                raise ValueError("value not in range")
    
    Trait = AddSetName(Trait)
    StringField = AddSetName(StringField)
    
    class T1:
        Val1=Trait(0,10)
    #    Val2=Trait(0,5)
        pass
    
    class FrmLogin(FlaskForm):
        username=StringField('username', validators=[DataRequired])
    #    password=StringField('password', validators=[DataRequired])
        pass
    
    if __name__ == '__main__':
        pass
    
    >>> new  <__main__.AddSetName.<locals>.ClsWithSetName object at 0x0000017F46386348>
    >>> init get called
    >>> Set Name  <__main__.AddSetName.<locals>.ClsWithSetName object at 0x0000017F46386348> <class '__main__.T1'> Val1 <super: <class 'Trait'>, <ClsWithSetName object>>
    
    >>> new  <UnboundField(ClsWithSetName, ('username',), {'validators': [<class 'wtforms.validators.DataRequired'>]})>
    

    如果我们在 new 钩子中刷出“返回”指令,则不会调用 initset_name。 同时,打印显示原始 new 调用为 'Trait' 字段返回一个对象(在特定地址),而在 StringField 的情况下它不返回一个对象(没有关联的地址with),而是一个名为“UnboundField”的类。 但是,不从 new 返回对象是一种不寻常的行为,对象实例的丢失使得 initset_name 的调用毫无意义。

    这可能会回答我的问题“为什么不调用 set_name”,但它没有提供模仿它的替代方法。

    【讨论】:

    • 我在 Stackoverflow 上变得更加活跃......无论如何,我需要提高我的分数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-17
    • 2022-09-23
    • 2018-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多