Django outputting data in templates after .select_related() - django

I have MySQL DB:
In Django models:
class Record(models.Model):
schema_id = models.IntegerField()
project_id = models.IntegerField()
name = models.CharField(max_length=50)
class Value(models.Model):
record = models.ForeignKey(Record)
key = models.ForeignKey(Key)
value = models.CharField(max_length=255)
class Key(models.Model):
name = models.CharField(max_length=50)
encode = models.BooleanField(default=False, blank=True)
In outputting it should looks like this:
## record.name ##
key.name - value.value
key.name - value.value
...
## record.name ##
key.name - value.value
key.name - value.value
...
I try to do it using the following code, but it's makes too many queries to database.
#in view
records = Record.objects.filter(project_id=1)
#in template
{% for record in records %}
<table>
<tr>
<td class="legend">Record:</td>
<td>{{ record.name }}</td>
</tr>
{% for value in record.value_set.all %}
<tr>
<td class="legend">{{ value.key.name }}:</td>
<td>{{ value.value }}</td>
</tr>
{% endfor %}
</table>
{% endfor %}
Using .select_related() i get all data in 2 query, but how iterate this data in templates to get similar structure?
records = Record.objects.filter(project_id=1)
values = Value.objects.filter(record__in=records).select_related().order_by('record')

Nice job for {% regroup %} tag.

Related

No such column: AGC.id Django

I've been working with an existing datatable, so I create the models using inspectdb. My class doesn't have a primary key, so Django adds an automatic primary key named id when I makemigrations and migrate. Later, when I define the template, views, and URLs to see the class table at my website, there is an error like this one:
no such column: AGC.id
I donĀ“t know how to fix it, I'm new to using Django.
Model:
class Agc(models.Model):
index = models.BigIntegerField(blank=True, null=True)
area = models.TextField(db_column='AREA', blank=True, null=True) # Field name made lowercase.
periodo = models.BigIntegerField(db_column='PERIODO', blank=True, null=True) # Field name made lowercase.
demanda = models.TextField(db_column='DEMANDA', blank=True, null=True) # Field name made lowercase. This field type is a guess.
class Meta:
db_table = 'AGC'
Template:
{% extends "despachowebapp/Base.html" %}
{% load static %}
{% block content %}
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Index</th>
<th scope="col">Area</th>
<th scope="col">Periodo</th>
<th scope="col">Demanda</th>
</tr>
</thead>
<tbody>
{% if AGCs %}
{% for AGC in AGCs %}
<tr>
<th scope='row'>{{ Agc.id }}</th>
<td>{{ Agc.index }}</td>
<td>{{ Agc.index }}</td>
<td>{{ Agc.area }}</td>
<td>{{ Agc.periodo }}</td>
<td>{{ Agc.demanda }}</td>
</tr>
{% endfor %}
{% else %}
<h1>No hay datos </h1>
</tbody>
</table>
{% endblock %}
Views:
def index(request):
AGCs=Agc.objects.all()
contexto={'AGCs': AGCs }
return render(request,'OfertasAGC.html', contexto)
URLs:
urlpatterns =[
path('admin/', admin.site.urls),
path('', include('webapp.urls')),
path('prueba/',views.index),
]
Django models must always have a primary key, you can use AutoField or BigAutoField or use another model field, but you will need to add the primary_key = True attribute.
For example:
class Agc(models.Model):
index = models.BigIntegerField(primary_key=True)
area = models.TextField(db_column='AREA', blank=True, null=True) # Field name made lowercase.
periodo = models.BigIntegerField(db_column='PERIODO', blank=True, null=True) # Field name made lowercase.
demanda = models.TextField(db_column='DEMANDA', blank=True, null=True) # Field name made lowercase. This field type is a guess.
class Meta:
db_table = 'AGC'

Django render many to many attributes after query, display None

I am using slug to query the model, and render result in HTML.
The code is unable to render actual name of region, it just return None
Model
class Region(models.Model):
name = models.CharField(blank=False, unique=True)
def __str__(self):
return self.name
class Theme(models.Model):
name = models.CharField(blank=False, unique=True)
slug = models.SlugField(default="", null=False)
def __str__(self):
return self.name
class ETF(models.Model):
ticker = models.CharField(max_length=6, blank=False, db_index=True, unique=True)
full_name = models.CharField(max_length=200, blank=False)
# many to many
region = models.ManyToManyField(Region)
theme = models.ManyToManyField(Theme)
views.py
def theme_etf(request, slug): # render ETFs with theme filter
filtered_results = ETF.objects.filter(theme__slug=slug)
return render(request, "etf/list_etf.html", {
"ETFs": filtered_results
})
Part of list_etf.html
{% for ETF in ETFs %}
<tr>
<td>{{ ETF.ticker }}</td>
<td>{{ ETF.full_name }}</td>
<td>{{ ETF.region.name }}</td> # What should I use in this line
</tr>
{% endfor %}
The code is unable to render actual name of region, it just return None
Result
Ticker, Name, Region
ARKF, ARK Fintech Innovation ETF, None
ARKK, ARK Innovation ETF, None
KEJI, Global X China Innovation, None
I would like to have this:
Ticker, Name, Region
ARKF, ARK Fintech Innovation ETF, Global
ARKK, ARK Innovation ETF, Global
KEJI, Global X China Innovation, China
I have the information in the database. I have checked it in admin.
Can an ETF have multiple regions as implied by your database design? If it does not I would suggest you use ForeignKey instead.
You are accessing the region field as if it were a ForeignKey.
In your database design you need to iterate over the objects saved in the ManyToManyField using .all.
{% for ETF in ETFs %}
<tr>
<td>{{ ETF.ticker }}</td>
<td>{{ ETF.full_name }}</td>
<td>{% for region in ETF.region.all %}{{ region.name }}{%endfor%}</td>
</tr>
{% endfor %}
Because you have many-to-many relationship, you cannot simply have single values. So, you have to list values.
{% for ETF in ETFs %}
<tr>
<td>{{ ETF.ticker }}</td>
<td>{{ ETF.full_name }}</td>
<td>
<ol>
{% for region in ETF.region %}
<li>{{region.name}}</li>
{% endfor %}
</ol>
</td>
</tr>
{% endfor %}

Display queryset results by month in template

Id like to group the results of my queryset in a table by month with the month name as a header for each table.
I have a model like so:
class Tenant(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
telephone = models.CharField(max_length=30)
email = models.CharField(max_length=30)
contract_end = models.DateField(blank=False)
def __str__(self):
return u'%s %s' % (self.first_name, self.last_name)
View like so:
def expired_contracts(request):
now = datetime.datetime.now()
tenant_queryset = Tenant.objects.all()
expired_list = []
for x in range(0, 12):
date = now + relativedelta(months=x)
expired = tenant_queryset.filter(
contract_end__year=date.year,
contract_end__month=date.month
)
expired_list += expired
context = {"expired_list": expired_list}
return render(request, "expired_template.html", context)
Template:
{% for tenant in expired_list %}
<table>
<tr>
<td>{{ tenant.first_name }}</td>
<td>{{ tenant.telephone }}</td>
<td>{{ tenant.contract_end }}</td>
</tr>
</table>
{% endfor %}
I guess I could create a bunch of empty lists and populate them with a loop and if statements, but that seems a little much.
Any other way I could go about this?
Thanks!
itertools.groupby() is an excellent tool for grouping your list.
First you should order your object by the attribute that you are grouping by
tenant_queryset = Tenant.objects.order_by('contract_end')
Grouping by month can be achieved by using itertools.groupby and formatting the date as the month's name as a string date.strftime('%B')
context = {
"expired_list": itertools.groupby(
expired_list,
lambda t: t.contract_end.strftime('%B')
)
}
You can then loop over the months and the tenants for that month in the template like so
{% for month, tenants in expired_list %}
<h3>{{ month }}</h3>
<table>
{% for tenant in tenants %}
<tr>
<td>{{ tenant.first_name }}</td>
<td>{{ tenant.telephone }}</td>
<td>{{ tenant.contract_end }}</td>
</tr>
{% endfor %}
</table>
{% endfor %}

Django model cross reference in templatre

Ok So my mind is going to mush...
I have 2 models. One is a card location and the other holds card types. I've got a view and template which display's the cards in a specific chassis. What I can't seem to get to work is the foreign key reference. I want to display the CardType.sName in the template.
I'm certain i've just done something stupid ...
Models.py:
class CardLocation(models.Model):
chassis = models.ForeignKey(Chassis)
slot = models.CharField(max_length=20)
slot_sub = models.CharField(max_length=20)
CardType = models.ForeignKey(CardType)
PartNum = models.CharField(max_length=200)
ProdID = models.CharField(max_length=200)
versID = models.CharField(max_length=200)
serialNum = models.CharField(max_length=200)
cleiCode = models.CharField(max_length=200)
lastSeen = models.DateTimeField()
isActive = models.BooleanField(default=0)
def __unicode__(self):
return self.slot
class CardType(models.Model):
sName = models.CharField(max_length=5)
lName = models.CharField(max_length=200)
description = models.CharField(max_length=200)
def __unicode__(self):
return self.sName
views.py
class DetailView(generic.ListView):
model = CardLocation
template_name = 'chassis/detail.html'
context_object_name = 'cardLoc'
def get_queryset(self):
#chassis_id = get_object_or_404(CardLocation, chassis_id__iexact=self.args[0])
chassis_id = self.args[0]
return CardLocation.objects.filter(chassis_id=chassis_id)
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(DetailView, self).get_context_data(**kwargs)
# Add in the
context['chassisQ'] = Chassis.objects.get(id=self.args[0])
#context['CardType'] = CardType.objects.order_by()
return context
detail.html
{% load staticfiles %}
<h2>
<table>
<tr><td>Name:</td><td>{{ chassisQ.name }}<td></tr>
<tr><td>Owner:</td><td>{{ chassisQ.owner }}<td></tr>
<tr><td>ip Adress:</td><td>{{ chassisQ.ipAddr }}<td></tr>
<tr><td>Last updated:</td><td>{{ chassisQ.lastSeen }}<td></tr>
</table>
</h2>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
{% if cardLoc %}
<table border=1>
<tr><td>slot</td><td>Type</td><td>Part Number</td><td>Product ID/</td><td>Version ID</td><td>Serial Num</td><td>CLEI code</td></tr>
{% for card in cardLoc %}
<tr>
<td align="right">{{ card.slot }}</td>
<td>Type {{ card.cardtype__set.all.sName }} </td> <!-- DISPLAY sName HERE -->
<td>{{ card.PartNum }}</td>
<td align="right">{{ card.ProdID }}/</td>
<td align="left">{{ card.versID }}</td>
<td>{{ card.serialNum }}</td>
<td>{{ card.cleiCode }}</td>
</tr>
{% endfor %}
</table>
{% else %}
<p>No cards are available...</p>
{% endif %}
A Card has numerous CardTypes (as it's a ForeignKey relationship), not just one. You have:
<td>Type {{ card.cardtype__set.all.sName }} </td>
You need to loop through all the CardTypes related to the Card:
{% for card in cardLoc %}
...
{% for cardtype in card.cardtype__set.all %}
<td>Type {{ cardtype.sName }}</td>
{% endfor %}
...
{% endfor %}

Passing raw SQL to Django template

I need to display a large amount of data that I don't want paginated because I'm using a jQuery tablesorter, and using Person.objects.all() in a view is very expensive for the database. Takes too long to load, so I'm trying to perform raw SQL in my view.
I tried using Django's generic views, but they were just as slow as the objects.all() method.
Here are my models. Essentially, I want to display all persons while counting how many times they have appeared in, say, var1 or var2.
class Person(models.Model):
name = models.CharField(max_length=64, blank=True, null=True)
last_name = models.CharField(max_length=64,)
slug = models.SlugField()
class Object(models.Model):
title = models.ForeignKey(Title)
number = models.CharField(max_length=20)
var1 = models.ManyToManyField(Person, related_name="var1_apps", blank=True, null=True)
var2 = models.ManyToManyField(Person, related_name="var2_apps", blank=True, null=True)
var3 = models.ManyToManyField(Person, related_name="var3_apps", blank=True, null=True)
# ...
slug = models.SlugField()
from django.db import connection
def test (request):
cursor = connection.cursor()
cursor.execute('SELECT * FROM objects_person')
persons = cursor.fetchall() # fetchall() may not be the right call here?
return render_to_response('test.html', {'persons':persons}, context_instance=RequestContext(request))
Template:
<table class="table tablesorter">
<thead>
<tr>
<th>Name</th>
<th>Var1</th>
<th>Var2</th>
<th>Var3</th>
</tr>
</thead>
<tbody>
{% for person in persons %}
<tr>
<td>{{ person.last_name }}{% if person.name %}, {{ person.name }}{% endif %}</td>
<td>{{ person.var1_apps.count }}</td>
<td>{{ person.var2_apps.count }}</td>
<td>{{ person.var3_apps.count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
What it does it iterate blank lines, but if I just call {{ creator }} it will show the entire SQL table -- which I do not want. I must be doing something wrong with the query, so any help appreciated.
The problem isn't the Person.objects.all(). When you loop through that queryset, you are doing three queries for every item in the queryset to calculate the counts.
The answer is to annotate your queryset with the counts for each field.
# in the view
persons = Person.objects.annotate(num_var1_apps=Count('var1_apps'),
num_var2_apps=Count('var2_apps'),
num_var3_apps=Count('var3_apps'),
)
# in the template
{% for person in persons %}
<tr>
<td>{{ person.last_name }}{% if person.name %}, {{ person.name }}{% endif %}</td>
<td>{{ person.num_var1_apps }}</td>
<td>{{ person.num_var2_apps }}</td>
<td>{{ person.num_var3_apps }}</td>
</tr>
{% end for %}