Sum using Django Template syntax? - django

I have an exercise that consists of sets, I assign sets to this exercise model. I am able to calculate the total volume of a single set. But how do I calculate the total volume for the exercise.
Is it possible to return the sum value of a for loop? When I do it in the view I am only able to return the sum of .all() like this
def homeview(request):
set = ExerciseSetModel.objects.all()
sum = 0
for obj in set:
sum += obj.setvolume()
context = {
'sum': sum, # returns the sum of everything but I want the single exercise
}
return TemplateResponse(request, 'poststemplates/posthome.html', context)
model:
class ExerciseModel(models.Post):
exercisename = models.TextField(max_length=500, verbose_name='text',
blank=True, null=True, default="text")
class ExerciseSetModel(models.Post):
exercise = models.ForeignKey(ExerciseModel, on_delete=models.CASCADE, default=None,)
repsnumber = models.IntegerField(default=1, null=True, blank=True)
weightnumber = models.IntegerField(default=1, null=True, blank=True)
def setvolume(self):
return self.repsnumber * self.weightnumber
views:
#login_required
def homeview(request):
exercises = ExerciseModel.objects.all()
context = {
'exercises': exercises,
}
return TemplateResponse(request, 'poststemplates/posthome.html', context)
template:
{% for exercise in exercises %}
{% for set in exercise.exercisesetmodel_set.all %}
{{set.setvolume}}
{% endfor %}
{% endfor %}
now let's say that I have two sets inside a single exercise, I'm able to calculate the set volume for each set, but how would I calculate the exercise volume for the entire exercise esentially calculating setvolume 1 + setvolume 2 adding up each iteration in the for loop

How about below options? These are way to detour lack of functionality of django template. If you have trouble when process something in template, you can consider doing that in views.
Option 1: Calculating total sum(sum all of sets by exercise) in views and passing {exercise.id:total_sum} dictionary to template
views.py
def homeview(request):
exercises = ExerciseModel.objects.all()
total_sum_by_exercise = {}
for exercise in exercises:
sets = ExerciseSetModel.objects.filter(exercise=exercise).all()
sum_all_sets = 0
for obj in sets:
sum_all_sets += obj.setvolume()
total_sum_by_exercise[exercise.id] = sum_all_sets
context = {
'exercises': exercises, 'total_sum_by_exercise': total_sum_by_exercise
}
return TemplateResponse(request, 'poststemplates/posthome.html', context)
posthome.html
{% for exercise in exercises %}
{% for set in exercise.exercisesetmodel_set.all %}
{{set.setvolume}}
{% endfor %}
{% for key, val in total_sum_by_exercise.items %}
{% if key == exercise.id%}
{{val}}
{% endif %}
{% endfor %}
{% endfor %}
Option 2: Defining method for calculating total sum in model, and call that method in template
models.py
class ExerciseModel(models.Model):
exercisename = models.TextField(max_length=500, verbose_name='text', blank=True, null=True, default="text")
def total_sum(self):
sets = ExerciseSetModel.objects.filter(exercise=self.id).all()
sum_all_sets = 0
for obj in sets:
sum_all_sets += obj.setvolume()
return sum_all_sets
class ExerciseSetModel(models.Model):
exercise = models.ForeignKey(ExerciseModel, on_delete=models.CASCADE, default=None,)
repsnumber = models.IntegerField(default=1, null=True, blank=True)
weightnumber = models.IntegerField(default=1, null=True, blank=True)
def setvolume(self):
return self.repsnumber * self.weightnumber
posthome.html
{% for exercise in exercises %}
{% for set in exercise.exercisesetmodel_set.all %}
{{set.setvolume}}
{% endfor %}
{{exercise.total_sum}}
{% endfor %}

Related

List of child ID in django template

Here is my models.py:
#child
class Country(models.Model):
name = models.CharField(max_length=255)
wine_rg = models.ManyToManyField(WineRegion, blank=True)
#parent
class WorldRegion(models.Model):
name = models.CharField(max_length=255)
country = models.ManyToManyField(Country, blank=True)
def __str__(self):
return self.name
views.py:
world_region_filters = WorldRegion.objects.all()
templates/test.html:
{% for world_region in world_region_filters %}
{{ world_region.name }} - {{ return list of country ID }}
{% endfor %}
How to return all country ID (child) on django template? I know I can do this:
{% for country in world_region.country.all %} {{ country.id }} {% endfor %}
But is there any way to make it shorter? I've tried this:
{{ world_region.country.all.id }}
But it doesn't work. Any suggestions?
i dont know why you want a list of ids in html
whatever you've implemented is fine still if you want list of ids maybe you can do like this:
data = WorldRegion.objects.all().values("name", "country_id")
region_names = [ i['name'] for i in data ]
country_ids = [ i['country_id'] for i in data ]
then you can pass it to html as a context
If you are using PostgresSQL, then you can use ArrayAgg like this:
from django.contrib.postgres.aggregates.general import ArrayAgg
from django.db.models import Count
world_region_filters = WorldRegion.objects.all().annotate(
country_list=ArrayAgg('country', distinct=True),
)

How to make individual product subscription for the same user in django

I have a website that has some services that has to be subscribed individually. The user has to subscribe the services he wants to avail. Now there are three plans for subscription monthly, quaterly and half-yearly. The user has to choose one of them individually for each service he is opting for.
For example, I have three services civil, tax, criminal. Now a user can choose to subscribe Civil for 1 month and Criminal for 6 month hence these will expire too individually. Based on the services he has choosen he will get menus once he logs in. Below is the approach I took to make the models.
models.py
SUB_CHOICES = (
('monthly', 'monthly'),
('quaterly', 'quaterly'),
('hf', 'half_yearly'),
('anually', 'anually'),
)
class ServiceProduct(models.Model):
title = models.CharField(max_length=50, null = True)
code = models.IntegerField(null=True, unique= True)
price = models.DecimalField(max_digits=10,decimal_places=2,null=True)
def __str__(self):
return self.title
class UserSubscription(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
service_choosen = models.ForeignKey(ServiceProduct, on_delete=models.CASCADE, null=True)
is_active = models.BooleanField(default= False)
subscribed_on = models.DateTimeField(null=True)
expiring_on = models.DateTimeField(null=True)
Here i connected the usermembership model to customuser with foreign key to make multiple subscription for the same user having is_active class that shall toggle based on the subscription dates and expiring dates. The ServiceProduct models stores the name, prices etc of the service that would be required during checkout
But now as I said the user will get menus after login based on what services is active in subscription I am unable to do this in the template.
I passed the value from my views as
views.py
subuser = request.user
sub = UserSubscription.objects.filter(user = subuser)
return render(request, 'testpage.html', {'subuser': subuser, 'sub': sub})
But in html i cannot write somthing like
{% for cust in sub %}
{% if cust.service_choosen.civil.is_active %}
do something...
{% endif %}
{% endfor %}
I am sure that I am unable to make the correct models .Therefore please suggest me what will be the right approach to create models that would make this type of subscription method possible.
Desired result:
If the user chooses civil for a month and criminal for 6 months then Civil should be active for him for a month and Criminal should be active for him for 6 months
I believe that is_active would be better as model method. Like this:
def is_active(self):
return timezone.now() <= self.expiring_on
because it will "update" itself automatically if the subsrciption time will pass.
Share more views.py and I will tell you, how to pass context to html.
in views.py I would first filter UserSubscription to select only active subscriptions
subuser = request.user
sub = UserSubscription.objects.filter(user = subuser, is_active = True)
return render(request, 'testpage.html', {'subuser': subuser, 'sub': sub})
In this way you can simplify your template:
{% for cust in sub %}
{% if cust.service_choosen.title == 'civil' %}
do something with Civil...
{% elif cust.service_choosen.title == 'tax' %}
do something with Tax...
{% elif cust.service_choosen.title == 'criminal' %}
do something with Criminal...
{% endif %}
{% endfor %}
or using code instead of title (suppose codes 1=Civil, 2=Tax and so on):
{% for cust in sub %}
{% if cust.service_choosen.code == 1 %}
do something with Civil...
{% elif cust.service_choosen.code == 2 %}
do something with Tax...
{% elif cust.service_choosen.code == 3 %}
do something with Criminal...
{% endif %}
{% endfor %}
Depending on the output you need, you could also try to generalize (example)
{% for cust in sub %}
<a href='#'>Service {{ cust.service_choosen.title }}</a>
{% endfor %}
Also consider that calculating is_active on the fly like suggested by NixonSparrow answer is a best practice

How To: For...Else in a Django template (if/else inside a for loop)

I apologize in advance if this has already been asked, but I couldn't find any answers that answered the problem I'm having:
I need to do something similar to a For...Else loop in a Django template.
I need to show a button on a template base on an if condition:
If the user has already bought this product, then show button 1
If the user has not bought this product, then show button 2
For each product, I have to go through the user's purchases, and then show one button or the other depending on whether they have already bought the product or not.
A simplified (and wrong) version of the code would be like:
{% for product in products %}
//other code here
{% for purchase in purchases %}
{% if purchase.service_id.id == product.id %}
// show button 1
{% else %}
// show button 2
{% endif %}
{% endfor %}
{% endfor %}
However, this code doesn't work, as it shows both buttons as it goes through the for loop.
I can't do a For...Empty because the user may have other purchases (so the for loop wouldn't be empty), but none of them coincide with this product.
Thanks in advance.
EDIT:
Thanks #JashOFHop for the reply! In the end, I found a way around. I'll share it in case anyone else find themselves in this situation:
For clarity, the models concerned in this case are:
class User(AbstractUser):
pass
class Service(models.Model):
user_id = models.ForeignKey("User", on_delete=models.CASCADE, related_name="user_id_services")
name = models.CharField(max_length=64)
status = models.BooleanField(default=True)
description = models.TextField(max_length=300)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="category_name")
price = models.IntegerField()
slots = models.IntegerField(default=1)
amount = models.IntegerField(default=1)
watchedby = models.ManyToManyField(User, blank=True, related_name="watchedby")
class Purchase(models.Model):
user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user_id_purchases")
service_id = models.ForeignKey(Service, on_delete=models.CASCADE, related_name="service_id_purchases")
amountpaid = models.IntegerField()
The view for this template was:
def explore(request):
# get all the active services from the db
services = Service.objects.filter(status=True).order_by('name')
# get the catogories for the filter option
categories = Category.objects.all().order_by('category')
# get the info of the user
userinfo = User.objects.get(username=request.user)
# get the info of the user's purchases
purchases = Purchase.objects.filter(user_id=userinfo)
# render the template
return render(request, "barter/explore.html", {
"services": services,
"categories": categories,
"userinfo": userinfo,
"purchases": purchases
})
And the template, as explained above, rendered all the services and in each is supposed to check whether this user has already bought said service or not.
Solution:
In the view I've added this and passed it also to the template:
# create a list of the IDs of the services purchased by the user to be able to render the buy/bought button correctly
purchases_list = []
for purchase in purchases:
purchases_list.append(purchase.service_id.id)
Then, the template is:
{% for service in services %}
// other code with infomation of the service here
// Important part:
{% if service.id in purchases_list %}
<button>You already bought this service</button>
{% else %}
<button>Buy now</button>
{% endif %}
{% endfor %}
You can create another models, for example Profile
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
cart = models.MenyToManyField(Product, null=True)
so in views for byu button you can write like this
def buy(request, pk): #byu button
product = Product.objects.get(id=pk)
profile = Profile.objects.filter(user.username=request.user)
profile.cart.add(product.id)
return redirect('some html')
another button has the same
def index(request, pk): # for template
product = Product.objects.get(id=pk)
profile = Profile.objects.filter(user.username=request.user)
return response(request, 'some html', context)
so template
{% for i in product%}
{% for products in profile%}
{% for p in products.cart.all %}
{% if i.id in p.id%}
<p>you bought</p>
{% else %}
<a href='byu'>Buy</a>
{% endif %}
{% endfor%}
{% endfor%}
{% endfor%}
but i think there is another the easiest way to solve your problem

django: managing data to show on templates

Is there a way to somehow pre-select data in the queryset before it's gone to templates? There is this 'project' models that connects to 'project_phase' and there are 'project_phase_history' records that keep track of all status changes.
I am building reports on top of this data and show all them projects on one page and want to display only project_phase_history that were submitted for the last few days OR one most recent.
I tried doing this in the view somehow like this:
projects_to_report_on = project.objects.filter(Q(current_phase__phase__id__in=[1,2,3,4]) & Q(role_sponsor__id = sponsor_id))
projects_to_report_on.current_phase.project_phase_history_set = projects_to_report_on.current_phase.project_phase_history.filter(...)
but this doesn't help, really - all data seems to jump into the template.
I ended up passing the date into the template and generating report as something like this:
{% for s in p.current_phase.project_phase_history_set.all %}
{% if s.date_submitted >= status_start_date %}
<tr>
<td>{{ s.date_submitted }}</td>
<td>{{ s.date_end_fact|default_if_none:"-" }}</td>
</tr>
{% endif %}
{% endfor %}
but it don't not have the flexibility I am looking for.
class project(models.Model):
name = models.CharField(max_length=100, null=False, unique=True)
description = models.CharField(max_length=1024,null=True,blank=True)
current_phase = models.ForeignKey('project_phase', null=True, blank=True, related_name="current_phase")
class project_phase(models.Model):
phase = models.ForeignKey('phases')
project = models.ForeignKey('project')
is_finished = models.BooleanField(default=False)
class project_phase_history(models.Model):
project_phase = models.ForeignKey('project_phase')
date_start_plan = models.DateField(null=True, blank=False)
date_start_fact = models.DateField(null=True, blank=True)
Here's your problem:
projects_to_report_on.current_phase.project_phase_history_set = projects_to_report_on.current_phase.project_phase_history.filter(...)
You are setting the Manager of the reverse end of the relationship to a QuerySet. That does not work. Here's an example with blog and entry models:
>>> print(Entry.objects.all())
[<Entry: Today>, <Entry: Hello>]
>>> today = datetime.date.today()
>>> print(Entry.objects.filter(pub_date__lt=today).all())
[<Entry: Hello>]
>>> blog.entry_set = Entry.objects.filter(pub_date__lt=today())
>>> print(blog.entry_set.all())
[<Entry: Today>, <Entry: Hello>]
>>> blog.entry_set = Entry.objects.none()
>>> print(blog.entry_set.all())
[<Entry: Today>, <Entry: Hello>]
Because:
>>> blog.entry_set = Entry.objects.all()
>>> type(blog.entry_set)
<class 'django.db.models.fields.related.RelatedManager'>
This means blog.entry_set is still a Manager and calling all on a Manager will simply return all records. So don't do that. Pass the QuerySet to your template and use that:
history_set = projects_to_report_on.current_phase.project_phase_history_set.filter(...)
# add it to the context as 'history_set'
{% for s in history_set.all %}
Since you need to loop over both projects and the history of that project, I'd pass a dictionary to the template:
from django.utils.datastructures import SortedDict
# ...
project_map = SortedDict()
for project in projects_to_report_on:
project_map[project] = history_query_set_goes_here
# ...
return render(request, template, {'project_map': project_map})
And then in your template:
{% for project, history_set in project_map.items %}
<h2>{{ project }}</h2>
<ol>
{% for history in history_set.all %}
<li>{{ history }}</li>
{% endfor %}
</ol>
{% endfor %}
SortedDict is documented on the Django wiki. It maintains the order of insertion so if you retrieve the projects from the database in the correct order, they will display in the same order.

limit choices in the drop down of subcategory based on related category in Django admin

I have three models as
class Category(models.Model):
name = models.CharField(max_length=128)
class SubCategory(models.Model):
category = models.ForeignKey(Category)
name = models.CharField(max_length = 400)
class Document(models.Model):
category = models.ForeignKey(Category, null=True,blank=True,help_text=_('Required'))
subcategory = models.ForeignKey(SubCategory, null=True, blank=True, help_text =_('Required'))
title = models.CharField(max_length=300)
Now in Admin interface I have category, subcategory and title field.
If a user is trying to select any subcategory then only that
subcategory item should be displayed which are related to Category.
A simple example is Country, state dropdown.
I am trying to get it from Modelform like
class DocumentAdminModelForm(ModelForm):
def __init__(self, *args, **kwargs):
super(DocumentAdminModelForm, self).__init__(*args, **kwargs)
self.fields['sub_category'] = forms.ModelChoiceField(queryset = SubCategory.objects.filter(category__id = self.fields['category'] ))
but it is not working. Should I use some ajax and jquery for this or
is there any other way you can suggest ??
Thanks
It seems that is the best way to realize this relations, one model with parent relation.
Here is my solution which is based on native django fields,templatetags and little custom of admin template.
In example, i generate custom select element with tabbed childs (based on native django).
Example of select field (sorry, example in Russian language):
Realization of Select class(for edit and create):
class mSelect(Widget):
def __init__(self, attrs=None, choices=()):
super(mSelect, self).__init__(attrs)
# choices can be any iterable, but we may need to render this widget
# multiple times. Thus, collapse it into a list so it can be consumed
# more than once.
self.choices = list(choices)
def render(self, name, value, attrs=None, choices=()):
if value is None: value = ''
final_attrs = self.build_attrs(attrs, name=name)
print name
output = [u'<select name=\"%s\" style=\"width:200px;\">' % name]
output.append(u"<option value=\"\"%s>----------</option>")
options = self.render_options(choices, [value])
if options:
output.append(options)
output.append('</select>')
return mark_safe(u'\n'.join(output))
def render_options(self, choices, selected_choices):
def render_option(option_value, option_label):
option_value = force_unicode(option_value)
selected_html = (option_value in selected_choices) and u' selected="selected"' or u''
return u'<option value="%s"%s style=\"padding-left:20px;\"> %s</option>' % (
escape(option_value), selected_html,
conditional_escape(force_unicode(option_label)))
# Normalize to strings.
selected_choices = set([force_unicode(v) for v in selected_choices])
output = []
for option_value, option_label in chain(self.choices, choices):
childs = **YOUR_MODEL**.objects.filter(parent=option_value).order_by("**YOUR_FIELDNAME**")
if len(childs)>0:
output.append("<option value=\"%s\" disabled >%s</option>" % (option_value,option_label))
for child in childs:
output.append(render_option(child.id, child.iname))
return u'\n'.join(output)
Then you need to create modelAdmin class for you model:
Example:
class **YOUMODELADMIN**(admin.ModelAdmin):
.....
.....
.....
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == '**YOUR_FIELD_NAME**':
kwargs["widget"] = mSelect()
field = super(**YOUMODELADMIN**, self).formfield_for_dbfield(db_field, **kwargs)
return field
If you need to show this relations in admin(list_filter) i think the best way is to write templatetag for this field+javascript function to show relation tree.
Example(image+code):
(copy file change_list.html into your template folder like: templates/admin/App/model or yourappname/templates/admin/yourmodelname/ change_list.html
Then add call of your template tag in list filter block:
Example of Javascript block:
<script>
function showTree(name_id)
{
if(document.getElementById("li_" + name_id).style.display=="none")
{
//document.getElementById("div_" + name_id).style.display = "block";
document.getElementById("li_" + name_id).style.display = "block";
}
else
{
//document.getElementById("div_" + name_id).style.display = "none";
document.getElementById("li_" + name_id).style.display = "none";
}
}
</script>
Example code of templatetag(python):
def YOURTEMPLATETAG(request):
root_link = "/admin/YOURAPP/YOURMODEL/"
mvar = "YOURFIELD_ID__id__in"
mlink = root_link + "?"
for item in request.GET:
if item!=mvar:
mlink += "&%s=%s" % (item,request.GET[item])
arr_options = []
dest = HERE YOU GET YOUR ROOT OBJECTS
selected = ""
for item in dest:
show = False
childs = HERE YOU GET YOU CHILD OBJECTS
if len(childs)>0:
str_req = "".join("%d," % child.id for child in childs).rstrip(",")
if u"ptype__id__in" in request.GET:
selected = request.GET["YOURFIELDNAME__id__in"]
if selected in str_req.split(","):
show = True
proot = {"name":item.iname,"link":str_req,"childs":childs,"show":show}
arr_options.append(proot)
if "," not in selected and len(selected)>0:
selected = int(selected)
return render_to_string("PATH_TO_YOUR_TEMPLATETAG_TEMPLATE/templatetags/show_options.html",{"mlink":mlink,"selected":selected,"options":arr_options,"name":u"YOUR FILTER NAME","tst":request})
Example of template for templatetag:
<h3>{{name}}</h3>
<ul>
<!--li class="selected"?q=-->
<li{% ifequal selected '' %} class="selected"{% endifequal %}>Все</li>
{% for item in options %}
<li {% ifequal selected item.link %} class="selected"{% endifequal %} >
{{item.name}}
[show tree]
</li>
<li id="li_{{item.link}}" {% ifequal item.show 1 %}{%else%}style="display:none;"{%endifequal%}>
<ul>
{% for child in item.childs %}
<li {% ifequal selected child.id %} class="selected"{% endifequal %}><div style="margin-left:10px;">{{child.FIELDOFNAME}}</div></li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
And finally block for change_list.html:
....
....
<div id="changelist-filter" style="width:350px;z-index:0;">
{% load YOURTEMPLATETAGFILE %}
{% show_date_cal request "/PATH_TO_YOUR_MODEL_VIEW/" %}
<h2>Custom filters</h2>
{% TEMPLATETAGNAME request %}
<h2>{% trans 'Filter' %}</h2>
{% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %}
...
.....
I think anyway this example will be useful for creating custom controls+admin filters
Sorry if not )))