I have these models in my django app:
class GeoBonus(models.Model):
name = models.CharField(max_length=250)
country = models.ForeignKey(Country, related_name='geo_bonuses')
bookmaker = models.ForeignKey(Bookmaker, related_name='geo_bonuses')
Attributes in Country and Bookmaker are not that important, lets say that each has name parameter.
In template I am having a for cycle:
{% for bookmaker in bookmakers %}
{{bookmaker.name}}
{% endfor %}
and I want to print name of GeoBonus based on country. Lets say user's country is a string.
{% for bookmaker in bookmakers %}
{{bookmaker.name}}
{% if country_code %}
{% for geo_bonus in bookmaker.geo_bonuses|in_country:country_code %}
{{geo_bonus}}
{% endfor %}
{% endif %}
{% endfor %}
This is my filter:
#register.filter
def in_country(qs, country_code):
return qs.filter(country__twocode=country_code)
Where country_code is send through view:
context = RequestContext(request,{
'bookmakers': Bookmaker.objects.select_related('geo_bonuses').all(),
'country_code': country_code,
})
But this does not work. What am I doing wrong? I am getting Invalid filter: 'in_country' (if I use simple filter as upper for string, it works, so template tags are loaded).
So basicly my question is, How to select one foreign key while running a for cycle?
This question has nothing at all to do with foreign keys or querysets. The error tells you exactly what is going wrong: it is not recognising the "in_country" filter.
Your assertion that "template tags are loaded" because upper works does not follow at all: upper is a built-in filter provided by Django itself, whereas in_country is not. You need to load your template tag library with {% load module_that_defines_filter %} before you can use it.
Related
I know how to return all objects by a foreign key in a Django template. But how to limit their number to the last 3? Is it possible?
Example
Models.html
class Person(models.Model):
...
class Tag(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
name = models.CharField(max_length=10)
Template.html
{% for tag in person.tag_set.all %}
...
{% endfor %}
What is expected (only the last 3 objects)?
{% for tag in person.tag_set.all[:3] %}
...
{% endfor %}
Use the slice template filter to slice a queryset/list, this gets the first 3 items
{% for tag in person.tag_set.all|slice:":3" %}
Negative indexing of querysets is not supported, getting the last three would be complex in a template
Not sure if it possible to use in template, but you can prepare your queryset using reverse(). https://docs.djangoproject.com/en/dev/ref/models/querysets/#reverse
Models:
class House(Model)
class Flat(Model):
house = ForeignKey(House, related_name="houses")
owner = ForeignKey(User)
class User(Model)
queryset:
queryset = User.objects.prefetch_related(
Prefetch("flats", queryset=Flat.objects.select_related("houses"))
And then flats:
{% for flat in user.flats.all %}
<p>№ {{ flat.number }}, {{ flat.house.name }}</p>
{% endfor %}
It's fine. But for houses I need only unique ones
{% for flat in user.flats.all %}
<p>House {{ flat.house.name }}</p>
{% endfor %}
But this template gives me ALL houses, with duplicates.
How can I avoid duplicates, any ideas? I tried .distinct() but it's dosen't work, looks like I using distinct() wrong or etc.
It seems you may end up with duplicate houses if a user is the owner of multiple flats, which are all in the same house. To get all houses that the user is related to via Flats, you can use a different query, starting with House so that you can use distinct:
distinct_houses = House.objects.filter(flats__owner=user).distinct()
The above returns one House object for each flat where the user is an owner, and makes sure you don't have duplicates. (Note that the above assumes the related_name is changed to flats for house = ForeignKey(House, related_name="flats"), since "houses" as the related_name seems like a typo to relate back to the Flat model.)
Alternatively, you could do something in-memory in Python, if you still also need to know all Flats via your original queryset = User.objects.prefetch_related(...) and don't want an additional query. Like:
distinct_houses = {flat.house for flat in user.flats.all()}
Side note: it looks like you have a typo in needing to use Flat.objects.select_related('house') rather than Flat.objects.select_related('houses') based on the Foreign Key field name being house.
Only found solution with template filters.
Queryset:
queryset = User.objects.filter(status="ACTIVE").prefetch_related(
Prefetch("flats", queryset=Flat.objects.select_related("house"))
)
With this I can get all flats for each user:
{% for user in object_list %}
{% for flat in user.flats.all %}
{{ flat.number }}, {{ flat.house.name }}
{% endfor %}
{% endfor %}
And I am using template filter to get unique houses.
{% for user in object_list %}
{% for flat in user.flats.all|get_unique_houses %}
{{ flat.house.name }}
{% endfor %}
{% endfor %}
# extra_tags.py
#register.filter(name="get_unique_houses")
def get_unique_houses(value):
return value.distinct('house')
I have a model named Universities in which there is a ManyToMany field named bookmarks associated with User model. In template file, I have looped through all the universities {% for university in universities %} and I am trying to show a bookmark icon based on the logged in user has bookmarked that specific university or not. However, it seems like I can not filter the query in template directly. How do I do that?
Don't name your models with plural names. "universities" should be "university" because every instance of the model represents a single university.
You can access one to many and many to many relationships in the templates. How you do that depends if you have assigned a related_name to the relationship.
Let me show you an example:
class University(models.Model):
name = models.CharField(max_length=50)
bookmarks = models.ManyToManyField(Bookmark, on_delete="models.CASCADE", related_name="universities")
Then you pass a list of all university models to the template:
class MyView(View):
def get(self, request):
context = { 'universities' : University.objects.all() }
return render(request, "mytemplate.html", context)
And finally you access all that you need from the template. This part is slightly tricky but not too much.
{% for university in universities %}
{% for bookmark in university.bookmarks.all %}
{{ bookmark }}
{% endfor %}
{% endfor %}
If you were to instead pass to the template a list of Bookmark instances, then you would have used the related_name stated in our example.
So like this:
{% for bookmark in bookmarks %}
{% for university in bookmark.universities.all %}
{{ university }}
{% endfor %}
{% endfor %}
If you didn't specify a related_name, then you can access the same data using the _set convention:
{% for bookmark in bookmarks %}
{% for university in bookmark.university_set.all %}
{{ university }}
{% endfor %}
{% endfor %}
I try to display the names of fields from a Model in my template.
This works fine with any type of fields except ManyToManyField.
I use this function in my models.py to return all fields.
def get_all_fields(self):
"""Returns a list of all field names on the instance."""
fields = []
# only display fields with values and skip some fields entirely
if f.editable and value and f.name not in ('id','lastname','firstname') :
fields.append(
{
'label':f.verbose_name,
'name':f.name,
'value':value,
})
return fields
In my template I use this loop to display all fields:
{% for f in modelname.get_all_fields %}
<td>{{f.label|capfirst}}</td><td>{{f.value|escape|urlize|linebreaks}}</td>
{% endfor %}
As mentioned before, this works fine with all fields except ManyToManyFields.
For example one of my M2M relations looks like this:
family = models.ManyToManyField('family', related_name="family", null=True, blank=True)
I'd be thankful for every hint that helps solving this.
Regards
Conrad
Try to specify verbose_name argument for ManytoManyfield
family = models.ManyToManyField('family',verbose_name=u'trampampam', related_name="family", null=True, blank=True)
You write {{f.value|escape|urlize|linebreaks}}, which displays the value of the field. However, the value of a M2M relation is a set of object instances and you need to iterate again over the set (if that is the result you want):
{% load m2m_filter %}
{% for f in modelname.get_all_fields %}
<td>{{f.label|capfirst}}</td>
<td>
{% if f.value|is_m2m %}
{% for object in f.value.objects.all %}
{{ object|escape|urlize|linebreaks }}
{% endfor %}
{% else %}
{{f.value|escape|urlize|linebreaks}}
{% endif %}
</td>
{% endfor %}
and you also have to create the filter
m2m_filter.py
from django import template
from django.db import models
register = template.Library()
def is_m2m(value):
return type(value) == models.ManyToManyField *
register.filter('is_m2m', is_m2m)
* I guess, it's a different type; just check that
I have the following models:
class Tag(models.Model):
name = models.CharField(max_length=20)
class Entry(models.Model):
title = models.CharField(max_length=100)
date = models.DateField()
tags = models.ManyToManyField(Tag)
In a view I create a list of Entry object and want to show the elements in the template:
{% for entry in entries %}
{{ entry.title }}
{{ entry.date }}
<!-- {% for tag in entry.tags %} {{ tag }} {% endfor %} -->
{% endfor %}
And with this template code it generates the following TemplateSyntaxError pointing to the template's first line (for tag):
Caught TypeError while rendering: 'ManyRelatedManager' object is not iterable
The entries variable is a list:
entries = Entry.objects.filter(user=user_id)
entries = list(entries)
entries.sort(key=lambda x: x.id, reverse=False)
Do you know what can be the problem here and how to resolve this issue?
I'm new to Django, so any suggestions how to debug the templates may be helpful.
Update
I get the same error even with this template:
{% for entry in entries.all %}
<!-- everything is commented out here -->
{% endfor %}
There is no need to turn the entries QuerySet into a list. Additionally, you can let the DB do the sorting using order_by.
entries = Entry.objects.filter(user_id=user_id).order_by('id')
Add .all to get all the values from a relationship (just like Entry.objects.all()).
entry.tags.all
You can try this in the shell as well (I use ipython so your output may look different):
$ ./manage.py shell
# ...
In [1]: from yourproject.models import Entry, Tags
In [2]: entry = Entry.objects.all()[0]
In [3]: entry.tags
Out[3]: <django.db.models.fields.related.ManyRelatedManager object at 0x...>
In [4]: entry.tags.all() # for an entry with no tags.
Out[4]: []
In [5]: # add a few tags
In [6]: for n in ('bodywork', 'happy', 'muscles'):
...: t, created = Tag.objects.get_or_create(name=n)
...: entry.tags.add(t)
In [7]: entry.tags.all()
Out[7]: [<Tag: ...>, <Tag: ...>, <Tag: ...>]
And if you want to call out the entries with zero tags use for..empty.
{% for tag in entry.tags.all %}
{{ tag.name }}
{% empty %}
No tags!
{% endfor %}
Here is the solution of your query,
Verifying your solution by giving an example
Suppose a book have number of tags, so in order to display all the tags of a book on template can be like this
{% for tag in book.tags.all %}
{{ tag.name }}
{% endfor %}
where the model of Tag is like,
class Tag(models.Model):
name = models.CharField(max_length=100)
def __unicode__(self):
return "%s" % unicode(self.name)
OK. I found the problem. I had some incorrect code which was commented out. But Django processed that code. So html comments didn't work here. I fixed this and it all worked like a charm.
So if you didn't know - the html comments don't prevent template processing.
This is because the template is being processed by Django first then HTML is rendered by browser.
The above from istruble is correct but if your question contains all of your code you need to specify a property in your template:
{% for entry in entries %}
{{ entry.title }}
{{ entry.date }}
{% for tag in entry.tags.all %} {{ tag.name }} {% endfor %}
{% endfor %}
or a default unicode function to your model:
class Tag(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name