django: managing data to show on templates - django

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.

Related

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, filter by multiple values

So I have these models:
Bands(models.Model):
mgmt = models.ForeignKey(User)
name = models.Charfield(max_length=200)
Contracts(models.Model):
band = models.ForeignKey(Bands)
start_date= models.DateField()
BookedGig(models.Model):
for = models.ForeignKey(Bands)
under= models.ForeignKey(Contracts)
date = models.DateField()
How would I construct something in my views.py file to capture all the BookedGigs for a user? My goal is just to display through a template, the various gigs under the title of the
contacts/bands.
in views.py I currently have
def Home(request):
user = request.user
bands = Bands.objects.filter(mgmt=user).order_by('name')
#This will give me the bands belonging to a user
contracts = Contracts.filter(band=bands)
#But here bands is not one value but a queryset.
#if I try
contracts = bands.booked_gig_set.all()
I get 'QuerySet' object has no attribute 'booked_gig_set'
templates: I know this is wrong but this is how I'd like to display the lists.
{% for b in bands %}
Band:{{b.name}}
{% for c in contracts %}
Contract Start:{{c.start_date}}
{% for g in gigs %}
{{g.dates}}
{% endfor %}
{% endfor %}
{% endfor %}
Thanks
Contracts.objects.filter(band__in=bands)
You might want to add a prefetch_related statement there to prefetch the gigs though, otherwise your template loop will hit the DB once per contract.
contracts = Contracts.objects.filter(band__in=bands).prefetch_related()

Print key/value object in a template

I'm using the django(v.1.2) generic view "list_detail" to print some objects (Model Article) stored in a db.
In urls.py I added the following line
urlpatterns += patterns('django.views.generic.list_detail',
url(r'^article/(?P<slug>[\-\d\w]+)/$', 'object_detail', {'slug_field': 'title_slug', 'queryset': Article.objects.filter(is_public=True)}, name='article'),
)
In the respective template (article_detail.html) I would like to print the article iterating over all its fields. Actually I wrote:
{% for k,v in object.fields %}
<p>{{k}}:{{v}}<p>
{% endfor %}
but it doesn't work. Any suggestions?
'queryset': Article.objects.filter(is_public=True) sends a list to your template.
but in template you are treating it like an Article object.
'article': Article.objects.filter(is_public=True)[0]
then you can access all items of Article. However, I don't understand what you are trying to do with it. is Article.fields a list or dict?
Let us solve this by an example-
I have a model as-
class item(models.Model):
item_number = models.AutoField(primary_key=True)
item_name = models.CharField(max_length=200)
lifetime_rating = models.DecimalField(max_digits=3,decimal_places=1, default=0)
current_rating = models.DecimalField(max_digits=3,decimal_places=1, default=0)
lifefeedbacknum = models.IntegerField(default=0)
currentfeedbacknum = models.IntegerField(default=0)
def __unicode__(self):
return self.item_name
def retlifetime_rating(self):
return self.lifetime_rating
Note method-retlifetime_rating which returns the value of lifetimerating for an object instance.
Now we wish to display lifetime ratings for all products
in views.py-
def showrating(request):
itemslist= models.item.objects.all()
return render(request, 'showrating.html', 'sitems' : itemslist)
the showrating.html file contains the following code snippet-
{% for element in sitems %}
<tr>
<td>{{ element }}</td>
<td>{{ element.retlifetime_rating }}</td>
</tr>
{% endfor %}
basically if you wish to display different fields of an object, you need to have a corresponding method call to return that field
off-course there are other ways to do it but this is most likely the simplest and easiest to implement

django templatetags not return correct data

I've built a small templatetag that looks towards my DB and makes a calculation based on the most popular trophies logged.
templatetag looks as follows:
#register.inclusion_tag('trophies/trophies.html')
def trophies():
return { 'trophies': Trophies.objects.values("specie").annotate(Count("id")).order_by()}
trophies/trophies.html
{% for obj in trophies %}
<li>{{ obj.specie }}</li>
{% endfor %}
trophy model
class Trophies(models.Model):
user = models.ForeignKey(User)
specie = models.ForeignKey(Specie)
Specie model
class Specie(ImageModel):
species = models.CharField(max_length=50, unique=True, verbose_name='Common Name')
running {{ obj.specie }} returns the id, and running {{ obj.specie.species }} returns nothing.
Why does this happen?
Try this:
#register.inclusion_tag('trophies/trophies.html')
def trophies():
return { 'trophies': Trophies.objects.values("specie", "specie__species").annotate(Count("id")).order_by()}
And in template:
{{ obj.specie__species }}
See related question: Display Django values() on Foreign Key in template as object instead of its id