bknux
bknux

Reputation: 556

jinja2 form include won't work

I'm making my first steps with flask and also jinja2. I have run through some examples but now i try to integrate flask-security and got stuck a bit.

I try to built a modal with an login-form, so I put up a div like this:

<div class="modal">
<h3>Login</h3>
<form action="{{ url_for_security('login') }}" method="POST" name="login_user_form">
    {{ login_user_form.hidden_tag() }}
    {{ render_field_with_errors(login_user_form.email) }}
    {{ render_field_with_errors(login_user_form.password) }}
    {{ render_field_with_errors(login_user_form.remember) }}
    {{ render_field(login_user_form.next) }}
    {{ render_field(login_user_form.submit) }}
</form>

That is the template code taken from the example provided by flask-security. Now i tried to wrap that in

{% extends layout.html %}
{% block loginModal %} ... {% endblock %}

and tried to invoke that in the layout.html by

{% block loginModal %}{% endblock %}

which now i learned is not the way, as the content is rendered and the layout.html only invoked.

Now i tried to put that code directly in the layout.html once by the include statement and as this didn't work i put it in directly.

I also added

{% from "security/_macros.html" import render_field_with_errors, render_field %}

in the first line, which are macros provided by the flask-security package. but all i get is the error:

UndefinedError: 'login_user_form' is undefined (after a long traceback)

I'm totally stuck here, reading jinja2 docs on the website for about an hour now. How can i get this form to work?

Update (I'm sorry, it is a late update):

My layout.html which implements the base layout for all pages

<!doctype html>
<html>
<head>
  <title>MyExample</title>
  <link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/style.css') }}">
  <link rel=stylesheet type=text/css href="{{ url_for('static', filename='css/gumby.css') }}">
</head>
<body>
  <div class=page>
    <nav id="navbar-main-nav" class="navbar">
      <div class="row">
      <a class="toggle" gumby-trigger="#navbar-main-nav #main-nav" href="#"><i class="icon-menu"></i></a>
      <h1 class="four columns logo"><a href="/">Logo</a></h1>
      <nav class="five columns pull_right">
        <ul id="main-nav">
          <li><a href="/link1/"><span>Link1 </span></a></li>
          <li><a href="/link2/"><span>Link2</span><i class="icon-cog" title="Customize"></i></a></li>
    {% if current_user.is_authenticated() %}
    <li>Hello {{ current_user.name }}</li>
    <li><a href="{{ url_for('security.logout') }}">Logout</a></li>
    {% else %}
    <li><a href="#" class="switch" gumby-trigger="#modal1">Open Modal</a></li>
    <li><a href="{{ url_for('security.register') }}">Register</a></li>
    {% endif %}
    </ul>
  </nav>
    </div>
  </nav>

  <div class="modal" id="modal1">
    <div class="content">
      <a class="close switch" gumby-trigger="|#modal1"><i class="icon-cancel" /></i></a>
        <div class="row">
          <div class="ten columns centered">
            <div class="row">
              <div class="five columns">
                {% block loginModal %}{% endblock %}
              </div>
              <div class="five columns">
                 <!-- register Form -->
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    {% block body %}{% endblock %}
  </div>
  <script src="{{ url_for('static', filename='js/libs/modernizr-2.6.2.min.js') }}"></script>
  <script src="{{ url_for('static', filename='js/libs/jquery-2.0.2.min.js') }}"></script>
  <script gumby-touch="js/libs/" src="{{ url_for('static', filename='js/libs/gumby.min.js') }}"></script>

</body>
</html>

My login.html

{% extends layout.html %}
{% block loginModal %}
{% from "security/_macros.html" import render_field_with_errors, render_field %}
{% include "security/_messages.html" %}

   <h3>Login</h3>
   <form action="{{ url_for_security('login') }}" method="POST" name="login_user_form">
      {{ login_user_form.hidden_tag() }}
      {{ render_field_with_errors(login_user_form.email) }}
      {{ render_field_with_errors(login_user_form.password) }}
      {{ render_field_with_errors(login_user_form.remember) }}
      {{ render_field(login_user_form.next) }}
      {{ render_field(login_user_form.submit) }}
  </form>
{% endblock %}

In an abstract way I want to built something like a widget i can import and use in another template to implement such things only once, like the login form. Later i want to be able to implement lists, that can be placed in a siderbar on every page, without having it implemented on each page by itself. I found Flask Plugable Views but I haven't found a good example to understand the proper usage.

Update 2:

This (updated) implementation renders no form at all, without any error. I think I may have misunderstood the use of blocks.

** Solution **

With help from Mark I found the template context processor very helpful as kind of injection of variables. For some it might be obvious: flask-security provides the Forms as import, therefore my code now looks like this:

from flask.ext.security import LoginForm, RegisterForm
...
@app.context_processor
def inject_userForms():
    return dict(login_user_form=LoginForm(), register_user_form=RegisterForm() )

I added the form directly into my layout.html which works well now. What does not work at all is the inclusion of the block. It works if I use blockception, using the login block within a content block.

Upvotes: 3

Views: 4716

Answers (1)

Mark Hildreth
Mark Hildreth

Reputation: 43071

Your error is:

UndefinedError: 'login_user_form' is undefined

This is coming from the template, which looks like this...

{% extends layout.html %}
{% block loginModal %}
{% from "security/_macros.html" import render_field_with_errors, render_field %}
{% include "security/_messages.html" %}

   <h3>Login</h3>
   <form action="{{ url_for_security('login') }}" method="POST" name="login_user_form">
      {{ login_user_form.hidden_tag() }}
      {{ render_field_with_errors(login_user_form.email) }}
      {{ render_field_with_errors(login_user_form.password) }}
      {{ render_field_with_errors(login_user_form.remember) }}
      {{ render_field(login_user_form.next) }}
      {{ render_field(login_user_form.submit) }}
  </form>
{% endblock %}

Therefore, you are referencing login_user_form, but Jinja2 is complaining that you haven't actually defined login_user_form. You typically define this when you are trying to call the function to render your template..

def my_view():
    # ...
    return render_template('login.html', login_user_form=login_user_form)

If you don't have the 'login_user_form' passed as an argument to render_template you will get the error.

If you are expecting login_user_form to be defined on the template context elsewhere in your application (such as in a template context processor), then ensure that that part of your application is indeed working.

Upvotes: 1

Related Questions