Django render many to many attributes after query, display None - django

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 %}

Related

For loop in template django with prefetch_related

I got an issue when trying to access values from other models by using prefetch_related
My model:
class testimport(models.Model):
id=models.AutoField(primary_key=True)
so_hd=models.CharField( max_length=50, unique=True)
ten_kh=models.CharField( max_length=500)
tien_dong=models.IntegerField(blank=True, null=True)
created_at=models.DateTimeField(auto_now_add=True)
objects=models.Manager()
def get_absolute_url(self):
return "/chi_tiet_hop_dong/%s/" % self.so_hd
class report(models.Model):
id=models.AutoField(primary_key=True)
so_hd=models.ForeignKey(testimport, on_delete=models.DO_NOTHING, to_field="so_hd")
nhan_vien=models.ForeignKey(Callers, on_delete=models.DO_NOTHING, null=True, blank= True, to_field="admin_id")
noi_dung=models.TextField()
My views:
....
get_contract_detail=testimport.objects.filter(so_hd__in=get_user).order_by("id").prefetch_related().values()
contract=get_contract_detail.filter(so_hd=so_hd).all()
return render(request, "caller_template/contract_detail.html", {"contract":contract,"the_next":the_next,"the_prev":the_prev, "so_hd":so_hd,"form":form,"form1":form1})
If I try to print out the content by values, it is ok:
print(get_contract_detail.filter(so_hd=so_hd).values("so_hd","report__noi_dung"))
In my template:
{% for report in contract %}
{% for content in report.so_hd.all%}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{content.noi_dung}}</td>
</tr>
{% endfor %}
{% endfor %}
There is no content in cells. How can I show the content
Please help
The reason this does not work is because of the use of .values(…) [Django-doc]. Furthermore you did not specify a related_name=… parameter [Django-doc], so that means that you access the reports with .report_set.all():
contract = testimport.objects.filter(
so_hd__in=get_user, so_hd=so_hd
).order_by('id').prefetch_related() # no .values()
context = {
'contract': contract,
'the_next': the_next,
'the_prev': the_prev,
'so_hd': so_hd,
'form': form,
'form1':form1
}
return render(request, 'caller_template/contract_detail.html', context)
and in the template render with .report_set.all:
{% for report in contract %}
{% for content in report.report_set.all %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ content.noi_dung }}</td>
</tr>
{% endfor %}
{% endfor %}

Get Display CHOICE in a ForeignKey Django Model

My models:
class Sell(models.Model):
item = models.OneToOneField(Buy, related_name='sell', on_delete=models.CASCADE)
date = models.DateField(default=timezone.now)
code = models.ForeignKey(Order, related_name='order', on_delete=models.CASCADE, null=True, blank=True)
class Order(models.Model):
status_choice = (
("W", "Aguardando aprovação do pagamento"),
("A", "Pagamento aprovado - produto em separação"),
)
code = models.CharField(max_length=100)
status = models.CharField(max_length=1, choices=status_choice, default='W')
My views:
def order(request):
orders = Sell.objects.values('code__code', 'date', 'code__status') \
.annotate(total_paid=Sum('total_paid')). \
filter(buyer__username=request.user.username).order_by('-date')
return render(request, 'orders/orders.html',
{'orders': orders})
My template:
{% for order in orders %}
<tr>
<td>{{ order.code__code }}</td>
<td>{{ order.date }}</td>
<td>{{ order.get_code__status_display }}</td>
</tr>
{% endfor %}
The output I was expecting: "Aguardando aprovação do pagamento" but it's not printing this.
Any help to display the full text instead the only word "W"?
Thank you!
You should use a period/full-stop/. to access objects related by a ForeignKey
{% for order in orders %}
<tr>
<td>{{ order.code.code }}</td>
<td>{{ order.date }}</td>
<td>{{ order.code.get_status_display }}</td>
</tr>
{% endfor %}

Using attribute from a related model django

At my work everyone has to plan until they've reached their planning target. To make this easier I'm building a planning tool to plan the activities. All the activities have standard durations which are saved in the PlanningActivity model. Now I want to show a list of all the planned activities with the standard duration and also sum up the total planned time in a week. How can I use strd_duration in my added Planning's? I've tried so much, but nothing seems to work...
models.py
class PlanningActivity(models.Model):
name = models.CharField(max_length=255)
is_billable = models.BooleanField()
is_auto_realised = models.BooleanField()
strd_duration = models.PositiveIntegerField()
def __str__(self):
return self.name
class Planning(models.Model):
added_by = models.ForeignKey(
User, related_name='planning_activity', on_delete=models.DO_NOTHING
)
activity = models.ForeignKey(
PlanningActivity, on_delete=models.DO_NOTHING
)
date = models.DateField()
note = models.CharField(max_length=255)
def __str__(self):
return str(self.id)
views.py
def home(request):
planning_form = PlanningForm(request.POST)
if request.user.is_authenticated:
planning = Planning.objects.filter(added_by=request.user).order_by('-date')
contracts = UserContract.objects.filter(assigned_user=request.user)
else:
planning = ''
contract = ''
if planning_form.is_valid():
new_planning = planning_form.save(commit=False)
new_planning.added_by = request.user
new_planning.save()
return redirect('/')
else:
planning_form = PlanningForm()
return render(request, 'home.html', {'planning_form': planning_form, 'planning':planning, 'contracts':contracts})
home.html
<table class="table table-striped mt-2">
<thead class="thead-light">
<tr>
<td>Activity</td>
<td>Date</td>
<td>Note</td>
<td>Duration</td>
</tr>
</thead>
<tbody>
{% for plan in planning %}
<tr>
<td>{{ plan.activity }}</td>
<td>{{ plan.date }}</td>
<td>{{ plan.note }}</td>
<td>{{ plan.activity_id }}</td> <== HERE I WANT TO SHOW strd_duration
</tr>
{% endfor %}
</tbody>
</table>
You can access the attribute through the foreign key:
{% for plan in planning %}
<tr>
<td>{{ plan.activity }}</td>
<td>{{ plan.date }}</td>
<td>{{ plan.note }}</td>
<td>{{ plan.activity.strd_duration }}</td>
</tr>
{% endfor %}
Note that in the view, you can optimize the number of queries to the database with a .select_related(…) clause [Django-doc]:
planning = Planning.objects.filter(
added_by=request.user
).select_related('activity').order_by('-date')

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 %}

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 %}