Как использовать колбу-админ для редактирования модели

Как установить password_hash с помощью generate_password_hash со страницы редактирования на flask-admin

  1. Я создаю имя пользователя и пароль в оболочке Python. пароль хэширует
  2. admin.add_view(MyModelView(пользователь, db.session) - позвольте мне редактировать пользовательский класс Models
  3. когда я редактирую пароль и отправляю, но пароль сохраняется в виде простого текста.

Как отредактировать пароль от flask-admin, пароль должен быть сохранен в виде хеширования

Мой код:

from werkzeug.security import generate_password_hash, check_password_hash
class User(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     email = db.Column(db.String(120))
     password_hash = db.Column(db.String(64))
     username = db.Column(db.String(64), unique=True, index=True)

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

     def __repr__(self):
          return '<User %r>' % self.username

#Create custom models view
class MyModelView(sqla.ModelView):
    @admin.expose('/login/')
    def index(self):
        return self.render('login.html')

# Create custom admin view
class MyAdminView(admin.BaseView):
    @admin.expose('/')
    def index(self):
        return self.render('myadmin.html')

admin = admin.Admin(name="Simple Views")
admin.add_view(MyAdminView(name='hello'))
admin.add_view(MyModelView(User, db.session))
admin.init_app(app)
app.run()

5 ответов

Решение

Я решил свою проблему с помощью функции on_model_change в flask-admin

#Create custom models view
class MyModelView(sqla.ModelView):
    @admin.expose('/login/')
    def index(self):
        return self.render('login.html')
    def on_model_change(self, form, User, is_created=False):
        User.password = form.password_hash.data

Альтернативным решением является подкласс TextField добавление пользовательской логики обработки:

class MyPassField(TextField):
    def process_data(self, value):
        self.data = ''  # even if password is already set, don't show hash here
        # or else it will be double-hashed on save
        self.orig_hash = value

    def process_fromdata(self, valuelist):
        value = ''
        if valuelist:
            value = valuelist[0]
        if value:
            self.data = generate_password_hash(value)
        else:
            self.data = self.orig_hash

class UserView(ModelView):
    form_overrides = dict(
        passhash=MyPassField,
    )
    form_widget_args = dict(
        passhash=dict(
            placeholder='Enter new password here to change password',
        ),
    )

Более простое решение, вам не нужно создавать подклассы TextField

Просто добавь on_form_prefill:

def on_model_change(self, form, User, is_created):
    if form.password_hash.data:
        User.set_password(form.password_hash.data)
    else:
        del form.password_hash

def on_form_prefill(self, form, id):
    form.password_hash.data = ''

Это предотвратит двойное хеширование.

Я попробовал решения, изложенные в некоторых других ответах, и добился успеха лишь частично. Возникли проблемы с возможностью редактирования пользователя позже, а также с изменением пароля или его полным исчезновением.

Одно открытие, которое я сделал, было то, что on_model_change фактически вызывается ПОСЛЕ того, как модель заполняется из формы. Невозможно получить доступ к старым значениям модели, не обращаясь к базе данных или исправлениям обезьян update_model,

Я придумал более простую версию (я считаю), которая работает во всех сценариях.

Вот весь вид:

class UserView(AdminModel):
    can_create = True
    column_list = ('name', 'email',)
    column_searchable_list = ('name', 'email',)
    form_excluded_columns = ('password',)
    form_extra_fields = {
        'set_password': PasswordField('Set New Password')
    }

    def on_model_change(self, form, model, is_created):
        if is_created:
            model.active = True
            model.pending = False
        if form.email.data:
            # Strip spaces from the email
            form.email = form.email.data.strip()
        if form.set_password.data:
            model.password = bcrypt.generate_password_hash(form.set_password.data.strip())

    def __init__(self, session, **kwargs):
        # You can pass name and other parameters if you want to
        super(UserView, self).__init__(User, session, **kwargs)

Что я сделал, так это добавил поле формы set_password который при заполнении создает хэш пароля и обновляет password на модели.

Один и готово!

Мое решение состояло в том, чтобы просто добавить поле форматирования столбца в модель UserView, которая возвращает dict всех полей формы. затем выбрал форматирование пароля, сохраняемого с помощью хеша bcrypt.

      class UserView(ModelView):
        column_formatters =dict(password=lambda v,c,m,password: bcrypt.generate_password_hash(m.password, config.get('BCRYPT_LOG_ROUNDS')) \
                .decode('utf-8'))

Подробнее об аргументах, которые принимает лямбда-функция, можно найти в официальной документации flaskAdmin. column_formatters

Другие вопросы по тегам