Mongoengine 0.8.0在模型中打破了我的自定义setter属性

时间:2013-06-02 09:49:37

标签: python mongoengine

在Mongoengine 0.7.10中,我仍然可以做以下事情:

class User(db.Document):
    email = db.EmailField(unique=True, required=True)
    _password = db.StringField(max_length=255, required=True)

    @property
    def password(self):
        return self._password

    @password.setter
    def password(self, password):
        self._password = bcrypt.generate_password_hash(password)
user = User(email='1@1.com', password='12345')

但是,上面的代码在0.8.0中断: ValidationError: ValidationError (User:None) (_password.Field is required: ['User'])

似乎MongoEngine在启动期间无法识别我的自定义密码设置器。我必须手动编写这些来修复它:

user = User(email='1@1.com')
user.password='12345'

这可能是由于以下变化(来自Mongonengine 0.8 upgrading notes):

Previously, if you had data the database that wasn’t defined in the Document definition, it would set it as an attribute on the document. This is no longer the case and the data is set only in the document._data dictionary:

我想知道这是故意还是MongoEngine中的错误?在我的模型中编写自定义属性setter的最佳做法是什么?

6 个答案:

答案 0 :(得分:5)

试试这段代码:

class User(db.Document):
    email = db.EmailField(unique=True, required=True)
    _password = db.StringField(max_length=255, required=True, db_field='password')

    def __init__(self, *args, **kwargs):
            db.Document.__init__(self, *args, **kwargs)

            if 'password' in kwargs:
                self.password = kwargs['password']

    @property
    def password(self):
        return self._password

    @password.setter
    def password(self, password):
        self._password = bcrypt.generate_password_hash(password)
user = User(email='1@1.com', password='12345')

它对我有用。

答案 1 :(得分:1)

这不是一个错误 - 它可以清除其他一些错误,并且是升级文档中提到的预期操作。但是,这对这种情况没有好处!

我认为创建密码哈希的自定义方法会更好,例如:set_password check_password等...

过去有过PasswordField实现,我将添加到extras-mongoengine中。

答案 2 :(得分:1)

您应该使用文档clean函数described in the mongoengine documentation

class User(db.Document):
    email = db.EmailField(unique=True, required=True)
    password = db.StringField(max_length=255, required=True)

    def clean(self):
        if not hashed(self.password):
            self.password = bcrypt.generate_password_hash(self.password)

    def hashed(self, password):
        return  # boolean check of whether the password is already hashed

user = User(email='1@1.com', password='12345')
user.save()

每次调用user.save()时,它现在都会检查您的密码是否类似于哈希值,如果不是,则在更新数据库之前对其进行哈希处理。

答案 3 :(得分:0)

这不是一个很好的解决方案,但是我真的很想通过在模型级别(而不是在一个或多个视图中)使用安全密码哈希将表单直接推送到模型中。只需将password重命名为_password,运行mongoengine文档验证并设置self._password

class User(db.Document):
    """User model."""

    username = db.StringField(max_length=50, required=True, unique=True)
    _password = db.StringField(max_length=255, required=True)

    def __init__(self, *args, **kwargs):
        kwargs['_password'] = kwargs.pop('password')
        db.Document.__init__(self, *args, **kwargs)
        self.password = kwargs['_password']

    @property
    def password(self):
        return self._password

    @password.setter
    def password(self, password):
        self._password = bcrypt.generate_password_hash(password)

答案 4 :(得分:0)

在我的示例中,我用哈希值覆盖了密码并将其存储为BinaryField。

class Users(Document):
    name = db.StringField(required=True)
    password = db.BinaryField(required=True)

    def __init__(self, *args, **kwargs):
        if 'password' in kwargs:
            password = kwargs['password']
            if not isinstance(password, bytes): # if it creates from user input, not from DB's load
                kwargs['password'] = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
        Document.__init__(self, *args, **kwargs)

答案 5 :(得分:0)

在最新版本的MongoEngine中,您将获得一个

FieldDoesNotExist: The fields "{'password'}" do not exist on the document "User"

当您尝试使用@property之类的上述答案时。

为避免这种情况,请在您的元字典中使用'strict':False

我希望读者知道我投入了大量时间为您提供这些信息,仅此而已。