how to use flask-admin for editing modelview
Asked Answered
M

5

4

How to set password_hash using generate_password_hash from the edit page of flask-admin

  1. i create a username and password in python shell. the password is hashing
  2. admin.add_view(MyModelView(User, db.session) - let me edit the User class Models
  3. when i edit the password and submit but the password is saved in plain text.

How to edit password from flask-admin, the password should be save in hashing type

My code is:

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()
Molini answered 10/3, 2015 at 17:17 Comment(0)
M
6

i solved my problem by using on_model_change function in 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
Molini answered 11/3, 2015 at 3:56 Comment(1)
Be warned that if password is already set then you will get password hash prefilled in your form, which after submitting will be re-hashed corrupting actual password. See my answer for an alternative solution.Nanananak
N
8

Alternative solution is to subclass a TextField adding custom processing logic:

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_formdata(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',
        ),
    )
Nanananak answered 19/6, 2016 at 20:55 Comment(1)
def process_fromdata -> def process_formdataKeloid
M
6

i solved my problem by using on_model_change function in 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
Molini answered 11/3, 2015 at 3:56 Comment(1)
Be warned that if password is already set then you will get password hash prefilled in your form, which after submitting will be re-hashed corrupting actual password. See my answer for an alternative solution.Nanananak
R
3

Simpler solution, you don't need to subclass TextField

Just add 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 = ''

This will prevent double-hashing.

Rounders answered 16/7, 2016 at 18:31 Comment(1)
What is the reason to delete form.password_hash field object in your on_model_change?Nanananak
O
2

I tried the solutions outlined in some of the other answers and I was only partially successful. There were issues with being able to edit the user later, and the password rehashing, or disappearing altogether.

One discovery I made was that on_model_change actually gets called AFTER the model is populated from the form. There is no way to access the old values of the model without querying the database or monkey patching update_model.

I have come up with a simpler version (I believe) that works in all scenarios.

Here is the whole view:

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)

What I did was add a form field set_password which when populated creates a password hash and updates password on the model.

One and done!

Odelet answered 26/6, 2017 at 13:24 Comment(0)
A
0

my solution was to simply add the column formatters field to the UserView model which returns a dict of all the form fields .then chose to format the password being saved with a bcrypt hash.

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'))

for more details on the arguments the lambdafunction takes ,the official flaskAdmin docs .column_formatters

Aubine answered 18/5, 2021 at 6:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.