Reputation: 661
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
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>
{{ 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 %}
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())
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?
In order to connect two objects from different apps, in the model.py, you have to
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
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.
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',
]
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
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}"
python manage.py makemigrations
python manage.py migrate
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),
]
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)
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})
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'),
]
Create directory app2/templates
Create directory app2/templates/app2
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>
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>
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())
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