How to use sorl-thumbnail package together with django-tables2 package? - django

I do have a table defined in tables.py that renders a column with recipes images.
class ImageColumn(Column):
def render(self, value):
return format_html(
'<img src="static/media/{url}" height="150px", width="150px">',
url=value
)
class RecipeTable(tables.Table):
image = ImageColumn()
name = Column(linkify=True)
class Meta:
model = Recipe
template_name = "django_tables2/bootstrap4.html"
fields = (
"image",
"name",
"directions",
"ingredients",
)
Each image has different size and when I render it with fixed height="150px", width="150px", aspect ratio messes up the image. Therefore I thought I could use sorl-thumbnail package to help mi with generating thumbnails, rather then resizing the whole images. It looks like it is not possible to easily use both django-tables2 and sorl-thumbnail since thumbnails are rendered in html template. My template contains only this to render the table:
{% render_table table 'django_tables2/bootstrap4.html' %}
I need to access the cell so that I can use thumbnail template tag where image should be placed.
{% thumbnail item.image ‘200x100’ as im %}
<img src=’{{ im.url }}’>
{% endthumbnail %}
The only solution I see could be to edit the bootstrap4.html, but is there a better way? Am I missing something?

Related

Using images in 2 different django models

I have a retail site selling multiple brands. Each brand has its logo on its own page. The logo images are in their own model.
How do I use the same logo for a different model on the index page? I'd like to use the same logos while highlighting some store coupons in the sidebar.
Currently, I have a Foreign Key to the store in the IMG model.
current code:
models.py
class Logo(models.Model):
store = models.ForeignKey(Store, on_delete=models.CASCADE,
related_name="storelogo")
image = models.ImageField(upload_to=get_image_path)
views.py
def show_store(request, slug):
store = Store.objects.get(slug=slug)
uploads = store.storelogo.all()
return render(request, 'store.html', {
'store': store,
'products': products,
'coupons': coupons,
'uploads': uploads,
})
HTML
<div class="store-logo">
{% for logo in store.storelogo.all %}
<img src="{{ logo.image.url }}">
{% endfor %}
</div>
The logo shows fine on the store page but I can't get it to show in a For loop on the index page. Is it possible?
I could add another image model and add the exact same logo images for the coupons but it seems to be totally against DRY principles. Are there template tags I can use on an index page or is it best to create another model?
Let me know if I left anything out. Thanks!

How to detect if CloudinaryField in django model is empty in template

picture = CloudinaryField('image', blank = True, null = True)
I have the above line in my django models file. And I am rendering the picture in my template with the cloudinary tag
{% cloudinary "filename" %}
But in cases where the model objects don't contain the picture, I want to forgo the template tag. Right now, my template renders a empty image if the model instance doesn't have a picture.
How can I detect if the model instance contains a picture, so that I can render it selectively using the `
{% if condition %}
None, True, False are available in Django templates. So you can do something similar to -
{% if photo.picture == None %}
or
{% if not photo.picture %}
That said, It's not clear as to why allow null values in this field in the first place. This field should contain all relevant details of the image for further manipulation and delivery.

Add extra data to choices in Django Admin

I have something like the following:
class Destination(models.Model):
name = models.CharField
picture = models.ImageField
def __unicode__(self):
return u"%s" % self.name
class Vacation(models.Model):
destination = models.ForeignKey(Destination)
When creating the model in my Django Admin interface, I'd like my Destinations to be displayed as radio buttons with the Destination name and Destination picture.
I'm using a custom add_form template so displaying radio buttons with destination name is no problem, but including the picture is difficult.
I would like to leave __unicode__(self) as-is, I only need the picture returned with the object in this admin view. Also I don't want to inline the object.
Any advice on how to do this (including how to incorporate it into the template) would be great!
EDIT: This SO post comes very close to what I need, but I would like to access the individual choice data instead of parsing it from a modified label.
This is not an admin-specific answer, but I think it should work in admin if you can use a custom template for the form.
You could make a modified widget (probably as a subclass of an existing Django widget), that send extra fields from the model to a custom widget template.
You could also render the form manually in the template where it's displayed, and make an inclusion tag that fetches any extra information using the id of your destination object, which is passed as the value of the option.
For example:
your_template.html
{% load destinations %}
{% for opt in form.destination %}
{{ opt.tag }}
{% destination opt.data.value %}
{% endfor %}
destinations.py (in your_app/templatetags)
from django import template
from your_app.models import Destination
register = template.Library()
#register.inclusion_tag('your_app/destination-option.html')
def destination(id):
destination=Destination.objects.filter(id=int(id)).first()
return {'destination':destination}
destination-option.html
<!-- any formatting you prefer -->
{{destination.title}}
<img src="{{destination.picture.image_url}}">

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.

Overiding placholders in django cms?

Hey im using the placeholder fields from django cms in some of my custom cms apps. Basically what im trying to achieve is specific styles and filters for the same placeholder fields being used in different templates.
for example if i have a model for vacancies that looks something like this:
from django.db import models
from cms.models.fields import PlaceholderField
# Create your models here.
class Vaccancy(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, unique = True)
ref_number = models.CharField(max_length=255)
info = PlaceholderField('info')
active = models.BooleanField(default=True, verbose_name="posistion active?")
and another model that also utilizes the placeholder field in a similar way. What i hoped i could do is overide the tex.html template then have some conditional logic to detect the name of the placeholder like so
{% ifequal placeholder "info" %}
{{ body|truncatewords:200|safe }}
{% endifequal %} the aim of this is so i can specify different filters like truncatewords etc as i dont want to apply this to every placeholder that uses a text plugin!
hope that was clear enough! cheers for any help!
If you use placeholder fields, you have to check for placeholder.slot, also note that {% if placeholder.slot == "info" %} seems a bit nicer than ifequal :D