Home > Software design >  flask-mongoengine model_form is missing a submit field - how do I append one to the form?
flask-mongoengine model_form is missing a submit field - how do I append one to the form?

Time:12-08

I am using generic MethodViews 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()

     # ...
  • Related