Django - trouble with separating objects by user and date - python

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>

Related

How to list objects of the same date?

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

How to iterate through multidimensional list/dict in a template

I try to rebuild this example:
https://blog.roseman.org.uk/2010/01/11/django-patterns-part-2-efficient-reverse-lookups/
I have a model "Product" and a model "Order". Order has a foreignkey to "product". So for 1 Product I have N Orders
In my template I have to display a lot of information so I would like to avoid to do "for order in Product.order_set.all()" in my template
In my template, if I write :
{{ object_list.1.related_items }}
everything is fine and I get what I want
but if I write:
{% for i in object_list %}
{{ object_list.i.related_items }}
{% endfor %}
I don't get a result.
Can somebody tell me how I solve this problem?
My object_list is nearly the same as in the above example:
products = Product.objects.all()
i = 0
qs = Product.objects.all()
obj_dict = dict([(obj.id, obj) for obj in qs])
objects = Order.objects.filter(producttyp__in=qs)
relation_dict = {}
for obj in objects:
relation_dict.setdefault(obj.producttyp_id, []).append(obj)
for id, related_items in relation_dict.items():
obj_dict[id].related_items = related_items
def get(self,request,*args,**kwargs):
context = {'object_list':self.obj_dict}
return render(request,self.template_name,context)
the only change i did is from
obj_dict[id]._related_items to obj_dict[id].related_items because of the not allowed underscore?!
How do I print the list in my template like:
- Product A
- Order 1
- Order 2
- Order 5
- Product B
- Order 3
- Order 6
best regards
That is logical, since here Django interprets i not as the variable, but as the an identifier, so it aims to access object_list.i, or object_list['i'], not object_list.1 for example.
You however do not need i here, you can just access the related_items of the object, like:
{% for object in object_list %}
{{ object.related_items }}
{% endfor %}
If related_items is, as the name suggests, a collection as well, we can iterate over these items as well:
{% for object in object_list %}
{% for subitem in object.related_items %}
{{ subitem }}
{% endfor %}
{% endfor %}
for a dictionary, we can access the .values, like:
{% for object in object_dict.values %}
{{ object.related_items }}
{% endfor %}
EDIT: as for the specific case of the listview. You can use .prefetch_related to fetch all the relations with one extra query:
class MyListView(ListView):
queryset = Product.objects.prefetch_related('order_set')
template = 'my_template.html'
In the template you can then render this like:
<ul>
{% for product in object_list %}
<li>{{ product }}</li>
<ul>
{% for order in product.order_set %}
<li>{{ order }}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>

Django template, loop through items if their id is equal to parent loop name

I'm trying to loop through different Zones and then show the items which are part of this zone
Zone is a model, has a name and a ForeignKey. Planche is a model which has Zone as ForeignKey.
I'm looping through zones to display each zone. In that loop I'm looping all Planches and would like to display only the ones that have Zone as a ForeignKey.
class Zones(models.Model):
name = models.CharField(max_length=30)
genre = models.ForeignKey(ZoneTypes, on_delete=models.CASCADE)
def __str__(self):
return self.name
class Planche(models.Model):
pzone = models.ForeignKey(Zones, on_delete=models.CASCADE)
ref = models.CharField(max_length=5, default="1")
length = models.IntegerField()
width = models.IntegerField()
orientation = models.CharField(max_length=30)
def __str__(self):
return self.ref
Template
<div>
<h1>My list of planches</h1>
</div>
{% for z in zones %}
<div>
<h2>Zone name: {{ z.name }}</h2>
{% for p in planches %}
{% if p.pzone == z.name }
<h1>Ref: {{ p.ref }}</h1>
<p>Length: {{ p.length }} - Width: {{ p.width }}</p>
<p>Orientation: {{ p.orientation }}
{% endif %}
{% endfor %}
</div>
{% endfor %}
{% if p.pzone = z.name %} returns False,
They both return the same string if I just display them {{ p.pzone }} and {{ z.name }} but I guess they aren't the same data type. I tried converting them to strings in a {% with %} statement but I keep failing
I'm assuming you want to display all the planches for each zone. You can use the related_name on the ForeignKey to access to items referencing the current object. You did not set any related name there, so it's the default one: planche_set.
<div>
<h1>My list of planches</h1>
</div>
{% for z in zones %}
<div>
<h2>Zone name: {{ z.name }}</h2>
{% for p in z.planche_set.all %}
<h1>Ref: {{ p.ref }}</h1>
<p>Length: {{ p.length }} - Width: {{ p.width }}</p>
<p>Orientation: {{ p.orientation }}
{% endfor %}
</div>
{% endfor %}
Be aware that method will execute N+1 queries (One to select your zones then one query per zone to retrieve the planches of each zone) unless you add prefetch_related('planche') in the view where your select your zones.
References:
Official documentation about backward relationships
What is `related_name` used for in Django?
If you want to display Planches for every Zones, you can write the second loop like this:
<div>
<h1>My list of planches</h1>
</div>
{% for z in zones %}
<div>
<h2>Zone name: {{ z.name }}</h2>
{% for p in z.planche_set.all %}
<h1>Ref: {{ p.ref }}</h1>
<p>Length: {{ p.length }} - Width: {{ p.width }}</p>
<p>Orientation: {{ p.orientation }}
{% endif %}
{% endfor %}
</div>
{% endfor %}
Here is example from another post: Django foreign key relation in template

Django Filter Objects and Get First Corresponding Values

I have two models.
class House(models.Model):
name= models.Charfield(max_length=100)
city= models.Charfield(max_length=100)
area= models.CharField(max_length=200)
country=models.CharField(max_length=30)
class HouseRooms(models.Model):
room_name=models.Charfield(max_length=200)
house= models.ForeignKey(House, related_name='house_hr')
room_price=models.PositiveIntegerField()
When a user run a keyword search, I want to return the name of each 'House' and the first room_price of the corresponding 'HouseRooms'. See my views below.
def my_house_search(request):
query_string= ''
rms= None
sms=None
if ('q' in request.GET) and request.GET['q'].strip():
query_string = request.GET['q']
entry_query= get_query(query_string, ['city','country',])
rms= House.objects.filter(entry_query).order_by('-pub_date')
sms= HouseRooms.objects.filter(house_id__in=rms)
return render(request, 'search/my_house_search.html',{'rms':rms, 'sms':sms, 'query_string':query_string})
Template:
{% if query_string %}
<p> Results </p>
{% if rms %}
{% for m in rms %}
<p> Name: {{ m.name }} </p>
{% empty %}
<p> No house found </p>
{% endfor %}
{% for sd in sms %}
<p> price: {{ sd.room_price }} for {{sd.room_name}}</p>
{% empty %}
<p> no price found </p>
{% endfor %}
{% endif %}
{% endif %}
With the code I wrote, it will return the name of each house and show all prices to all houses like this:
Coker House
Klopp House
$50/day for small room
$100/day for medium room
$200/day for big room
$200/day for quack room
$400/day for master room
$500/day for big room
I just want it to return the result like this.
Coker House
$50/day for small room
Klopp House
$200/day for quack room
What am I missing? How do I go about this?
You shouldn't query HouseRooms explicitly in the view. Instead, you can use the reverse relationship accessor inside your iteration in the template itself.
{% for m in rms %}
<p> Name: {{ m.name }} </p>
{% with m.house_hr.first as sd %}
{% if sd %}
<p> price: {{ sd.room_price }} for {{sd.room_name}}</p>
{% else %}
<p> no price found </p>
{% endif %}
{% endwith %}
{% empty %}
<p> No house found </p>
{% endfor %}

Django template filters 2 ManytoMany fields

I am writing an application in Django that takes all of the athletes in an event, sorts them into categories (lightweight, heavyweight, etc), and then displays the athletes in each category sorted from the top scoring athlete to the bottom scoring athlete. I can't get my template to sort the athletes into classes; either it displays all of the athletes or none.
Here are the relevant sections of my models and template:
An athlete may be in multiple categories and each category has multiple athletes. Also the categories are ordered by date.
models.py
class Entry(models.Model):
athlete = models.ForeignKey(Athlete, related_name='entries')
event = models.ForeignKey(Event, related_name='entries')
athlete_category = models.ManyToManyField(Category, related_name='entries')
athlete_score = models.CharField(max_length=20, blank=True)
placing = models.SmallIntegerField(blank=True, null=True)
class Category(models.Model):
category_name = models.CharField(max_length=100)
class CategoryOrder(models.Model):
event = models.ForeignKey(Event)
category = models.ForeignKey(Classes)
category_order = models.SmallIntegerField()
event_placings.html
{% for category in categories %}
<p>{{ category.category_name }}</p>
{% for entry in entries %}
{% for athlete_category in entry.athlete_category %}
{% if athlete_category == category %}
<p>{{ entry.athlete.first_name }} {{ entry.athlete.last_name }} - {{ entry.placing }}</p>
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
The template is supposed to list each of the categories and then all of the athletes in that category based on his placing. The output should be:
Men's Lightweight
John Doe - 1
Joe Public - 2
Women's Lightweight
Jane Doe - 1
Eva Braun - 2
etc.
Currently I get:
Men's Lightweight
Women's Lightweight
It lists the categories, but not the athletes. Where am I going wrong?
Looks like you have unnecessary loop in your template. It could be just like this:
{% for category in categories %}
<p>{{ category.category_name }}</p>
{% for entry in category.entries %}
<p>{{ entry.athlete.first_name }} {{ entry.athlete.last_name }} - {{ entry.placing }}</p>
{% endfor %}
{% endfor %}
To maintane ordering of athletes you can use Meta ordering on your Entry class or make method def athletes_ordered(self) on your Category class with something like
def athletes_ordered(self):
return self.entries.order_by('athlete_score')
In the second case you'll have to replace category.entries by category.athletes_ordered in the template.

Categories