How to retrieve integer from database objects - django

I'm fairly new to Django and try to get a database for scientific reviews up and running. One of the things I would like to show is how many reviews have been published in each year.
In this example I used the following data:
year: number of reviews published
1999: 1
2019: 4
2022: 5
models.py
class Review(models.Model):
title = models.CharField(max_length=100,null=True)
doi = models.URLField(max_length=200,unique=True,null=True)
author = models.CharField("First author",max_length=100,null=True)
year = models.PositiveIntegerField(null=True)
views.py
from django.shortcuts import render
from .models import Review
from django.db.models import Count
def about(response):
reviews_year_count = Review.objects.values('year').annotate(Count('year'))
context = {'reviews_year_count':reviews_year_count}
return render(response, "main/about.html", context)
about.html
<html>
<body>
{{reviews_year_count|join:", "}}
</body>
</html>
output on localhost
Currently:
{'year': 1999, 'year__count': 1}, {'year': 2019, 'year__count': 4}, {'year': 2022, 'year__count': 5}
Aim:
1, 4, 5
What I can't figure out is how to get rid of the {'year': 1999, 'year__count': } and only have the integer value there. I looked at Django documentation pages. One of the things I tried was add defer() behind reviews_year_count = Review.objects.values('year').annotate(Count('year')) but calling defer() after values() doesn’t make sense according to documentation.
My aim is to use the data as input for a chart using chartsjs.

You can use python to get only list of values:
reviews_year_count = Review.objects.values('year').annotate(Count('year'))
reviews_year_count = [i['year__count'] for i in reviews_year_count]
It is possible to do with django ORM using values too

Related

Django - Query count of each distinct status

I have a model Model that has Model.status field. The status field can be of value draft, active or cancelled.
Is it possible to get a count of all objects based on their status? I would prefer to do that in one query instead of this:
Model.objects.filter(status='draft').count()
Model.objects.filter(status='active').count()
Model.objects.filter(status='cancelled').count()
I think that aggregate could help.
Yes, you can work with:
from django.db.models import Count
Model.objects.values('status').annotate(
count=Count('pk')
).order_by('count')
This will return a QuerSet of dictionaries:
<QuerySet [
{'status': 'active', 'count': 25 },
{'status': 'cancelled', 'count': 14 },
{'status': 'draft', 'count': 13 }
]>
This will however not list statuses for which no Model is present in the database.
Or you can make use of an aggregate with filter=:
from django.db.models import Count, Q
Model.objects.aggregate(
nactive=Count('pk', filter=Q(status='active')),
ncancelled=Count('pk', filter=Q(status='cancelled')),
ndraft=Count('pk', filter=Q(status='draft'))
)
This will return a dictionary:
{
'nactive': 25,
'ncancelled': 25,
'ndraft': 13
}
items for which it can not find a Model will be returned as None.

django humanize timesince do not printing

My django view is passing this inside my template:
context={'material':material_id,'inventory_list':inventory,'now_time':datetime.now()
Inside my material object I have this field that is update each new save:
last_update = models.DateTimeField()
And I'm trying to print an humanized time ago in the screen so I'm doing this:
{{now_time | timesince:material.last_update }}
It's not printing anything, despite separately both variables are appearing correctly:
{{now_time}} {{material.last_update}}
OUTPUT:
May 18, 2018, 11:10 a.m. May 18, 2018, 10:45 a.m.
Here is my complete view:
def material_page(request,pk):
try:
material_id=Material.objects.get(id=pk)
inventory = InventoryMove.objects.filter(material=material_id).order_by("-date")
i=0
for movement in inventory:
print (inventory[i].date)
i+=1
except Material.DoesNotExist:
raise Http404("Material does not exist")
return render(
request,
'material_detail.html',
context={'material':material_id,'inventory_list':inventory,'now_time':datetime.now()}
)

Date value loaded once with Django QuerySet filtered on date.today

Scenario: a queryset filtered by a date value seems to use a date value as generated by the process once and then reused for all subsequent requests. That is, if the app is running for a few days, and a piece of content is published with the publish date equal to the current day, it will not show up as published until the application server is reloaded.
The obvious answer is that the queryset is using date.today() instead of date.today, however that's not the case here:
from datetime import date, timedelta
from django.db import models
class PublishedManager(models.Manager):
def get_query_set(self):
return super(PublishedManager, self).get_query_set().filter(
published_date__lte=date.today, active=True)
Why is date.today not being evaluated properly? Am I missing something blindingly obvious here or is there something else going on?
This is an app running Django 1.4.16 on Python 2.7 with Apache mod_wsgi.
You need to call the method:
date.today()
date.today just returns the built-in method. It doesn't actually call it. Example:
>>> from datetime import date
>>> date.today
<built-in method today of type object at 0x97f780>
>>> date.today()
datetime.date(2014, 11, 4)

Django Date-Based Generic Views: How to Access Variables

I have a series of urls tied to Django's generic date views. In the extra_context parameter, I'd like to pass in a queryset based off the year/ month variables in the URLs, but I'm not sure how to access them. For example, in
url(r'^archive/(?P<year>20[1-2][0-9])/?$', archive_year,
{'queryset': Article.objects.all(),
'date_field': 'publication_date',
'template_name': 'articles/archive-date-list.html',
'extra_context': {'content': 'articles'}},
name='article_archive'),
I'd like to add in the 5 most recent articles where the publication date's year is gte year and lt year + 1. Ideally, the collection would be looked up on each request and not just cached at compile time. Am I better off writing a context processor for this/ extending the view?
You create a wrapper around the generic view:
# myapp/views.py
def my_archive_year(request, year):
# Logic to get the articles here
return archive_year(request,
year=year,
date_field='publication_date',
template_name='articles/archive-date-list.html',
extra_context = {'content': articles}
)
# urls.py
url(r'^archive/(?P<year>20[1-2][0-9])/?$', 'myapp.views.my_archive_year', name='article_archive'),

Flexible pagination in Django

I'd like to implement pagination such that I can allow the user to choose the number of records per page such as 10, 25, 50 etc. How should I go about this? Is there an app I can add onto my project to do this?
Thanks
Django has a Paginator object built into core. It's a rather straightforward API to use. Instantiate a Paginator class with two arguments: the list and the number of entries per "page". I'll paste some sample code at the bottom.
In your case you want to allow the user to choose the per-page count. You could either make the per-page count part of the URL (ie. your/page/10/) or you could make it a query string (ie. your/page/?p=10).
Something like...
# Assuming you're reading the Query String value ?p=
try:
per_page = int(request.REQUEST['p'])
except:
per_page = 25 # default value
paginator = Paginator(objects, per_page)
Here's some sample code from the Django doc page for the Paginator to better see how it works.
>>> from django.core.paginator import Paginator
>>> objects = ['john', 'paul', 'george', 'ringo']
>>> p = Paginator(objects, 2)
>>> p.count
4
>>> p.num_pages
2
>>> p.page_range
[1, 2]
>>> page1 = p.page(1)
>>> page1
<Page 1 of 2>
>>> page1.object_list
['john', 'paul']
google on "django pagination" and make sure to use "covering index" in your SQL for efficient query.
T. Stone's answer covers most of what I was going to say. I just want to add that you can use pagination in Generic Views. In particular, you may find django.views.generic.list_detail.object_list useful.
You can write a small wrapper function that gets the number of objects to display per page from the request object, then calls object_list.
def paginated_object_list(request, page):
my_queryset=MyModel.objects.all()
#Here's T. Stone's code to get the number of items per page
try:
per_page = int(request.REQUEST['p'])
except:
per_page = 25 # default value
return object_list(request, queryset=my_queryset,
paginate_by=per_page, page=page)
Then, the context for your template will contain the variables,
paginator: An instance of django.core.paginator.Paginator.
page_obj: An instance of django.core.paginator.Page.
and you can loop through page_obj to display the objects for that page.
What does need? Well.
You can add custom control for change_list.html, for pagination block for example.
This will be reload list page with get parameter per_page for example with certain value onchange event.
For your adminModel you must override changelist_view method where you must handle get parameter and set this value as list_per_page field value.
def changelist_view(self, request):
if request.GET.get('per_page') and int(
request.GET.get('per_page')) in CHANGELIST_PERPAGE_LIMITS:
self.list_per_page = int(request.GET.get('per_page'))
else:
self.list_per_page = 100
extra_context = {'changelist_perpage_limits': CHANGELIST_PERPAGE_LIMITS,
'list_per_page': self.list_per_page}
return super(mymodelAdmin, self).changelist_view(request, extra_context)
I use extra_context for access to this values into template. Maybe there is more neat approach to access i don't know :-)