I am using generic MethodView
s in my Flask application together with Flask-MongoEngine. I am also trying to automate form creation based on the given model by using the model_form(db_model)
function. Lastly, I am trying to let Bootstrap-Flask take care of rendering the produced form. The problem is that there is no submit button, because the form returned by model_form
does not include a SubmitField.
Is there a way to append a submit field before the form is rendered?
Here is a minimally viable app
from datetime import datetime
from flask import Flask, Blueprint, render_template, url_for
from flask_mongoengine import MongoEngine
from flask_bootstrap import Bootstrap5
db = MongoEngine()
bootstrap = Bootstrap5()
class Role(db.Document):
meta = { 'collection': 'roles' }
name = db.StringField(verbose_name='Role Name', max_length=24, required=True, unique=True)
description = db.StringField(verbose_name='Role Description', max_length=64, default=None)
date_created = db.DateTimeField(default=datetime.utcnow())
date_modified = db.DateTimeField(default=datetime.utcnow())
def get_id(self):
return str(self.id)
def safe_delete(self):
if self.name == 'admin':
return False, 'Admin role is not removable.'
result, resp = self.delete()
if not result:
return result, resp
return True, f'Privilege {self.name} removed successfully'
class AuthItemView(MethodView):
def __init__(self, model, template):
self.model = model
self.template = template
def _get_item(self, id):
return self.model.objects.get_or_404(id=id)
def get(self, id):
item = self._get_item(id)
return render_template(self.template, item=item)
class AuthItemEditor(AuthItemView):
def _get_form(self):
form = model_form(self.model, exclude=['date_created', 'date_modified'])
return form()
def get(self, id):
item = self._get_item(id)
form = self._get_form()
return render_template(self.template, item=item, form=form)
def post(self, id):
return 'Yay! Role changes were posted!', 200
staff_bp = Blueprint('staff', __name__)
staff_bp,add_url_rule('/roles/<id>', view_func=AuthItemView.as_view('role_info', Role, 'roleinfo.html')
staff_bp.add_url_rule('/roles/<id>/edit', view_func=AuthItemEditor.as_view('role_edit', Role, 'roleedit.html')
def create_app():
app = Flask(__name__)
app.config['MONGODB_SETTINGS'] = {
'db': 'someapp',
'host': 'localhost',
'port': 27017
}
app.config['SECRET_KEY'] = 'some-very-secret-key'
app.register_blueprint(staff_bp)
db.init_app(app)
bootstrap.init_app(app)
return app
if __name__ == __main__:
a = create_app()
a.run()
The roleedit
template is where the magic has todoesn't happen:
{% extends 'base.html' %}
{% from 'bootstrap5/form.html' import render_form %}
{% block content %}
<h1>Edit Properties of <em>{{ item.name }}</em></h1>
<p>
{{ render_form(form) }}
<!-- There is no submit button!!! How do I submit??? //-->
</p>
{% endblock %}
Here is a very basic base template:
<!doctype html>
<html lang="en">
<head>
{% block head %}
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{% block styles %}
<!-- Bootstrap CSS -->
{{ bootstrap.load_css() }}
{% endblock %}
<title>Page title</title>
{% endblock %}
</head>
<body>
<!-- Your page content -->
{% block content %}{% endblock %}
{% block scripts %}
<!-- Optional JavaScript -->
{{ bootstrap.load_js() }}
{% endblock %}
</body>
</html>
Lastly, the roleinfo
template (it's there just to be able to run the app):
{% extends 'base.html' %}
{% block content %}
<h1>Properties of <em>{{ item.name }}</em></h1>
<!-- display item properties as a table or as responsive bootstrap grid //-->
{% endblock %}
CodePudding user response:
Turns out, it is rather easy...
# ... other imports ...
from wtforms import SubmitField
# ... inside the get_form function
form = model_form(self.model)
setattr(form, 'submit', SubmitField('Submit')
# now return the fixed form
If anyone has a different approach, please share!
CodePudding user response:
You can pass a Form class to model_form
. For example:
class BaseForm(Form):
submit = SubmitField('Submit')
class AuthItemEditor(AuthItemView):
def _get_form(self):
form = model_form(self.model, base_class=BaseForm, exclude=['date_created', 'date_modified'])
return form()
# ...