【发布时间】:2018-07-19 21:10:23
【问题描述】:
我在 SQLAlchemy 中有一个用户类。我希望能够在数据库中加密用户的电子邮件地址属性,但仍然可以通过过滤查询进行搜索。
我的问题是,如果我使用@hybrid_property,我的查询理论上有效,但我的构造无效,如果我使用@property,我的构造有效,但我的查询无效
from cryptography.fernet import Fernet # <- pip install cryptography
from werkzeug.security import generate_password_hash
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email_hash = db.Column(db.String(184), unique=True, nullable=False)
password_hash = db.Column(db.String(128))
# @property # <- Consider this as option 2...
@hybrid_property # <- Consider this as option 1...
def email(self):
f = Fernet('SOME_ENC_KEY')
value = f.decrypt(self.email_hash.encode('utf-8'))
return value
@email.setter
def email(self, email):
f = Fernet('SOME_ENC_KEY')
self.email_hash = f.encrypt(email.encode('utf-8'))
@property
def password(self):
raise AttributeError('password is not a readable attribute.')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
# other checks and modifiers
对于选项 1:当我尝试使用 User(email='a@example.com',password='secret') 构建用户时,我会收到回溯,
~/models.py in __init__(self, **kwargs)
431 # Established role assignment by default class initiation
432 def __init__(self, **kwargs):
--> 433 super(User, self).__init__(**kwargs)
434 if self.role is None:
435 _default_role = Role.query.filter_by(default=True).first()
~/lib/python3.6/site-packages/sqlalchemy/ext/declarative/base.py in _declarative_constructor(self, **kwargs)
697 raise TypeError(
698 "%r is an invalid keyword argument for %s" %
--> 699 (k, cls_.__name__))
700 setattr(self, k, kwargs[k])
701 _declarative_constructor.__name__ = '__init__'
TypeError: 'email' is an invalid keyword argument for User
对于选项 2:如果我将 @hybrid_property 更改为 @property,则构造很好,但我的查询 User.query.filter_by(email=form.email.data.lower()).first() 失败并返回 None。
我应该进行哪些更改才能使其按要求工作?
==============
注意我应该说我已经尽量避免使用双重属性,因为我不想对底层代码库进行大量编辑。所以我明确地试图避免将创建与User(email_input='a@a.com', password='secret') 和User.query.filter_by(email='a@a.com').first() 的查询分开:
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email_hash = db.Column(db.String(184), unique=True, nullable=False)
password_hash = db.Column(db.String(128))
@hybrid_property
def email(self):
f = Fernet('SOME_ENC_KEY')
value = f.decrypt(self.email_hash.encode('utf-8'))
return value
@property
def email_input(self):
raise AttributeError('email_input is not a readable attribute.')
@email_input.setter
def email_input(self, email):
f = Fernet('SOME_ENC_KEY')
self.email_hash = f.encrypt(email.encode('utf-8'))
@property
def password(self):
raise AttributeError('password is not a readable attribute.')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
# other checks and modifiers
【问题讨论】:
标签: properties sqlalchemy flask-sqlalchemy getter-setter