KevinW
KevinW

Reputation: 661

Django python for template tag not rendering

First, thank you, I'm grateful for any help provided!!!

I am trying to render two forms in the same template from separate apps as well as display data from two separate classes in the same model. Essentially I want to

  1. Display the name of each the ToolsCategory in the template as

    titles/headers

  2. Display form data from the local form from the Tools app as well as a form from a second app as a sort of 'sub' form.

I thought I had the template set up correctly for #1 above, but despite having data available (stored in the database), ToolCategories are not displaying in the template, specifically in this section (template referenced in full at the end of this question):

                    {% for cat in tool_categories %}
                    <h2>{{ cat.name }}</h2>
                    {% endfor %}

For #2, I have searched all over including the Django documentation, and have not been able to figure out how to reference another apps' form to include either after the templates local form tag is closed or within it. Do I use includes and some combination of url references from within the template?

I have the following:

models.py

from django.db import models

class Tool(models.Model):
    name = models.CharField(max_length=200)
    description = models.CharField(max_length=200, blank=True)
    date_added = models.DateTimeField(auto_now_add=True)
    date_modified = models.DateTimeField(auto_now=True)
    version = models.DecimalField(max_length=6, max_digits=6, decimal_places=2)
    is_archived = models.BooleanField(default=False)

    def __str__(self):
        return str(self.name)


class ToolCategory(models.Model):
    name = models.CharField(max_length=200)
    description = models.CharField(max_length=200, blank=True)

    class Meta:
        verbose_name_plural = "Tool Categories"

    def __str__(self):
        return str(self.name) + str(self.description)

template html document

{% extends '../account/account_base.html' %}
{% load static %}
{% block content %}

    <div class="bg-light">
        <div class="container space-1">
            <div class="mb-9">
            <h2 class="h4">
                <a href="{% url 'tools_list' %}"><br/> Tools</a> {{ tool.name }}
            </h2>
            <h6><a href="{% url 'tools_list' %}"><< BACK to the Tools list</a></h6>
            </div>
                <div class="row justify-content-between align-items-center mb-4">
                    <form action="" method="post" class="form-group">
                        {% csrf_token %}
                        {{ form.non_field_errors }}
                        {% if form.tool.errors %}

                            <ol>
                                {% for error in form.subject.errors %}
                                    <li><strong>{{ error|escape }}</strong></li>
                                {% endfor %}
                            </ol>
                        {% endif %}

                        <p>Now Editing Tool Details for: {{ tool.name }}</p>
                        <div class="js-focus-state input-group mb-2">
                            <div class="input-group-prepend">
                    <span class="input-group-text">
                        <span class="fas fa-puzzle-piece"> Name</span>
                     </span>
                            </div>
                            {{ form.name }}
                        </div>

                        <div class="js-focus-state input-group mb-2">
                            <div class="input-group-prepend">
                        <span class="input-group-text">
                            <span class="fas fa-cogs"> Description</span>
                        </span>
                                {{ form.description }}</div>
                        </div>

                        <div class="js-focus-state input-group mb-2">
                            <div class="input-group-prepend">
                                        <span class="input-group-text">
                                            <span class="fas fa-calculator">Version</span>
                                        </span>
                                {{ form.version }}
                            </div>
                        </div>

                         <div class="js-focus-state input-group mb-2">
                             <div class="input-group mb-3">
                                <div class="input-group-prepend">
                                    <span class="input-group-text">
                                        <span class="fas fa-file">  Archived? </span>
                                    </span>
                                    &nbsp;&nbsp;{{ form.is_archived }}
                                </div>
                            </div>
                         </div>


                            {% for cat in tool_categories %}
                            <h2>{{ cat.name }}</h2>
                            {% endfor %}


                        <button name="submit" type="submit" class="btn btn-primary  transition-3d-hover">Update</button>
                        <a href="{% url 'view_tool' tool.id %}">
                            <button name="button" type="button" class="btn btn-primary  transition-3d-hover">View Only
                            </button>
                        </a>
                        <a href="{% url 'delete_confirm' tool.id %}">
                            <button name="button" type="button" class="btn btn-danger  transition-3d-hover"
                                    onclick="return confirm('Are you sure you want to delete this tool?')">Delete
                            </button>
                        </a>
                        <a href="{% url 'delete' tool.id %}">
                            <button name="button" type="button" class="btn btn-danger  transition-3d-hover">Delete - NO
                                CONFIRMATION
                            </button>
                        </a>
                    </form>
                </div>
            </div>
        </div>

{% endblock content %}

5.24 REVISION

shmakovpn, I couldn't respond totally in the comments, thank you so much for your insight here.

I learned the following, and have some questions: 1. To create a drop-down box, in a form.py, with a model object of another application in your program, you can reference that object using an import statement (in your example, I'm paraphrasing below):

forms.py

from app1.models import ToolCategory

class EditToolForm(forms.Form):
tool_category = forms.ModelChoiceField(queryset=ToolCategory.objcts.all())
  1. Additionally, by importing the model ToolCategory as shown above, in forms.py, this provides access to the class ToolsCategory fields to be able to pull into a form in forms.py. This enables you to pull in class objects (am I saying that correctly?) from other models to display in forms. In #12, why would I not want to show which category the tool is in on the form? Can you clarify the reasoning for the two different forms? Is it simply a different way to display the data?

  2. In order to connect two objects from different apps, in the model.py, you have to

    1. import the reference model object, and
    2. add a one-to-many relationship in the model that is referencing the drop down (in this case the tools_category, as shown in #1 above)
  3. In references to #13, in order to get a list of the objects in the view, you have to iterate over the objects and append the data to the forms variable in the views.py (not the template as I had originally assumed).

in views.py (again paraphrasing)

 def get(self, request): #Can you clarify why "get"? Could you call this GetTools?

                initial={  #This appears to be creating the tuples for the name and data?

        return render(request, f"{__package__}/tpl1.html", {'forms': forms}) #can you clarify this?

Upvotes: 1

Views: 1088

Answers (1)

shmakovpn
shmakovpn

Reputation: 831

My answer will be quite long. Unfortunately, the information you provided is not enough. However, it is obvious that you are mistaken in several places at once. Firstly, your models are not connected in any way. Secondly, the logic is violated in the HTML template, the form fields are displayed, and then the category headings that are unrelated follow. The view file is missing, it’s impossible to understand exactly what you are trying to create in this case. Therefore, I will give two examples at once. I hope they will be useful. See comments in the code.

  1. Create new django project
  2. cd /path/to/your/django/project
  3. source path/to/virtualenv/bin/activate # activate your virtualenv
  4. python manage.py startapp app1 # start first application
  5. python manage.py startapp app2 # start second application
  6. Edit your django project settings.py file, add app1 and app2

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app1',
        'app2',
    ]
    
  7. Now we have two separated applications: app1 and app2. For instance, let's put model ToolCategory to app1. Edit file app1/models.py

    from django.db import models
    
    
    class ToolCategory(models.Model):
        name = models.CharField(max_length=200)
        description = models.CharField(max_length=200, blank=True)
    
        class Meta:
            verbose_name_plural = "Tool Categories"
    
        def __str__(self):
            # return str(self.name) + str(self.description)
            return f"{self.name}, {self.description}"  # this is more pretty, I think
    
  8. Let's put model Tool to app2. But there are no relations between your models. You have to add one to many relation from model ToolCategory to model Tool. Edit file app2/models.py

    from django.db import models
    from app1.models import ToolCategory  # you have to import model ToolCategory
    
    
    class Tool(models.Model):
        name = models.CharField(max_length=200)
        description = models.CharField(max_length=200, blank=True)
        date_added = models.DateTimeField(auto_now_add=True)
        date_modified = models.DateTimeField(auto_now=True)
        version = models.DecimalField(max_length=6, max_digits=6, decimal_places=2)
        is_archived = models.BooleanField(default=False)
        tool_category = models.ForeignKey(to=ToolCategory, on_delete=models.CASCADE, null=False)  # adding OneToMany relation
    
        def __str__(self):
            # return str(self.name)  # you may change this on your flavor
            return f"{self.name}/{self.tool_category.name}"
    
  9. python manage.py makemigrations

  10. python manage.py migrate

  11. Let's app2 will provide the views, templates and paths of url. Edit your project urls.py

    from django.contrib import admin
    from django.urls import path
    from django.urls import include
    
    urlpatterns = [
        path('app2/', include('app2.urls'), ),  # http://your.site:port/app2/... will switches control to app2/urls.py
        path('admin/', admin.site.urls),
    ]
    
  12. Add app2/forms.py You can use forms.ModelForm instead of forms.Form, then the code will be shorter. But I want to show more details.

    from django import forms
    from app1.models import ToolCategory
    
    
    class EditToolForm(forms.Form):
        """
        this form uses fields from both models: app1.models.ToolCategory and app2.models.Tool
        """
        name = forms.CharField(max_length=200)
        description = forms.CharField(max_length=200, required=False)
        version = forms.DecimalField(max_digits=6, decimal_places=2)
        is_archived = forms.BooleanField(initial=False)
        tool_category = forms.ModelChoiceField(queryset=ToolCategory.objects.all())
    
    
    class YourToolForm(forms.Form):
        """
        this form uses fields from model app2.models.Tool only, categories from app1 will be added in app2.views.YourView
        """
        name = forms.CharField(max_length=200)
        description = forms.CharField(max_length=200, required=False)
        version = forms.DecimalField(max_digits=6, decimal_places=2)
        is_archived = forms.BooleanField(initial=False)
    
  13. Edit app2/views.py

    from django.shortcuts import render
    from django.views.generic import View
    from app1.models import ToolCategory
    from .models import Tool
    from .forms import EditToolForm, YourToolForm
    
    
    class MyView(View):
        """
        Tools editing view
        """
        def get(self, request):
            tools = Tool.objects.all()
            forms = []
            for tool in tools:
                form = EditToolForm(
                    initial={
                        'name': tool.name,
                        'description': tool.description,
                        'version': tool.version,
                        'is_archived': tool.is_archived,
                        'tool_category': tool.tool_category,
                    }
                )
                forms.append(form)
            return render(request, f"{__package__}/tpl1.html", {'forms': forms})
    
    
    class YourView(View):
        """
        Tools editing view grouped by ToolCategory
        """
        def get(self, request):
            categories = ToolCategory.objects.all()
            categories_list = []
            for category in categories:
                category_dict = {'name': category.name, 'forms': []}
                tools = category.tool_set.all()
                for tool in tools:
                    category_dict['forms'].append(
                        YourToolForm(
                            initial={
                                'name': tool.name,
                                'description': tool.description,
                                'version': tool.version,
                                'is_archived': tool.is_archived,
                            }
                        )
                    )
                categories_list.append(category_dict)
            return render(request, f"{__package__}/tpl2.html", {'categories': categories_list})
    
  14. Add app2/urls.py

    from django.urls import path
    from .views import MyView, YourView
    
    
    app_name = __package__
    urlpatterns = [
        path('my/', MyView.as_view(), name='my_view'),
        path('your/', YourView.as_view(), name='your_view'),
    ]
    
  15. Create directory app2/templates

  16. Create directory app2/templates/app2

  17. Add app2/templates/app2/tpl1.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Hello</title>
    </head>
    <body>
        {% for form in forms %}
            <form>
                {{ form.as_p }}
            </form>
        {% endfor %}
    </body>
    </html>
    
  18. Add app2/templates/app2/tpl2.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Hello</title>
    </head>
    <body>
        {% for category in categories %}
            <div>
                <h2>{{ category.name }}</h2>
                {% for form in category.forms %}
                    {{ form.as_p }}
                {% endfor %}
            </div>
        {% endfor %}
    </body>
    </html>
    
  19. Add several test to checking that all code works correctly. Edit app2/tests.py

    from django.test import TestCase
    from django.test import Client
    from django.shortcuts import reverse
    from app1.models import ToolCategory
    from .models import Tool
    
    
    class Test1(TestCase):
        client_class = Client
    
        def setUp(self) -> None:
            # create first tool category
            category1 = ToolCategory(name='cat1', description='cat desc1')
            category1.save()
            # create second tool category
            category2 = ToolCategory(name='cat2', description='cat desc2')
            category2.save()
            # create tools
            t1 = Tool(name='t1', description='t1 desc', version='1.1', tool_category=category1)
            t1.save()
            t2 = Tool(name='t2', description='t2 desc', version='1.2', tool_category=category1)
            t2.save()
            t3 = Tool(name='t3', description='t3 desc', version='1.5', tool_category=category2)
            t3.save()
    
        def test_my_view(self):
            response = self.client.get(reverse(f'{__package__}:my_view'))  # makes test request to my_view
            print(response.content.decode())
    
        def test_your_vies(self):
            response = self.client.get(reverse(f'{__package__}:your_view'))  # makes test request to your_view
            print(response.content.decode())
    
  20. python manage.py test app2 Test command output (I have shown html only):

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Hello</title>
    </head>
    <body>
            <form>
                <p><label for="id_name">Name:</label> <input type="text" name="name" value="t1" maxlength="200" required id="id_name"></p>
    <p><label for="id_description">Description:</label> <input type="text" name="description" value="t1 desc" maxlength="200" id="id_description"></p>
    <p><label for="id_version">Version:</label> <input type="number" name="version" value="1.10" step="0.01" required id="id_version"></p>
    <p><label for="id_is_archived">Is archived:</label> <input type="checkbox" name="is_archived" required id="id_is_archived"></p>
    <p><label for="id_tool_category">Tool category:</label> <select name="tool_category" required id="id_tool_category">
      <option value="">---------</option>
      <option value="1" selected>cat1, cat desc1</option>
      <option value="2">cat2, cat desc2</option>
    </select></p>
            </form>
            <form>
                <p><label for="id_name">Name:</label> <input type="text" name="name" value="t2" maxlength="200" required id="id_name"></p>
    <p><label for="id_description">Description:</label> <input type="text" name="description" value="t2 desc" maxlength="200" id="id_description"></p>
    <p><label for="id_version">Version:</label> <input type="number" name="version" value="1.20" step="0.01" required id="id_version"></p>
    <p><label for="id_is_archived">Is archived:</label> <input type="checkbox" name="is_archived" required id="id_is_archived"></p>
    <p><label for="id_tool_category">Tool category:</label> <select name="tool_category" required id="id_tool_category">
      <option value="">---------</option>
      <option value="1" selected>cat1, cat desc1</option>
      <option value="2">cat2, cat desc2</option>
    </select></p>
            </form>
            <form>
                <p><label for="id_name">Name:</label> <input type="text" name="name" value="t3" maxlength="200" required id="id_name"></p>
    <p><label for="id_description">Description:</label> <input type="text" name="description" value="t3 desc" maxlength="200" id="id_description"></p>
    <p><label for="id_version">Version:</label> <input type="number" name="version" value="1.50" step="0.01" required id="id_version"></p>
    <p><label for="id_is_archived">Is archived:</label> <input type="checkbox" name="is_archived" required id="id_is_archived"></p>
    <p><label for="id_tool_category">Tool category:</label> <select name="tool_category" required id="id_tool_category">
      <option value="">---------</option>
      <option value="1">cat1, cat desc1</option>
      <option value="2" selected>cat2, cat desc2</option>
    </select></p>
            </form>
    </body>
    </html>
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Hello</title>
    </head>
    <body>
            <div>
                <h2>cat1</h2>
                    <p><label for="id_name">Name:</label> <input type="text" name="name" value="t1" maxlength="200" required id="id_name"></p>
    <p><label for="id_description">Description:</label> <input type="text" name="description" value="t1 desc" maxlength="200" id="id_description"></p>
    <p><label for="id_version">Version:</label> <input type="number" name="version" value="1.10" step="0.01" required id="id_version"></p>
    <p><label for="id_is_archived">Is archived:</label> <input type="checkbox" name="is_archived" required id="id_is_archived"></p>
                    <p><label for="id_name">Name:</label> <input type="text" name="name" value="t2" maxlength="200" required id="id_name"></p>
    <p><label for="id_description">Description:</label> <input type="text" name="description" value="t2 desc" maxlength="200" id="id_description"></p>
    <p><label for="id_version">Version:</label> <input type="number" name="version" value="1.20" step="0.01" required id="id_version"></p>
    <p><label for="id_is_archived">Is archived:</label> <input type="checkbox" name="is_archived" required id="id_is_archived"></p>
            </div>
            <div>
                <h2>cat2</h2>
                    <p><label for="id_name">Name:</label> <input type="text" name="name" value="t3" maxlength="200" required id="id_name"></p>
    <p><label for="id_description">Description:</label> <input type="text" name="description" value="t3 desc" maxlength="200" id="id_description"></p>
    <p><label for="id_version">Version:</label> <input type="number" name="version" value="1.50" step="0.01" required id="id_version"></p>
    <p><label for="id_is_archived">Is archived:</label> <input type="checkbox" name="is_archived" required id="id_is_archived"></p>
            </div>
    </body>
    </html>
    

Upvotes: 2

Related Questions