I am trying to render data from 3 models in single html table. I have tried an error few times but failed. Below is my code. The workstation, printer and laptop have foreign of site ID. I want to list all the workstation, laptop and printer filter by a site.
Below is my code:
Models:
class Site(models.Model):
site_name = models.CharField(max_length=30, blank=True, null=True, verbose_name="Site Name")
site_address = models.CharField(max_length=30, blank=True, null=True, verbose_name="Site Address")
def __str__(self):
return self.site_name
class Workstation(models.Model):
name = models.CharField(max_length=30, blank=True, null=True, verbose_name="Workstation Name")
serial = models.CharField(max_length=30, blank=True, null=True, verbose_name="Serial")
workstation_model = models.CharField(max_length=30, blank=True, null=True, verbose_name="Workstation Model")
sitename = models.ForeignKey(Site, on_delete=models.SET_NULL, blank=True, null=True, verbose_name="Site")
def __str__(self):
return self.name
class Laptop(models.Model):
name = models.CharField(max_length=30, blank=True, null=True, verbose_name="Laptop Name")
serial = models.CharField(max_length=30, blank=True, null=True, verbose_name="Serial")
laptop_model = models.CharField(max_length=30, blank=True, null=True, verbose_name="Laptop Model")
sitename = models.ForeignKey(Site, on_delete=models.SET_NULL, blank=True, null=True, verbose_name="Site")
def __str__(self):
return self.name
class Printer(models.Model):
name = models.CharField(max_length=30, blank=True, null=True, verbose_name="Printer Name")
serial = models.CharField(max_length=30, blank=True, null=True, verbose_name="Serial")
printer_model = models.CharField(max_length=30, blank=True, null=True, verbose_name="Printer Model")
sitename = models.ForeignKey(Site, on_delete=models.SET_NULL, blank=True, null=True, verbose_name="Site")
def __str__(self):
return self.name
URL:
urlpatterns = [
path('', views.SiteView.as_view(), name='site'),
path('report/<sitename>', views.ReportView.as_view(), name='reportview'),
]
Template 1: Site List
<table>
<tr>
<th>Site</th>
<th>Address</th>
</tr>
{% for site in site_list %}
<tr>
<td><a href="{% url 'reportview' site.site_name %}">{{ site.site_name }}</a></td>
<td>{{ site.site_address }}</td>
</tr>
{% endfor %}
</table>
Template 2: Report equipment by site
<table>
<tr>
<th>Device</th>
<th>Serial</th>
</tr>
{% for item in site %}
<tr>
<td>{{ item.name }}</td>
<td>{{ item.serial }}</td>
</tr>
{% endfor %}
</table>
View for template 1: Successful output:
class SiteView(ListView):
model = Site
template_name = "mytestapp/site.html"
View for template 2: Failed
class ReportView(TemplateView):
template_name = "mytestapp/report.html"
def post(self, request):
site = request.POST["site"]
context = super().get_context_data(request)
context["site"] = Workstation.objects.get(sitename=site)
context["site"] = Laptop.objects.get(sitename=site)
context["site"] = Printer.objects.get(sitename=site)
return context
I am trying to list workstation, laptop and printer in single table filter by a site. Sample output: image
You are essentially looking for a union, filter all three models by sitename then union them together. Make sure to change in sitename according to your requirements.
# Here we loop over the three models and add the subqueries to the query list
queries = []
for model in [Printer, Laptop, Workstation]:
queries.append(
model.objects.filter(sitename=1)
.values("name", "serial")
)
# We are unpacking the previously added queries, p stands for printer, same for l and w
p, l, w = queries
print(p)
# Here we are doing union of the three models,i.e
# Printer union Laptop union Workstation
plw = p.union(l, w, all=True)
<QuerySet [{'name': 'Epson', 'serial': '2'}, {'name': 'printer1', 'serial': '23'}, {'name': 'Asus', 'serial': '1'}, {'name': 'Dell', 'serial': '56'}, {'name': 'Tas', 'serial': '1'}, {'name': 'WS2', 'serial': '234'}]>
Django doesn't give much control in union queries so you might want to use raw queries if you need more control. Or you can query all 3 models separately and chain them together using itertools.chain
from itertools import chain
print(list(chain(*queries)))
Views
class ReportView(TemplateView):
template_name = "mytestapp/report.html"
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
site = kwargs["sitename"]
queries = []
for model in [Printer, Laptop, Workstation]:
queries.append(model.objects.filter(sitename=site).values("name", "serial"))
p, l, w = queries
print(p)
plw = p.union(l, w, all=True)
context["site"] = plw
return context
The url should look like this http://127.0.0.1:8000/report/1