How to list objects of the same date? - python

I want to list all items in my template, but I want to list items under the same year. For example,
under the 2021 title, model objects for that year should be listed. Year titles should come dynamically. How can I do it?
views.py
def press_list(request):
press_contents = PressContent.objects.all().order_by('-date')
context = {
'press_contents': press_contents
}
return render(request, "press_list.html", context)
models.py
class PressContent(models.Model):
label = models.CharField(max_length=500)
url = models.URLField(max_length=500)
date = models.DateTimeField(blank=True, null=True)
press_list.html
{% for press in press_contents %}
<div class="card" style="width: 18rem; margin:15px">
<div class="card-header">
{{ press.date.year }}
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">{{ press.label }}</li>
# Other objects from this year should come here.
</ul>
</div>
{% endfor %}
To be clear:
2021
obj 1
obj 2
2020
obj 3
obj 4
...
...

You can work with the {% regroup … by … %} template tag [Django-doc]:
{% regroup press_contents by year as pressitems %}
{% for pressyear in pressitems %}
<div class="card" style="width: 18rem; margin:15px">
<div class="card-header">
{{ pressyear.grouper }}
</div>
<ul class="list-group list-group-flush">
{% for press in pressyear.list %}
<li class="list-group-item">{{ press.label }}</li>
# Other objects from this year should come here.
{% endfor %}
</ul>
</div>
{% endfor %}

If you can afford to convert a queryset into a list of objects, then you can use the built-in template filter regroup (I think, I've never used it).
Another approach would be to write a python generator function and pass it in the context, to be iterated over by the template. This avoids issues with resource consumption when the queryset comprises a large number of objects. Something like
def year_grouper():
qs = PressContent.objects.all().order_by('-date')
last_object_year = 1000000000
for obj in qs:
obj.year_changed = ( obj.date.year != last_object_year )
yield obj
last_object_year = obj.date.year
and
{% for obj in year_grouper %}
{% if obj.year_changed %}
... year header, etc.
{% endif %}
... display obj
{% endfor %}

Related

Django Order QuerySet by Min Value from Child Table

I am trying to create a specific complex query in Django that I am struggling with.
I am using pagination to display a list of cakes on the index page. I want to sort them by different attributes. Below is a screenshot of my index page.
Name (A-Z) & Name (Z-A) are implemented by simply ordering the Cake queryset by 'name' or '-name' which I am getting as POST from the form at the top of the page.
cakes_list = Cake.objects.all().order_by('name')
However I am struggling to order the queryset by minimum price.
Each cake is available in different dimensions which have different prices (dimensions and prices between cakes are different). These are stored in Dimension with a foreign key pointing to the Cake they belong to.
I want to find out the cheapest option for each cake and order my list of cakes that is used in the pagination based on that (both min price asc and desc).
I have also created a method from_price which returns the price of that cake. Which I use in my template to display each cake name together with the minimum price. But I cannot seem to be able to implement that into my sorting.
I appreciate help with how I can create a query or similar that allows me to sort all of my cakes based on the minimum price for each. I am just learning Django, so my current implementations might not be ideal.
vault/models.py:
class Cake(models.Model):
name = models.CharField(max_length=200)
def from_price(self):
temp = self.dimension_set.aggregate(Min('price')).get('price__min')
if not temp:
temp = 0
return temp
class Dimension(models.Model):
cake = models.ForeignKey(Cake, on_delete=models.CASCADE)
dimension = models.CharField(max_length=50)
price = models.DecimalField(max_digits=6, decimal_places=2)
vault/views.py
def index(request):
#from the form on the index page
order_by = request.POST.get('order_by')
if not order_by:
order_by = 'name'
cakes_list = Cake.objects.all().order_by(order_by)
paginator = Paginator(cakes_list, 5)
page_number = request.GET.get('page', 1)
page_obj = paginator.get_page(page_number)
return render(request, 'vault/index.html', {'page_obj': page_obj, 'order_by': order_by})
vault/templates/vault/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{# Form for cake sorting dropdown #}
<form action="{% url 'vault:index' %}" method="post">
{% csrf_token %}
<label for="order_by">Order by:</label>
<select name="order_by" id="order_by">
<option {% if order_by == "name" %} selected="selected" {% endif %} value="name">Name (A-Z)</option>
<option {% if order_by == "-name" %} selected="selected" {% endif %} value="-name">Name (Z-A)</option>
{% comment %}
New options for ordering by price
<option {% if order_by == "" %} selected="selected" {% endif %} value="name">Price from (Low to High)</option>
<option {% if order_by == "" %} selected="selected" {% endif %} value="-name">Price from (High to Low)</option>
{% endcomment %}
</select>
<input type="submit" value="Select">
</form>
{# Code for printing the list of cakes #}
{% if page_obj %}
<ul>
{% for cake in page_obj %}
<li>
<a href="{% url 'vault:detail' cake.id %}">
{{ cake.name }}
{% if cake.from_price %}
- from £{{ cake.from_price|floatformat:'2' }}
{% endif %}
</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>No cakes are available</p>
{% endif %}
{# Code for the pagination navigation elements #}
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
« first
previous
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
last »
{% endif %}
</span>
</div>
</body>
</html>
Try to use annotation before ordering like that:
cakes_list = Cake.objects.annotate(
max_price=Max('dimension__price'),
).order_by('max_price')
If you need a min price value on each Cake record then probably the easies way is to use subquery:
from django.db.models import OuterRef, Subquery
sub_q = Dimension.objects.filter(cake=OuterRef('id')).order_by('price')
qs = Cake.objects.annotate(min_price=Subquery(sub_q.values('prize')[:1]).order_by('min_price')

Query Django Object Lists of ForeignKey

I have a MenuSection model and a Product model. The Product model has MenuSection set as a ForeignKey. Everything works fine, except I am having difficulty figuring out how to query the Product model and listing out a list of objects unique to the ForeignKey, but print the ForeignKey value once at the top of the template.
Example of how I would like the Products printing to the template:
Salads (FK) <= Only printed once
Cobb (product.name)
Ceasar (product.name)
Pizza (FK)
Supreme (product.name)
Veggie (product.name)
...
Tag
#register.inclusion_tag('tags/_starters.html', takes_context=True)
def products(context):
product = Product.objects.all()
return {
'product': product,
'request': context['request'],
}
Tag Template
{% for p in product %}
<div class="menu-wrapper">
<div class="menu-description">
<h1>{{ p.section.name }}</h1> <======= This is the (FK) that I need to print once.
<div class="menu-list">
<h5>{{ p.name }}</h5>
<p class="price">
{% if p.price_b %}
{{ p.price_a }}/
{{ p.price_b }}
{% else %}
{{ p.price_a }}
{% endif %}
</p>
<span class="menu-dot-line"></span>
</div>
<p class="menu-ingredients">{{ p.description }}</p>
</div>
</div>
{% endfor %}
Model
#register_snippet
class Product(ClusterableModel):
section = models.ForeignKey(MenuSection, verbose_name='Menu Section')
name = models.CharField(max_length=255, verbose_name='Menu Item Name')
...
Instead of querying Product in your tag, return
return {
'menu_sections': MenuSection.objects.all()
}
Then, in your template,
{% for menu_section in menu_sections %}
{{ menu_section.name }}
{% for product in menu_section.product_set.all %}
{{ product.name }}
{% endfor %}
{% endfor %}

Django - trouble with separating objects by user and date

So I have these models:
excercises_choices = (('Bench Press', 'Bench press'),('Overhead Press', 'Overhead Press'), ('Squat', 'Squat'),
('Deadlift', 'Deadlift'))
unit_choices = (('kg','kg'), ('lbs', 'lbs'))
class Lifts(models.Model):
user = models.ForeignKey('auth.User', null=True)
excercises = models.CharField(max_length=200, choices=excercises_choices)
sets = models.IntegerField(null=True, blank=True)
reps = models.IntegerField(null=True, blank=True)
weight = models.FloatField()
unit = models.CharField(max_length=3, choices=unit_choices)
created_date = models.ForeignKey('Dates')
amrap_set = models.BooleanField(default=False)
amrap_rep = models.IntegerField(null=True, blank=True)
def __str__(self):
return self.excercises
class Dates(models.Model):
created_date = models.DateField(unique=True)
def __str__(self):
return str(self.created_date)
Let's say I have few lifts at different dates for admin and few lifts at different for xx user.
I want multiple lifts matching one date that's why I've made foreign key. (eg. 3 lifts to 2016-10-10 and 2 lifts to 2016-10-11).
Here is a view for showing it:
#login_required
def entries(request):
date = Dates.objects.all().order_by('-created_date')
lifts_by_user = Lifts.objects.filter(user=request.user)
return render(request, 'lift/entries.html', {'date': date,
'lifts_by_user': lifts_by_user})
And template:
{% extends 'lift/base.html' %}
{% block content %}
{{ user }}
{% if user.is_authenticated %}
{% for date in date %}
<p><strong>{{ date }}</strong>
{% for i in date.lifts_set.all %}
{{ i }}
{% endfor %}
add new lift</p>
{% endfor %}
{% endif %}
<p>
Delete lifts or dates
</p>
{% endblock %}
The problem is that I dont know how to separate it by dates AND by user.
This is how it looks like How do i keep this pattern date - lifts_to_that_date but for separate users? I dont want to see admin's entries while I am on test user
Have a look at the regroup template tag, it does exactly what you need.
You can do something like this in your view:
#login_required
def entries(request):
lifts_by_user = (Lifts.objects.filter(user=request.user)
.order_by('-created_date__created_date'))
return render(
request,
'lift/entries.html',
{'lifts_by_user': lifts_by_user}
)
And replace the for date in dates loop in your template with something like:
{% regroup lifts_by_user by created_date.created_date as lifts %}
<ul>
{% for day in lifts %}
<li>Date: {{ day.grouper }}
<ul>
{% for lift in day.list %}
<li>{{ lift }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
I've used a ul here so that it's easier to compare to the example in the docs, but obviously you can change the markup to whatever you need. It's important to know that regroup doesn't order its input, so you need to order by created_date in your view.
If you're using Django's dev version you can use this instead:
{% regroup lifts_by_user by created_date.created_date as lift_list %}
<ul>
{% for day, lifts in lift_list %}
<li>Date: {{ day }}
<ul>
{% for lift in lifts %}
<li>{{ lift }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Which I think is a little clearer.
As an aside, none of this relies on having dates stored as a foreign key, but that's up to you.
Questions from comments:
order_by('-created_date__created_date') is joining Lifts to Dates through the Lifts.created_date foreign key and ordering by the Dates.created_date field. Have a look at https://docs.djangoproject.com/en/dev/topics/db/queries/#lookups-that-span-relationships for details.
for day, lifts in lift_list is using tuple unpacking.
As a quick example:
t = (1, 2, 3)
# first, second, third will have values 1, 2, 3 respectively
first, second, third = t
{% regroup lifts_by_user by created_date.created_date as lifts_list %} produces a list of namedtuples (again, only in the dev version, if you're using 1.10 or earlier it's a list of dicts so you can't use this trick) so as you're iterating through lift_list you can unpack the date and list of lifts into separate variables.
If you have a Lift instance called lift, you can get the pk for its date by using lift.created_date_id. Accessing it where you have the date URL in your example template is a little trickier because you have to get a lift out of the regrouped date's list. Something like this:
{% regroup lifts_by_user by created_date.created_date as lifts %}
<ul>
{% for day in lifts %}
<li>Date: {{ day.grouper }}
{# day.list.0 gets the first lift for this day #}
Date PK: {{ day.list.0.created_date_id }}
<ul>
{% for lift in day.list %}
<li>{{ lift }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>

Django Template - neat 1-1 translation of values (dictionary lookup)

I've an object containing an attribute defined by a series of choices - here's it's class definition:
class StashURLJobRequest(models.Model):
STATUS_CHOICES = ((0,"Requested"),(1,"Done"),(2,"Failed"))
url = models.URLField()
created = models.DateTimeField(auto_now_add = True, auto_now=False)
processed = models.DateTimeField(auto_now_add = False, auto_now = True)
status = models.IntegerField(choices=(STATUS_CHOICES))
requestBy = models.CharField(max_length=32)
def __unicode__(self):
return smart_unicode(self.url + str(self.created))
def status_display(self):
status_dict = dict(self.STATUS_CHOICES)
return status_dict[self.status]
I get a list of these and pass through into a template, intending to render each one as an individual row, again, here's the template code:
{% for job_instance in jobs %}
<div class="width100pc">
<div class="width10pc"> <img src="{% static 'img/empty.png' %}" /> </div>
<div class="width80pc">
<div class="width70pc textleft sans textsmall "> {{ job_instance.url }} </div>
<div class="width15pc textcentre sans texttiny "> {{ job_instance.processed }} </div>
<div class="width5pc textcentre sans texttiny {% ifequal job_instance.status_display 'Done' %} status_green {% endifequal %}
{% ifequal job_instance.status_display 'Requested' %} status_amber {% endifequal %}
{% ifequal job_instance.status_display 'Failed' %} status_red {% endifequal %}"> {{ job_instance.status_display }} </div>
<div class="width10pc textcentre sans texttiny"> {{ job_instance.requestBy }} </div>
</div>
<div class="width10pc"> <img src="{% static 'img/empty.png' %}" /> </div>
</div>
{% endfor %}
In particular, note the section:
{% ifequal job_instance.status_display 'Done' %} status_green {% endifequal %}
{% ifequal job_instance.status_display 'Requested' %} status_amber {% endifequal %}
{% ifequal job_instance.status_display 'Failed' %} status_red {% endifequal %}
Each of {status_green, status_amber and status_red} refer to a different css class, and allows me to render the status in an appropriate colour.
This seems a little unwieldy, and I don't want to code the css-class into the model, or the view (preferring to leave format and display to the template/html definitions) - so the question is, is there a way to do this that's more "pythonic" - ideally, I'd like to use something like:
{{ class_dict = {'Done' : 'status_green', 'Requested' : 'status_amber', 'Failed' : 'status_red' } }}
< ... some code ... >
{{ class_dict[job_instance.status_display] }}
(dodgy curly-braces aside!)
Add a helper function to the object, so that you don't need any logic in the template?
Add to StashURLJobRequest
def status_display_colour(self):
return {
"Done":"status_green",
"Requested":"status_amber",
"Failed":"status_red"
}[ self.status_display() ]
And the template becomes <div class="width5pc textcentre sans texttiny {{ job_instance.status_display_colour }} ...
Another way would be class="... my-class-{{job_instance.status_display}}" with my-class-Done, my-class-Requested, my-class-Failed appropriately defined in your css. It's inferior because it depends implicitly on status_display remaining a single word with no spaces or other special characters.

Sort python list with comparing integers

Here are my original two (2) functions:
def user_bible(query):
output = requests.get("http://getbible.net/json?passage={0}".format(query))
json_dict_output = json.loads(output.text.strip("();"))
before_for_loop_parse = json_dict_output[u'book'][0][u'chapter'] #[u'2'][u'verse']
keys = before_for_loop_parse.keys()
keys.sort(compare_number_strings)
stored_list = []
for k in keys:
stored_list.append(before_for_loop_parse[k][u'verse'])
return parse_rough_draft(json_dict_output)
and my second small function:
def compare_number_strings(string1, string2):
return cmp(int(string1), int(string2))
Then my tutor helped me edit a new function which allowed for chapter and verse numbers to be presented in the function's output
def parse_rough_draft(json_dict_output):
books = json_dict_output[u'book']
result_dict={}
for i in books:
book_name_variable = i[u'book_name']
current_book_value = result_dict.setdefault(book_name_variable, {})
chapter_variable = i[u'chapter_nr']
chapter_value = current_book_value.setdefault(chapter_variable, {})
all_chapter_verses_variable = i[u'chapter']
for m in all_chapter_verses_variable.keys():
verse = all_chapter_verses_variable[m]
chapter_value[m] = verse[u'verse']
return result_dict
But, my new function parse rough draft(): outputs an unsorted list. I am trying to sort this list and then compare the integers of the list so the output will present verses from say "Jn 2:4-19" in proper numerical order. In this function parse rough draft(): am I defining a dictionary above the for loop, and thus is everything in the for loop incorporated in my result_dict dictionary?
I added:
keys = result_dict.keys()
keys.sort(compare_number_strings)
return keys
But now the output on my jinja2 template page only prints the output ' Book: John '
from the following Bible.html template
</form>
{% with messages = get_flashed_messages() %}
{% with books = messages[0] %}
{% if books %}
<div class="well">
<div class="row" style="margin: 50px;margin-right: 100px; font-size:150%;">
<div class="col-md-12">
<!--<h5>Scripture:</h5> -->
{% for book in books %}
<div class="book">
Book: {{book}}
{% for chapter in books[book]%}
<div class="chapter">
Chapter: {{chapter}}
{% with sorted_keys = books[book][chapter].keys() %}
{% for verse in sorted_keys %}
<div class="verse">
Verse: {{verse}}
<div class="verse-text">{{books[book][chapter][verse]}}</div>
</div>
{% endfor %}
{% endwith %}
</div>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}
{% endwith %}
{% endwith %}
</div>
The function can be seen live here:
http://shielded-brushlands-1568.herokuapp.com/Bible/
and the github repo here:
https://github.com/phillipsk/webapp/blob/master/templates/bible.html

Categories