[PYTHON] Django drop-down menu implementation

Implementation image

スクリーンショット 2020-08-17 14.54.31.png

Create something like this that when you click on a letter, the items in the lower layer are displayed. (The above one has a check box, but for the time being, I aim to display the DB data in a hierarchical structure without a check box.)

Implementation 1 (Date)

First, prepare the data to be used. This time, prepare the DB and data so that the parent category → child category → grandchild category as shown in the image. Add the following to models.py.

models.py


class CategoryIDModels(models.Model):
    class Meta:
        db_table = 'CategoryID'

    CategoryID = models.CharField(
        primary_key=True,
        verbose_name='CategoryID',
        blank=False,
        null=False,
        max_length=20,
        default='',
    )

    Category_name = models.CharField(
        verbose_name='Category name',
        blank=False,
        null=False,
        max_length=225,
        default='',
    )
class CategoryID2Models(models.Model):
    class Meta:
        db_table = 'CategoryID2'

    CategoryID = models.CharField(
        primary_key=True,
        verbose_name='CategoryID',
        blank=False,
        null=False,
        max_length=20,
        default='',
    )

    Category_name = models.CharField(
        verbose_name='Category name',
        blank=False,
        null=False,
        max_length=225,
        default='',
    )

    ParentCategoryID = models.ForeignKey(
        CategoryIDModels, 
        to_field='CategoryID',
        verbose_name='Parent category',
        on_delete=models.CASCADE,
        null=True
    )
class CategoryID3Models(models.Model):
    class Meta:
        db_table = 'CategoryID3'

    CategoryID = models.CharField(
        primary_key=True,
        verbose_name='CategoryID',
        blank=False,
        null=False,
        max_length=20,
        default='',
    )

    Category_name = models.CharField(
        verbose_name='Category name',
        blank=False,
        null=False,
        max_length=225,
        default='',
    )

    ParentCategoryID = models.ForeignKey(
        CategoryID2Models, 
        to_field='CategoryID',
        verbose_name='Parent category',
        on_delete=models.CASCADE,
        null=True
    )

CategoryIDmodels (parent)-> CategoryID2models (child)-> CategoryID3models (grandchild) CategoryID2 and 3 set the upper layer "CategoryID" column as Foreign Key.

The data to be input is as follows. スクリーンショット 2020-08-20 10.42.03.png

Also, when inputting data, CategoryID2Models and CategoryID3Models should also set the ParentCategoryID column. See the official documentation and below for ForeignKey.

Build a one-to-many model with Django ForeignKey

Implementation 2 (HTML)

Next, prepare the html side. Create "Dropmenu.html" under the templates folder of the project.

Dropmenu.html


<!DOCTYPE html>

{% load static %}
{% load Drop %}

<html lang="ja">
    <head>
      <meta charset="utf-8">
      <link rel="stylesheet" type="text/CSS" href="{% static 'css/drop.css' %}" />
      <title>{% block title %}DropMenu{% endblock %}</title>
    </head>
    <body>
        {% csrf_token %}
        {% for data in form %}
        <span><p id="click_event" style="display:inline;">{{data.Category_name}}</p></span>
              <ul>
                {% for things in form_child|in_category:data.CategoryID %}
                <li><p id="click_event2" style="display:inline;">{{things.Category_name}}</p>
                    <ul>
                    {% for thing in form_gchild|in_category:things.CategoryID %}
                      <li>{{thing.Category_name}}</li>
                    {% endfor %}
                    </ul>
                </li>
                {% endfor %}
              </ul>
        {% endfor %}
            <script type="text/javascript">
              $(function () {
                //Parent menu processing
                $(document).on('click', '#click_event', function(){
                  $(this).parent().next('ul').slideToggle('fast');
                });
                //Child menu processing
                $(document).on('click', '#click_event2', function(e){
                  $(this).parent().children('ul').slideToggle('fast');
                  e.stopPropagation();
                });
              });
            </script>
    </body>
</html>

Please play with CSS below and design as you like.

drop.css


span {
    display: block;
    margin: 0 0 4px 0;
    padding : 15px;
    line-height: 1;
    color :#fff;
    background: #5200b7;
    cursor :pointer;
}
  
li {
    cursor: pointer;
    border-bottom: 1px solid #5200b7;
    color: #222;
}

A "custom template filter" is created as an important part of this implementation. The third line {% load Drop%} in the HTML file is the process of reading the custom template filter. I won't go into detail about custom template filters, but I've created my own filters that can be used with Django templates. All you have to do is create a "templatetags" folder in your project and create a custom template file in it.

The following is a custom template file.

Project_folder/templatetags/Drop.py


register = template.Library()

@register.filter
def in_category(things, category):    
    return things.filter(ParentCategoryID=category)

As the processing content, DB data is received in the first argument and CategoryID is received in the second argument. It is as simple as filtering the received DB data with ParentCategoryID and returning it.

How to use is the 17th line of the html file

python


{% for things in form_child|in_category:data.CategoryID %}

A custom template filter is applied to the DB data called form_child. The CategoryID of CategoryIDModels is passed as the second argument.

It's a little difficult to understand at this rate, so let's take a look at the data passed to the template in views.py.

Implementation 3 (views)

views.py


@login_required
def Dropmenu_date(request_val):
    ##Template loading
    template = loader.get_template('Dropmenu.html')
    form = CategoryIDModels.objects.all().order_by('CategoryID')
    form_child = CategoryID2Models.objects.all().order_by('CategoryID')
    form_gchild = CategoryID3Models.objects.all().order_by('CategoryID')

    context = {
        'form': form,
        'form_child': form_child,
        'form_gchild': form_gchild,
    }

    return HttpResponse(template.render(context, request_val))

After that, if you link views and template in urls.py, a drop-down menu should be displayed.

Recommended Posts

Django drop-down menu implementation
[Django] Make a pull-down menu
Implementation of login function in Django
Django
Like button implementation in Django + Ajax
Django blog on heroku: login implementation
React → Ajax → Django on Linux implementation memo
Asynchronous processing implementation in Django (Celery, Redis)