How to do it...

To render an indented list of categories with checkboxes, we will create and use a new  MultipleChoiceTreeField form field and also create an HTML template for this field. The specific template will be passed to the crispy_forms layout in the form. To do this, perform the following steps:

  1. In the utils app, add a fields.py file (or update it if one already exists) and create a MultipleChoiceTreeField form field that extends ModelMultipleChoiceField, as follows:
# utils/fields.py
# ...other imports... from django import forms

# ...

class MultipleChoiceTreeField(forms.ModelMultipleChoiceField):
widget = forms.CheckboxSelectMultiple

def label_from_instance(self, obj):
return obj
  1. Use the new field with the categories to choose from in a new form for movie creation. Also, in the form layout, pass a custom template to the categories field, as shown in the following:
# movies/forms.py
from django import forms 
from django.utils.translation import ugettext_lazy as _ 
from crispy_forms.helper import FormHelper 
from crispy_forms import layout, bootstrap

from utils.fields import MultipleChoiceTreeField from .models import Movie, Category
class MovieForm(forms.ModelForm):
class Meta:
model = Movie

categories = MultipleChoiceTreeField(
label=_("Categories"),
required=False,
queryset=Category.objects.all())

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.helper = FormHelper()
self.helper.form_action = ""
self.helper.form_method = "POST"
self.helper.layout = layout.Layout(
layout.Field("title"),
layout.Field(
"categories",
template="utils/checkbox_multi_select_tree.html"),
bootstrap.FormActions(
layout.Submit("submit", _("Save")),
)
)
  1. Create a template for a Bootstrap-style checkbox list, as shown in the following:
{# templates/utils/checkbox_multi_select_tree.html #}
{% load crispy_forms_filters %}
{% load l10n %}

<div id="div_{{ field.auto_id }}"
class="form-group{% if wrapper_class %}
{{ wrapper_class }}{% endif %}
{% if form_show_errors and field.errors %}
has-error{% endif %}
{% if field.css_classes %}
{{ field.css_classes }}{% endif %}">
{% if field.label and form_show_labels %}
<label for="{{ field.id_for_label }}"
class="control-label {{ label_class }}
{% if field.field.required %}
requiredField{% endif %}">
{{ field.label|safe }}{% if field.field.required %}
<span class="asteriskField">*</span>{% endif %}
</label>
{% endif %}

<div class="controls {{ field_class }}"{% if flat_attrs %}
{{ flat_attrs|safe }}{% endif %}>
{% include 'bootstrap3/layout/field_errors_block.html' %}

{% for choice_value, choice_instance
in field.field.choices %}
<label class="form-check checkbox{% if inline_class
%}-{{ inline_class }}{% endif %}
level-{{ choice_instance.level }}">
<input type="checkbox" class="form-check-input"
{% if choice_value in field.value
or choice_value|stringformat:'s'
in field.value
or choice_value|stringformat:'s' ==
field.value|stringformat:'s'
%} checked{% endif %}
name="{{ field.html_name }}"
id="id_{{field.html_name}}_{{forloop.counter}}"
value="{{ choice_value|unlocalize }}"
{{ field.field.widget.attrs|flatatt }}>
<span>{{ choice_instance }}</span>
</label>
{% endfor %}

{% include "bootstrap3/layout/help_text.html" %}
</div>
</div>

Template tags in the snippet above have been split across lines for legibility, but in practice template tags must be on a single line, and so cannot be split in this manner.
  1. Create a new view for adding a movie, using the form we just created:
# movies/views.py
# ...other imports...
from django.views.generic import FormView

from .forms import MovieForm

# ...

class MovieAdd(FormView):
template_name = 'movies/add_form.html'
form_class = MovieForm
success_url = '/'
  1. Add the associated template to show the Add Movie form with the {% crispy %} template tag, whose usage you can learn more about in the Creating a form layout with django-crispy-forms recipe in Chapter 3, Forms and Views:
{# templates/movies/add_form.html #}
{% extends "base.html" %}
{% load i18n static crispy_forms_tags %}

{% block stylesheet %}
<link rel="stylesheet" type="text/css"
href="{% static 'site/css/movie_add.css' %}">
{% endblock %}

{% block content %}
<h2>{% trans "Add Movie" %}</h2>
<div id="form_add_movie">
{% crispy form %}
</div>
{% endblock %}

  1. We also need a URL rule pointing to the new view, as follows:
# movies/urls.py
# ...other imports...
from django.urls import path

from .views import MovieAdd

# ...

urlpatterns = [
# ...
path('add/', views.MovieAdd.as_view(),
name="add_movie"),
]
  1. Add rules to your CSS file to indent the labels using the classes generated in the checkbox tree field template, such as .level-0, .level-1, and .level-2, by setting the margin-left parameter. Make sure that you have a reasonable amount of these CSS classes for the expected maximum depth of trees in your context, as follows:
/* static/site/movie_add.css */
.level-0 {
    margin-left: 0;
}
.level-1 {
    margin-left: 20px;
}
.level-2 {
    margin-left: 40px;
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.128.145