Django, how to iterate over one to many fields of different lengths - python

I pretty new to Django and I am trying to create an app to display tennis match results. So far, beyond my belief I have managed to hack together some code that is working.
However, I now have a problem though as currently I want to list the matches and their scores together in a template, but the number of sets can be different for each match and when I iterate through them I am getting an index error. Some matches may have 2 sets, others 3,4 or 5... perhaps some even just 1 if a player retires.
I have models for the match and each set, something like this. (I could get the match date, match, result and Set1 etc. for the player to display as those lists all have the same number of values. However, the length of list for set3 as an example is much shorter and causes an error.):
models.py
class Match(models.Model):
match_ID = models.AutoField(primary_key=True)
match_date = models.DateField()
players = models.ManyToManyField(Team, through='MatchStats', related_name='pim')
hometeam = models.ForeignKey(Team, to_field='teamname', related_name='hometeam')
awayteam = models.ForeignKey(Team, to_field='teamname', related_name='awayteam')
hometeam_sets = models.IntegerField()
awayteam_sets = models.IntegerField()
class Set(models.Model):
set_ID = models.AutoField(primary_key=True)
match = models.ForeignKey(Match)
set_number = models.IntegerField()
hometeam_games = models.IntegerField(default=0)
awayteam_games = models.IntegerField(default=0)
views.py
def playermatches(request, player_ID):
context = RequestContext(request)
p = get_object_or_404(Player, pk=player_ID)
match_list = Match.objects.filter(players=player_ID).order_by('-match_date')[:100]
i = len(match_list)
j = 0
#This works as all test matches have a 1st set!
s1_list = Set.objects.filter(match=match_list, set_number=1).order_by(-match__match_date')[:100]
"""
I am totally out of ideas as to what I might do next though.
Tried various things like 'if exists', 'try except IndexError etc'.
Below was the last thing I tried which failed yet again.
"""
s3_list = []
while j < i:
s3 = Set.objects.filter(match=match_list, set_number=3)[j]
if s3:
s3_list.append(s2)
else:
s3 = Set.objects.filter(set_ID=1)
s3_list.append(s3)
lst1 = match_list
lst2 = result_list
lst3 = s1_list
...
lst5 = s3_list
mandr = zip(lst1, lst2, lst3,... lst5)
context_dict = {...}
return render_to_response('stats/players.html', context_dict, context)
template.html
{% if matches %}
<ul>
{% for match, team, s1, s2 in mandr %}
<li>{{ match.match_date }} {{ match.hometeam }} vs. {{ match.awayteam }} ( {{ team.result }} )</li>
<li>{{ match.hometeam_sets }}:{{ match.awayteam_sets }} -- {{ s1.hometeam_games }}-{{ s1.awayteam_games }} {{ s3.hometeam_games }}-{{ s3.awayteam_games }}</li>
{% endfor %}
</ul>
{% else %}
<br/>
<strong>There are currently no matches in the system - try again tomorrow!</strong>
{% endif %}

Didn't need anything too fancy in the end. Realized that I need to lookup the related sets via the foreign key for each match... and then simply iterate over those in the template.
{% for match in mandr %}
{% for set in match.sets.all %}
{{ set.hometeam_games }}:{{ set.awayteam_games }}
{% endfor %}
{% endfor %}

Don't mix match list, result list with remaining lists. Pass them as separate objects in views and iterate over them in templates.
Change your views & template as follows.
views.py
m_list = match_list
r_list = result_list
list1 = s1_list
list2 = s2_list
..............
list5 = s3_list
mandr = zip(m_list, r_list)
my_lists = zip(list1, list2, ..., lst5)
templates.py
{% if matches %}
<ul>
{% for match, team in mandr %}
<li>{{ match.match_date }} <a href="/stats/match/{{ match.match_ID }}/">
{{ match.hometeam }} vs. {{ match.awayteam }}</a> ( {{ team.result }} )</li>
<li>{{ match.hometeam_sets }}:{{ match.awayteam_sets }} --
{% for lst in my_lists %}
<li>{{ lst.hometeam_games }}</li>
{% endfor %}
{% endfor %}
</ul>
{% else %}
<br/>
<strong>There are currently no matches in the system - try again tomorrow!</strong>
{% endif %}

Related

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>

Values_list with flat=True still showing brackets

so I have this html code:
<legend><h1 > Weapons that this character has </h1></legend>
<div>
<ul style="list-style-type:none" >
{% if request.GET.idChar %}
<li >{{ charname }} Has Maihand {{ mh }} and Offhand {{ oh }}</li>
{% else %}
<li>no</li>
{% endif %}
</ul>
</div>
The issue is that {{ charname }}, {{ mh }} and {{ oh }} are still displaying the brackets after i added flat=True in their line in views.py, in fact they stopped showing the parenthesys, but still showing the brackets. I also tried named=True, named=bool, flat=bool. Also tried values instead of values_list...
Views.py:
def character_weapons (request):
idchar = request.GET.get('idChar')
weapons = Characterweapons.objects.filter(characterid__exact=idchar).values_list('weaponid','categoryid_id')
charname = Characters.objects.filter(characterid__exact = idchar).values_list('name', flat=True)
print("charname: ")
print(charname)
if weapons[0][1] == 1:
mh_id = weapons[0][0]
oh_id = weapons[1][0]
elif weapons[0][1] == 2:
oh_id = weapons[0][0]
mh_id = weapons[1][0]
mh = Weapons.objects.filter(weaponid__exact=mh_id).values_list('weaponname', flat=True)
oh = Weapons.objects.filter(weaponid__exact=oh_id).values_list('weaponname', flat=True)
context={"mh": mh, "oh": oh,"idchar": idchar,"charname": charname}
return render(request,'users/character_weapons.html', context)
If someone knows how to fix it please let me know.
Thanks in advance!
values_list() with flat=True returns a list of values, so you have to extract the value with an index of 0 since your queries appear to always return only one row:
context={"mh": mh[0], "oh": oh[0],"idchar": idchar,"charname": charname[0]}
It's a flat list, but it's still a list. If you want to output it in one go, use the |join filter.
{{ mh|join:"," }}

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>

Haystack search on a many to many field is not working

I'm trying to run a search on a model that has a many to many field, and I want to filter the search using this field.
here is my current code:
search_indexes.py
class ListingInex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
business_name = indexes.CharField(model_attr='business_name')
category = indexes.MultiValueField(indexed=True, stored=True)
city = indexes.CharField(model_attr='city')
neighborhood= indexes.CharField(model_attr='neighborhood')
state = indexes.CharField(model_attr='state')
address = indexes.CharField(model_attr='address')
zip_code = indexes.CharField(model_attr='zip_code')
phone_number = indexes.CharField(model_attr='phone_number')
def get_model(self):
return listing
def index_queryset(self, using=None):
return self.get_model().objects.all()
def prepare_category(self, obj):
return [category.name for category in obj.category_set.all()]
listing_text.txt
{{ object.business_name }}
{{ object.state }}
{{object.city}}
{{object.zip_code}}
{{object.phone_number}}
{{object.neighborhood}}
{% for category in obj.category.all %}
{{ category.name }}
{% endfor %}
I'm trying to do a simple search like:
search_results = SearchQuerySet().filter(category=query_text).filter
it returns an empty list. Thanks in advance.
Reading your code I think the problem is here:
{% for category in obj.category.all %}
{{ category.name }}
{% endfor %}
Category is not an object, is a MultiValueField. Maybe if you try something like:
{% for category in object.category.all %}
{{ category.name }}
{% endfor %}
object.category.all instead of obj.category.all. I'm not sure the solution is this, but I'm pretty sure the error could be in this 3 lines.

Decode values of a tuple in django template

if(len(f1) > 0):
for qs in profile_map:
p = Profile.objects.get(pk=qs.emp.id)
t_name = p.first_name + p.last_name
t_arr.append((q.profile.id,emp_name))
response_dictionary.update({'tarr':t_arr})
render_to_response('project/profile_table.html',context_instance=RequestContext(request,{'response_dictionary': response_dictionary}))
In Django template How to deocode all the 1.values of the tuple 2.search the tuple for a certain value in q.profile.id
{% for ele in response_dictionary.tarr%}
alert('{{ele}}');
//Get this as alert (11L, u'Employee3.')
{% endfor %}
In your case, the generator will assign the tuple to ele, so you can access the first, last name with {{ ele.0 }} {{ ele.1 }}.
But this is also legal, to unpack the tuple into two vars:
{% for first_name, last_name in response_dictionary.tarr %}
if you are using django 0.96 you can't have multiple values in for loop. so this will not work:
{% for first_name, last_name in response_dictionary.tarr %}
instead use
{% for ele in response_dictionary.tarr %}
{{ ele.0 }} {{ ele.1 }}
{% endfor %}

Categories