【问题标题】:Flask-SQLAlchemy: How do I call an instance method on a model instance before committing it to the database?Flask-SQLAlchemy:如何在模型实例提交到数据库之前调用实例方法?
【发布时间】:2017-09-01 02:00:25
【问题描述】:

我有一个想要初始化的模型,例如SomeModel(name='george', password='whatever').

在将它提交到数据库之前,我想调用另一个方法(我们称之为gen_password_hash)来创建密码的哈希版本,并将其设置为模型实例的属性。

所以我希望这发生在实例化之后,但在提交到数据库之前。

更新

我想看看是否可以通过在我的模型上定义 __init__ 函数来实现这一点。

def __init__(self, **kwargs):
    self.set_pass(kwargs.pop('password'))
    super(User, self).__init__(**kwargs)
    self.generate_email_address_confirmation_token()

这是我在尝试删除/重新创建表以重置我的应用程序时得到的回溯:

Traceback (most recent call last):
  File "main.py", line 7, in <module>
    import gg.cli
  File "/home/blaine/freelance/myproject/gg/cli/__init__.py", line 183, in <module>
    reset_all()
  File "/home/blaine/freelance/myproject/gg/cli/__init__.py", line 164, in reset_all
    load_test_data()
  File "/home/blaine/freelance/myproject/gg/cli/__init__.py", line 51, in load_test_data
    admin=True)
  File "<string>", line 4, in __init__
  File "/home/blaine/freelance/myproject/lib/python3.4/site-packages/sqlalchemy/orm/state.py", line 414, in _initialize_instance
    manager.dispatch.init_failure(self, args, kwargs)
  File "/home/blaine/freelance/myproject/lib/python3.4/site-packages/sqlalchemy/util/langhelpers.py", line 66, in __exit__
    compat.reraise(exc_type, exc_value, exc_tb)
  File "/home/blaine/freelance/myproject/lib/python3.4/site-packages/sqlalchemy/util/compat.py", line 187, in reraise
    raise value
  File "/home/blaine/freelance/myproject/lib/python3.4/site-packages/sqlalchemy/orm/state.py", line 411, in _initialize_instance
    return manager.original_init(*mixed[1:], **kwargs)
  File "/home/blaine/freelance/myproject/gg/users.py", line 67, in __init__
    self.generate_email_address_confirmation_token()
  File "/home/blaine/freelance/myproject/gg/users.py", line 71, in generate_email_address_confirmation_token
    token.update(self.email_address.encode() + current_app.secret_key + \
  File "/home/blaine/freelance/myproject/lib/python3.4/site-packages/werkzeug/local.py", line 347, in __getattr__
    return getattr(self._get_current_object(), name)
  File "/home/blaine/freelance/myproject/lib/python3.4/site-packages/werkzeug/local.py", line 306, in _get_current_object
    return self.__local()
  File "/home/blaine/freelance/myproject/lib/python3.4/site-packages/flask/globals.py", line 51, in _find_app
    raise RuntimeError(_app_ctx_err_msg)
RuntimeError: Working outside of application context.

This typically means that you attempted to use functionality that needed
to interface with the current application object in a way.  To solve
this set up an application context with app.app_context().  See the
documentation for more information.

我用this question for reference

【问题讨论】:

  • 这取决于gen_password_hash 的定义方式。如果它是在SomeModel 类中定义的,那么您应该能够实例化该类,然后在提交之前调用方法.gen_password_hash。但如果它是在模型之外定义的函数,那么您需要类似 user = SomeModel(name='george', password='whatever'); user.hashed_pwd = gen_password_hash(user.password); db.session.add(user); db.session.commit() 的东西。如果您有第二种情况,还要确保 hashed_pwd 是您的模型中定义的列。
  • 我明白了。这样可行。我希望使用__init__ 方便地包装所有这些东西,但我收到错误消息,说我在应用程序上下文之外工作。
  • 欢迎分享导致错误的代码,我或这里的其他人将很乐意提供修复。您绝对可以使用 init 来获取密码散列。
  • 好的,我已经添加了我尝试的解决方案。

标签: python-3.x sqlalchemy flask-sqlalchemy


【解决方案1】:

以下是我对您要做什么的解释。您想创建一个静态方法,将给定的字符串密码转换为散列密码。显然python的hash不是密码散列函数,所以我建议使用其他更一致和可靠的东西。在您的应用程序中初始化User 模型期间可以使用这种散列方法。这意味着每次您使用用户名、电子邮件和密码创建用户时,该密码在成为随后的User 对象的属性之前首先被散列。请看以下示例:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)


class User(db.Model):
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(120), unique=True)
    password = db.Column(db.String(36))

    def __init__(self, username, email, password):
        self.username = username
        self.email = email
        self.password = self.generate_pwd_hash(password)

    @staticmethod
    def generate_pwd_hash(pwd):
        """
        This is where you define how
        to hash the user's password.
        """
        return str(hash(pwd))  # This is, of course, not the way to hash a password


db.create_all()

user = User(username='george',
            email='george@example.com',
            password='whatever')

try:
    db.session.add(user)
    db.session.commit()
except:
    print("This username must already exist.")

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-28
    • 2016-05-31
    • 2018-03-21
    • 2018-07-03
    • 2016-08-30
    • 1970-01-01
    • 2014-08-19
    • 1970-01-01
    相关资源
    最近更新 更多