Django : Customizable templates - django

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

Related

Using placeholder tags in non-django CMS pages

In the Django CMS there's the {% placeholder 'content' %}. I tried to use it on a non-django-cms page, i.e., a detail-view page that comes from an apphook. However, when I switch to the structure view in the detail-view page and the placeholder does not seem to reflect. Is that's how it's supposed to work or is there a problem with my code? If it's how it's supposed to work is there a way to make placeholder appear in the page?
You can't use {% placeholder outside of CMS pages.
If you're on one of these pages, you can use a static placeholder. These will show the same content on any page where a static placeholder with the same name exists. So a good example of these is a footer, or header where you'd want it to be the same on all pages;
{% static_placeholder "footer" %}
Another thing you can use, good for your example of a detail page in an apphook, is a PlaceholderField on your models.
Take this example;
from django.db import models
from cms.models.fields import PlaceholderField
class Category(models.Model):
name = models.CharField(max_length=20)
description = PlaceholderField('category_description')
In your template you can then render this placeholder and it'll behave like a standard placeholder on a cms page;
{% load cms_tags %}
{% render_placeholder category_instance.description language 'en' %}
You can find docs for PlaceholderField here

How do I properly compare two different models based on their field values and output the differences?

I am trying to figure out how to produce the output between two querysets that have similar fields. I have two different models where I keep identical fields, and at times I want to show the differences between these two models. I have researched and successfully used sets and lists in my python code, but I can't quite figure out how to leverage them or determine if I Can. Sets seems to strip out just the field values, and when I try to loop through my sets, it doesn't currently work because it's just the values without the keys.
Example:
class Team(models.Model):
player = models.Charfield
coach = models.Charfield
class TeamCommittee(models.Model):
player = models.Charfield
coach = models.Charfield
I want to be able to query both models at times and be able to exclude data from one model if it exists in the other. I have spent the afternoon trying to use sets, lists, and loops in my django templates but can't quite work this out. I have also tried various querysets using exclude....
I tried something like....
query1 = TeamCommittee.objects.filter(id=self.object.pk).values('coach','player')
query2 = Team.objects.filter(id=self.object.pk).exclude(id__in=query1)
When I use the approach above, I get TypeError: Cannot use multi-field values as a filter value.
I am wondering if I can do this via a query or if I need to dump my querysets and go down a path of manipulating a data dictionary? That seems extreme for what I am trying to do though. This does seem to be a bit more complicated because I am trying to cross reference two different models. If it was the same model this would be a lot easier but it's not an option for this particular use case.
Thanks in advance for any thoughts on the right way to approach this.
If you want to compare on the basis of the ID of both tables, probably you can use this:
teamID = list(Team.objects.all().values_list('id', flat=True))
query1 = TeamCommittee.objects.filter(id__in=teamID)
teamCommitteeID = list(TeamCommittee.objects.all().values_list('id', flat=True))
query2 = Team.objects.filter(id__in=teamCommitteeID)
I was super close...Instead I just did this...
query1 = TeamCommittee.objects.filter(id=self.object.pk).values('coach','player').distinct()
Then in my template I did a very simple....
{% for item in query1.all %}
item
{% endfor %}
Then when I wanted to get the values out of the other queryset I just did the same thing with the loop.
query2 = Team.objects.filter(id=self.object.pk).values('coach','player').distinct()
Then in my template I did a very simple....
{% for item in query2.all %}
item
{% endfor %}
Sometimes simplicity is hard.
The answer above after additional testing only partially worked. I ultimately abandoned that approach and instead incorporated the logic below into my template. I did not need to create separate queries, I just needed to loop through the fields that were already available to me as part of the DetailView I was using....
{% for author in author_detail.author_set.all %}
{% for book in book_detail.book_set.all %}
{% if author.author_name %}
{% if book.book_name in author.book_name %}
{% if author.book_name == book.book_name %}
{% elif author.book_name != book.book_name %}
{{ author.author_name }}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
Thank you to everyone who made suggestions to get me to this point.

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 template conditional delete button

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

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.