Display name of many to many relation in template - django

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

Related

Combining model data to form url pattern

I am generating a generic list right now, with the following code:
views.py
class ServiceReportIndex(LoginRequiredMixin, ListView):
model = TblServiceRecords
context_object_name = 'all_servicereports'
login_url = 'login'
template_name = 'servicereport/servicereport_index.html'
def get_context_data(self, **kwargs):
context = super(ServiceReportIndex, self).get_context_data(**kwargs)
context['companies'] = TblCompanies.objects.all()
return context
In my template, I want to generate a URL using both of the models. The TblServiceRecords model contains a column that references the company_id, which is the primary key of the appropriate company in the TblCompanies model. I want to use the company_name from the Companies model in my list view. How would I go about doing that? I'm sure it's simple but I can't seem to get my url tags done correctly.
<div class="col-sm-4">
<p>Company Name</p>
{% for servicereport in all_servicereports %}
<p>{% for servicereport.company_id in companies.company_id %} {{ companies.company_name }} {% endfor %}</p>
{% endfor %}
</div>
Also, how can I be sure my views.py is set up correctly for multiple model functionality? I ask because if I put
{% for company_name in companies %}
{{companies.company_name}}
{% endfor %}
In my template, nothing comes up, but there are no errors either.
Probably you cannot see companies bacause of this:
{{companies.company_name}}
companies is queryset and it does not have company_name property.
Try this:
{% for company_name in companies %}
{{company_name.company_name}}
{% endfor %}

Django manytomany field filter list

I am printing a list of beers matching some filters, and the bars where each is on tap. These are in a manytomany relationship. I need to filter this list of bars to only show those in a given state.
I can achieve this using if statements in the template, but then am unable to format the list to use commas with an 'and' before the final item (like https://stackoverflow.com/a/3649002/6180992), as I do not know the length of the list.
I have thought of three ways this might be possible, but cannot get any to work:
filtering the bars related field as well as the beers in the views
assembling the list in the template before looping through again to print it
filtering the bars related field in the template
Here are the relevant sections of code:
models.py
class Bar(models.Model):
bar = models.CharField(max_length=200, default='FinshnPig')
state = models.CharField(max_length=200,default='NY')
def __str__(self):
return self.bar
class Meta:
ordering = ('bar','region')
class Tap(models.Model):
bar = models.ManyToManyField(Bar,default='FinshnPig')
brewery = models.CharField(max_length=200)
beer = models.CharField(max_length=200)
state = models.CharField(max_length=200, default='NY')
def __str__(self):
return self.beer
views.py
f = TapFilter(request.GET, queryset=Tap.objects.filter(state="VIC"))
template:
{% for tap in filter %}
<li>
<b>{{ tap.beer }}</b>
<em>{{ tap.brewery }}</em>
#{% for bar in tap.bar.all %}{% if bar.state == "VIC" %}{{ bar.bar }}</b>{% endif %}{% include "taplists/comma.html" %}{% endfor %}
</li>
{% endfor %}
You can use prefetch for querying only the related bars before sending it to template, like so:
prefetch = Prefetch(
'bar',
queryset=Bar.objects.filter(state=CHOSEN_STATE),
to_attr='selected_states'
)
filter = Tap.objects.filter(state=CHOSEN_STATE).prefetch_related(prefetch)
Now inside your template use the custom attribute you assigned:
{% for tap in filter %}
# Now selected_bars only contains the bars in the state you want
{% for bar in tap.selected_bars.all %}
...
{% endfor %}
{% endfor %}
Additional info on Django docs for prefetch_related
Note: Using prefetch for ManyToMany relations will also increase the performance, as there won't be as many database lookups.

Return multiple models from a view in Django

I am learning Django,
I am having a problem with fetching data from 2 different models with the same view.
My models are as follow :
#python_2_unicode_compatible
class Destination(models.Model):
roadtripname = models.CharField(max_length=100)
def __str__(self):
self.roadtripname
#python_2_unicode_compatible
class Trajectories(models.Model):
roadtripname = models.ForeignKey(Destination, on_delete=models.CASCADE)
place_text = models.CharField(max_length=100)
def __str__(self):
return self.place_text
my view class is this :
class IndexView(generic.ListView):
template_name = 'roadtrip/index.html'
context_object_name = 'roadtrip_list'
def get_queryset(self):
return Destination.objects.filter()
my html is :
{% for roadtrip in roadtrip_list %}
{{ roadtrip.roadtripname }}
{% endfor %}
I have tried to return my 2 models like this :
return Destination.objects.filter(), Trajectories.objects.filter()
and then in html doing
{% for roadtrip in roadtrip_list[0] %}
{{ roadtrip.roadtripname }}
{% endfor %}
but this does not work, I do not know how to be able to access both the Destination and Trajectories models. Could anyone point me in the right direction?
I could just loop through the data from the second set(trajectories) and save in an javascript array when it's equal to the select value.
You could but that is just awful and you're just opening yourself up to allow a client to modify something that they really don't need to.
With every django many-to-x relationship you can find all objects related to another with _set so just keep your initial get_queryset the same (returning destination) and then get trajectories for each object.
{% for roadtrip in roadtrip_list %}
{% for trajectory in roadtrip.trajectories_set.all %}
{# Use trajectories #}
{% endfor %}
{{ roadtrip.roadtripname }}
{% endfor %}
*I'm aware there are very simple optimizations that could be made here for performance improvements but I'm intentionally not including them

django template get foreign key of queryset

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.

Need some advice for duplicated queries

I have a lot of duplicated queries (in django debug toolbar) when i load my menu tabs, im sure i can optimize this but don't find the good way.
Models :
class Categorie(models.Model):
name = models.CharField(max_length=30)
visible = models.BooleanField(default = False)
def __str__(self):
return self.nom
def getscateg(self):
return self.souscategorie_set.all().filter(visible = True)
class SousCategorie(models.Model):
name = models.CharField(max_length=30)
visible = models.BooleanField(default = False)
categorie = models.ForeignKey('Categorie')
def __str__(self):
return self.name
def gettheme(self):
return self.theme_set.all().filter(visible = True)
class Theme(models.Model):
name = models.CharField(max_length=100)
visible = models.BooleanField(default = False)
souscategorie = models.ForeignKey('SousCategorie')
def __str__(self):
return self.name
Views :
def page(request):
categs = Categorie.objects.filter(visible = True)
return render(request, 'page.html', locals())
Templates :
{% for categ in categs %}
<li>
{{categ.name}}
<ul>
{% for scateg in categ.getscateg %}
<li>
{{scateg.name}}
<ul>
{% for theme in scateg.gettheme %}
<li>{{ theme.name }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
I have look at prefetch_related but only work if i want load Categorie from SousCategorie and SousCategorie from Theme, so if i understand i need the contrary of this...
Solved !
If it can help :
from .models import Categorie, SousCategorie, Theme, SousTheme
from django.db.models import Prefetch
pf_souscategorie = Prefetch('souscategorie_set', SousCategorie.objects.filter(visible=True))
pf_theme = Prefetch('souscategorie_set__theme_set', Theme.objects.filter(visible=True))
pf_soustheme = Prefetch('souscategorie_set__theme_set__soustheme_set', SousTheme.objects.filter(visible=True))
categs = Categorie.objects.filter(visible=True).prefetch_related(pf_souscategorie, pf_theme, pf_soustheme)
and call like this in the template :
{% with currscat=categ.souscategorie_set.all %}
{% with currth=souscateg.theme_set.all %}
{% with currsth=theme.soustheme_set.all %}
Bye
In Django every time you evaluate a new queryset a query is executed, so you need to reduce the number of queryset being used. Here is what is happening:
You create a queryset Categorie.objects.filter(visible=True) and it is passed into the view layer, there the first query is executed at the this tag {% for categ in categs %}
Inside the loop, for every category you're calling a method categ.getscateg which returns a new queryset return self.souscategorie_set.all().filter(visible = True), this queryset will be executed at the second loop in your template {% for scateg in categ.getscateg %}
The same thing happens with {% for theme in scateg.gettheme %}
Using prefetch_related was the right move, try something like (didn't test it):
Categorie.objects.filter(visible=True, souscategorie_set__visible=True, souscategorie_set__theme_set__visible=True).prefetch_related('souscategorie_set__theme_set')
prefetch_related works by running a first query to load the categories that satisfy your current filter, then it executed a second query to load all subcategories and so on.
In other cases you can use select_related, but that only works when a single query can be used, as an example, it would work if you needed the category and subcategory of a theme, like:
Theme.objects.filter(pk=1).select_related('souscategorie__categorie')
The difference here is that Theme is the one that has the ForeignKey, so it has only one subcategory, and it can be loaded with a single join, that means you can use select_related only when your queryset is from the Model that points and I think that it also works with OneToOneField.
I have reduce my querysets by 2 by using "with"
It's a good point but i always got a lot of duplicate (like 44 duplicate for 51 queries), for example i hit my database when i do that :
{% for categ in categs %}
{% with currscat=categ.getscateg %}
<li class = "{% if currscat %} has_menu {% endif %}"> {{categ.name}} # This line hit database
{% if currscat %} # This line hit database
<ul class = "menu-1"> ... </ul>
{% endif %}
</li>
{% endwith %}
{% endfor %}
I try to use prefetch_related like this :
categs = Categorie.objects.filter(visible=True).prefetch_related('souscategorie_set')
but thats just add query to database, don't reduce...
Some advice?
Thanks