Django template conditional delete button - django

I've got a model called Group (not to confuse with the built-in Django groups) with a ManyToMany field called admins to django.contrib.auth.models.User. In my template I want the user to be able to delete a group when it is an admin (the admins field contains the current user). The way I am doing this at the moment is with a loop like this:
{% for admin in group.admins.all %}
{% if user == admin %}[x]{% endif %}
{% endfor %}
Since Django discourages passing attributes to functions inside templates, I cannot use the filter or the get functions on the admins field. But I was wondering whether there was a more direct approach to solving this problem rather that looping through all admins.

Django templates support the if x in list syntax (since Django 1.2, according to the linked thread). You can simplify your for-loop with a single if:
{% if user in groups.admins %}
[x]
{% endif %}

Related

Django admin dashboard, is there a way to lowercase model name?

I'm looking for a way to lowercase the first letter of a model in my django admin site.
i.e.:
model verbose name is "agent-1.0.0" is shown as "Agent-1.0.0" on the dashboard,
simple but IDK
grappelli trick will also work for me.
django - 1.7.1
also - need this only for one app models group - not all of my dashboard should be lowercase...
so, overriding the index.html is not so efficient
The capitalization is hard-coded in the template, same for the templates in Grappelli.
You can use catavaran's suggestion, but this will transform every model name. Overriding the template is a huge pain in the ass to maintain for something this small.
The only workable solution I can think of is to bypass the capfirst filter with a space:
class Meta:
verbose_name = " agent-1.0.0"
As capfirst only forcibly capitalizes the first character, nothing will happen if the first character is not a letter.
Model name passed to template as capfirst(model._meta.verbose_name_plural) so you have to lowercase it in the admin/index.html tempate or via CSS. Imho CSS option is simpler:
div.module tr[class^=model-] th {
text-transform: lowercase;
}
If you want lowercase only some models (for example User) then change CSS selector to this:
div.module tr.model-user th {
text-transform: lowercase;
}
With Grapelli you could create a custom Dashboard by running:
python manage.py customdashboard
and setting GRAPPELLI_INDEX_DASHBOARD on your settings to your custom class.
You can make this custom class extend from the Dashboard class that grappelli offers and override it to your needs. Look especially at the ModelList class, where you can specify the title you want for the model.
There is a CSS-way for those who don't want to override Django admin classes. Override and extend templates/admin/base_site.html template as follows:
{% extends "admin/base_site.html" %}
{% block extrahead %}
<style>
h1.model-title {text-transform: lowercase;}
h1.model-title:first-letter {text-transform: uppercase;}
</style>
{% endblock %}
{% block content_title %}
{% if title %}<h1 class="model-title">{{ title }}</h1>{% endif %}
{% endblock %}
This will make only first letter of each content_title uppercase.
You can use the same way to lowercase model name in admin tables as well as sidebar. However, I'd like to point that by tacit agreement model's verbose_name as well as verbose_name_plural shouldn't be capitalized. This will save you a lot of overrides in your project, like I provided above to normalize change_list header.

django best practice query foreign key

I am trying to understand the best way to structure queries in django to avoid excessive database hits.
This is similar to the question: Django best practice with foreign key queries, but involves greater 'depth' in the queries.
My situation:
models.py
class Bucket(models.Model):
categories = models.ManyToManyField('Category')
class Category(models.Model):
name = models.CharField(max_length=50)
class SubCategory(models.Model):
category = models.ForeignKey(Category)
class SubSubCategory(models.Model):
subcat = models.ForeignKey(SubCategory)
views.py
def showbucket(request, id):
bucket = Bucket.objects.prefetch_related('categories',).get(pk=id)
cats = bucket.categories.prefetch_related('subcategory_set__subsubcategory_set',)
return render_to_response('showbucket.html', locals(), context_instance=RequestContext(request))
and relevant template:
{% for c in cats %}
{{c}}
<ul>
{% for d in c.subcategory_set.all %}
<li>{{d}}</li>
<ul>
{% for e in d.subsubcategory_set.all %}
<li>{{e}}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
{% endfor %}
Despite the use of prefetch_related(), I seem to be hitting the database each time the top two for statements are evaluated, e.g. {% for c in cats %}, (at least I believe so from reading the debug_toolbar). Other ways I've tried have ended up with (C x D x E) number of database hits. Is this something inherently wrong with my use of prefetch, queries, or models? What is the best way in Django to access database objects with a "depth > 1" so-to-speak?
Use select_related() instead:
https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.select_related
bucket = Bucket.objects.select_related('categories',).get(id=id)
cats = bucket.categories.select_related('subcategory_set__subsubcategory_set',)
So, i found out there's a couple things going on here:
First, my current understanding on select_related vs prefetch_related:
select_related() follows foreign-key relationships, causing larger result sets but means that later use of FK won't require additional database hits. It is limited to FK and one-to-one relationships.
prefetch_related() does a separate lookup for each relationship and joins them in python, and is means to be used for many-to-many, many-to-one, and GenericRelation and GenericForeignKey.
By the book, I should be using prefetch(), as I was not 'following' the Foreign Keys.
That's what I had understood going into this, but my template seemed to be causing additional queries when evaluating the given for loops in the template, even when I added the use of {with} tags.
At first, I had thought I had discovered something similar to this issue, but I am unable to replicate when I built out my simplified example. I switched from using the debug toolbar to direct checking using the following template code (in the article Tracking SQL Queries for a Request using Django by Karen Tracey, I would link but am link-limited):
{% with sql_queries|length as qcount %}
{{ qcount }} quer{{ qcount|pluralize:"y,ies" }}
{% for qdict in sql_queries %}
{{ qdict.sql }} ({{ qdict.time }} seconds)
{% endfor %}
{% endwith %}
Using this method, I am only seeing 5 queries for using pre-fetch() (7 with debug_toolbar), and queries grow linearly when using select_related() (with +2 for debug_toolbar), which I believe is expected.
I will gladly take any other advice/tools on getting a handle on these types of issues.

Django : Customizable templates

I am working on creating a web portal and I want to offer the users the feature of making changes to there profile/dashboard like changed background. etc.
Can anybody please guide me to an efficient approach to achieve this?
Thanks
This is trickery that has more to do with css and javascript than with django templates.
The only thing that is django related here is the actual storing of these preferences.
e.g. the filepaths of the actual background images.
After that you will do something similar to what is described in this answer:
how to change html background dynamically
EDIT
I don't see why you need different directories for every user. Django templates
give you more than enough power to do what you want.
For example, let's say that each user can upload his own background picture. Also
I assume that you follow this popular django pattern for storing additional information
about your users. https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users
So we have this UserProfile model:
class UserProfile(models.Model):
CHOICES = (
('vertical', 'Vertical'),
('horizontal', 'Horizontal'),
)
user = models.OneToOneField(User)
background_image = models.ImageField(upload_to='images')
dashboard_layout = models.CharField(max_length=10, choices=CHOICES)
You can pass this extra information to your javascript context(either with Ajax or without)
and then change the background image for every individual user.
Also we could do special layout at the template level like this:
{% extends "base.html" %}
{% block main_body %}
{% if request.user.get_profile.dashboard_layout == 'vertical' %}
{% include "layouts/vertical.html" %}
{% else %}
{% include "layouts/horizontal.html" %}
{% endif %}
{% endblock main_body %}

How to get list of multiple choices

I have a problem rendering a template variable for example {{ profile.speciality }}
that resulted in [u'pediatrics'] on the web page, what I actually wanted is simply
pediatrics. The data is coming from MultipleChoiceField and a CheckboxSelectMultiple
widget, because user should be able to select multiple options.
Then i used request.POST.getlist('speciality') to populate data in to the model instance,
for example:
user_profile = UserProfile(speciality=request.POST.getlist('speciality'))
I also tried to iterate over {{ profile.speciality }} in the template but what I am getting is string iteration rather than a list iteration.
I am an absolute beginner, I have no programming experience, pardon me if I did anything stupid, need help desperately, tried everything I could.
thank you
You can use this to render a MultipleChoiceField
{% for speciality in profile.speciality.all %}
{{ speciality }}
{% endfor %}

django get list of distinct 'children' of ForeignKey related model (and do this in template?)

I'm making a database of released music albums
models.py
class Image(models.Model):
image = models.ImageField(....
class Album(models.Model):
title = models.CharField(....
class Release(models.Model):
album = models.ForeignKey(Album)
cover_art = models.ForeignKey(Image, blank=True, null=True, on_delete=models.SET_NULL)
In my template (at the moment I'm using generic views) I have:
{% for a in album_list %}
{% for r in a.release_set.all %}
{% if r.cover_art %}
# display cover art image
{% endif %}
{% endfor %}
{% endfor %}
The problem is that sometimes an album has been released several times with identical cover art, in which case I'd like to display the image only once, with some text listing the releases it pertains to.
I've tried:
{% for i in a.release_set.cover_art %}
{% for i in a.release_set.cover_art_set %}
{% for i in a.release_set.all.cover_art %}
{% for i in a.release_set.all.cover_art_set %}
Or in a simpler case, I'd at least like to display the images smaller if there are more than one of them.
{% if a.release_set.count > 1 %} # works but displays duplicate images
{% if a.release_set.cover_art_set.count > 1 %} # doesn't work (see above)
Is it possible to get a list of objects related by reversing this ForeignKey lookup then asking for the set of their children? The only way I can think of is by assembling some tuples/lists in the view.
I managed this with a new method on the Album model:
class Album(models.Model):
title = models.CharField(....
def distinct_cover_images(self):
"Returns the queryset of distinct images used for this album cover"
pks = self.release_set.all().values_list('cover_art__pk', flat=True)
distinct_cover_images = Images.objects.filter(pk__in=pks).distinct()
return distinct_cover_images
Then the template is much more simple:
{% for i in a.distinct_cover_images %}
Credit to #danilobargen however for his contribution to this code.
If I understood this right:
An album can have several releases
A release has only one cover
You want to loop over all covers of an album
In that case, the following should work:
{% for release in a.release_set.all %}
{{ release.cover_art.image }}
{% endfor %}
If you want to prevent listing identical covers, you can either compare the covers in the loop, or prepare a set with distinct covers in your view, so you can pass it on to the template.
# Solution using a set
context['distinct_coverimages'] = \
set([r.cover_art.image for r in album.release_set.all()])
# Solution using two queries, might perform better
pks = album.release_set.values_list('cover_art__pk', flat=True)
context['distinct_coverimages'] = models.Image.filter(pk__in=pks).distinct()
A third alternative would be creating a custom template filter for your album, to return all distinct release covers.
In any case, I recommend debugging such things in your Django shell. You can issue the shell with ./manage.py shell. If you have installed django-extensions, you can also use ./manage.py shell_plus to autoload all models. All object attributes and functions that don't require arguments (e.g. normal instance attributes or instance functions without arguments like 'string'.isalnum()) can also be used the same way (just without the parentheses) in your template.