Django crispy form tab

Question:

I am now studying Django form. Right now I am focusing on crispy form.

For now crispy and then after I master the form I will move on to Django Admin form and Django admin model form.

Django 1.10
Python 3.6.0

I am following these tutorials:
https://blog.bixly.com/awesome-forms-django-crispy-forms
http://django-crispy-forms.readthedocs.io/en/latest/layouts.html#
https://godjango.com/29-crispy-forms/

Here are my source code:

views.py:

from django.views.generic import FormView
from apps.colors.forms import PersonDetailForm

class ColorStudyView(FormView):
    template_name = 'colors/study.html'
    form_class = PersonDetailForm
    success_url = '/'

forms.py:

from crispy_forms.bootstrap import Tab, TabHolder
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Layout
from django import forms


class NoFormTagCrispyFormMixin(object):
    @property
    def helper(self):
        if not hasattr(self, '_helper'):
            self._helper = FormHelper()
            self._helper.form_tag = False
        return self._helper


class PersonDetailForm(forms.Form):
    name = forms.CharField(max_length=100)
    age = forms.IntegerField(required=False)
    address1 = forms.CharField(max_length=50, required=False)
    address2 = forms.CharField(max_length=50, required=False)
    city = forms.CharField(max_length=50, required=False)
    state = forms.CharField(max_length=50, required=False)

    mobile = forms.CharField(max_length=32, required=False)
    home = forms.CharField(max_length=32, required=False)
    office = forms.CharField(max_length=32, required=False)
    twitter = forms.CharField(max_length=100, required=False)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.form_tag = False
        self.helper.layout = Layout(
            TabHolder(
                Tab('Information',
                    'name',
                    'age'
                    ),
                Tab('Address',
                    'address1',
                    'address2',
                    'city',
                    'state'
                ),
                Tab('Contact',
                    'mobile',
                    'home',
                    'office',
                    'twitter',
                )
            )
        )
        self.helper.layout.append(Submit('submit', 'Submit'))

study.html:

{% load crispy_forms_tags %}


<!DOCTYPE html>
<html lang="en">
<head>
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" />
    <meta charset="UTF-8">
    <title>Study</title>
    <form action="" method="POST">
    {% crispy form %}
    </form>
</head>
<body>

</body>
</html>

Problem:
Tab does not change.
Am I miss something?
Sorry for very basic level question, but this is my first day with Django frontend

Here is my picture. Tab Address and Contact are not work.

enter image description here

Update:
Zollie solves my problem. Here is my study.html

{% load staticfiles %}
{% load crispy_forms_tags %}


<!DOCTYPE html>
<html lang="en">
<head>
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" />
    <meta charset="UTF-8">
    <title>Study</title>
    <form action="" method="POST">
    {% crispy form %}
    </form>
</head>
<body>
<script src="http://code.jquery.com/jquery-1.9.1.js">
</script>
<script src="http://code.jquery.com/ui/1.11.0/jquery-ui.js">
</script>
<script type="text/javascript" src="{% static 'bootstrap-tab.js' %}">  </script>

</body>
</html>

And here is my filesystem configuration

/Users/el/Code/siam-sbrand/static
(siam-sbrand) Sarits-MacBook-Air-2:static el$ ls
admin           django_extensions   img
bootstrap-tab.js    file.txt        js
dist            font            rest_framework
Asked By: joe

||

Answers:

Check these things:

  1. In settings.py you should have CRISPY_TEMPLATE_PACK = 'bootstrap3'
  2. In your static files you should have a bootstrap-tab.js

I had the same problem when first I wanted to use the Tabs in crispy forms. The documentation is quite weak unfortunately for this package.

The answer on why TabHolder and Tabs are not working is, because you have to include jquery and javascript in your html template head. Also, you have to download bootstrap-tab.js and put it in your ‘static’ folder in the bootstrap subfolder ie., and you also have to include the path to it in your html .

So, it is not enough if you just include Bootstrap or bootstrap.css in the html head.
Here is an example how it should look like:

  <head>
<script src="http://code.jquery.com/jquery-1.9.1.js">
</script>
<script src="http://code.jquery.com/ui/1.11.0/jquery-ui.js">
</script>
<script type="text/javascript" src="{% static 'bootstrap/js/bootstrap-tab.js' %}">  </script>

Answered By: Zollie

Although the question is old and solved, I find it might be worthwhile to add some more observations: I came across the problem and found another cause.

First, I checked and confirmed that I had included the .js files as mentioned in the accepted answer. But the tab still did not work.

I found some bootstrap tab examples, such as https://mdbootstrap.com/docs/jquery/components/tabs/. And I concluded that, to make tab work, one should make sure there is a one-to-one relationship between the href attr of the ‘tab header’ and the id attr of the ‘tab body’. Like this:

<ul class="nav nav-tabs" id="myTab" role="tablist">
  <li class="nav-item">
    <a class="nav-link active" id="home-tab" data-toggle="tab" href="#home" role="tab" aria-controls="home"
      aria-selected="true">Home</a>
  </li>
  <li class="nav-item">
    <a class="nav-link" id="profile-tab" data-toggle="tab" href="#profile" role="tab" aria-controls="profile"
      aria-selected="false">Profile</a>
  </li>
  <li class="nav-item">
    <a class="nav-link" id="contact-tab" data-toggle="tab" href="#contact" role="tab" aria-controls="contact"
      aria-selected="false">Contact</a>
  </li>
</ul>
<div class="tab-content" id="myTabContent">
  <div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">A Tab</div>
  <div class="tab-pane fade" id="profile" role="tabpanel" aria-labelledby="profile-tab">B tab</div>
  <div class="tab-pane fade" id="contact" role="tabpanel" aria-labelledby="contact-tab">C tab</div>
</div>

Note: pay attention to the href attrs of ‘a’ elements and the id attrs of ‘div’ elements.

I did some experimentation and concluded that: once related .js and other files are imported and href and id attrs are properly set, the tab would work.

Now the problem became “how to make crispy set them properly”.

I checked following files:
1. /root/.pyenv/versions/3.7.3/lib/python3.7/site-packages/crispy_forms/bootstrap.py

class Tab(Container):
    """
    Tab object. It wraps fields in a div whose default class is "tab-pane" and
    takes a name as first argument. Example::

        Tab('tab_name', 'form_field_1', 'form_field_2', 'form_field_3')
    """
    css_class = 'tab-pane'
    link_template = '%s/layout/tab-link.html'

    def render_link(self, template_pack=TEMPLATE_PACK, **kwargs):
        """
        Render the link for the tab-pane. It must be called after render so css_class is updated
        with active if needed.
        """
        link_template = self.link_template % template_pack
        return render_to_string(link_template, {'link': self})
  1. /root/.pyenv/versions/3.7.3/lib/python3.7/site-packages/crispy_forms/templates/bootstrap3/layout/tab-link.html
<li class="tab-pane{% if 'active' in link.css_class %} active{% endif %}"><a href="#{{ link.css_id }}" data-toggle="tab">{{ link.name|capfirst }}{% if tab.errors %}!{% endif %}</a></li>

I noticed the css_id attr. And I guessed that if one set ‘css_id’ properly, maybe crispy would do the rest. I tried. And it worked like a charm.

Maybe crispy documentation needs some improvement.

Answered By: fishoverflow

If you- like me- search for answer why this solution doesn’t work for you, check if you are using bootstrap 4. To my knowledge tabs from crispy don’t work on 5 at least for now.

Answered By: Adamix1010
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.