Reputation: 101
How to set password_hash using generate_password_hash from the edit page of flask-admin
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()
Upvotes: 4
Views: 5139
Reputation: 352
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
Upvotes: 0
Reputation: 3913
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',
),
)
Upvotes: 8
Reputation: 12717
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!
Upvotes: 2
Reputation: 33
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.
Upvotes: 3
Reputation: 101
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
Upvotes: 6