Opened 14 years ago
Last modified 14 years ago
#13848 closed
Template filter bug when accessing foo_set manager. — at Initial Version
Reported by: | Owned by: | nobody | |
---|---|---|---|
Component: | Template system | Version: | 1.2 |
Severity: | Keywords: | Related objects | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Hi Awesome Django Team,
Below is a bug that I've discovered (I did not find anything similiar in the bug list). The best way I understand it is that when using foo.bar_set.all.0 with a filter the filter is called twice. First with the bar object being passed and then second with the bar object as string.
Below is the code to duplicate it:
class Foo(models.Model):
title = models.CharField(max_length=50)
class Photo(models.Model):
image = models.ImageField(upload_to=)
In a view I pass all Foos objects to the template. And attempt to retrieve just the first image to be displayed but first it is passed to a filter to create a thubmnail.
Here is the rough template code that generates the error: Caught AttributeError while rendering: 'str' object has no attribute 'path'.
{% for foo in foos %}
<img alt='image' src='{{ foo.photo_set.all.0.image|thumbnail:'200x200 }}
{% endfor %}
The thumbnail filter is called twice - print typeof(file) statement in the filter outputs:
<class 'django.db.models.fields.files.ImageFieldFile'>
<type 'str'>
A workarround is insert another loop like this:
{% for foo in foos %}
{% for photo in foo.photo_set.all %}
{% if forloop.first %}
<img alt='image' src='{{ photo.image|thumbnail:'200x200 }}
{% endif %}
{% endfor %}
{% endfor %}
Thank you for looking into it.
# Author Daniel Sokolowski (danols@…)
#
# Original see http://www.djangosnippets.org/snippets/955/
#
# A filter to resize a ImageField on demand, a use case could be:
# <img src="{{ object.image.url }}" alt="original image">
# <img src="{{ object.image|thumbnail }}" alt="image resized to default 104x104 format">
# <img src="{{ object.image|thumbnail:'200x300' }}" alt="image resized to 200x300">
# not implemented <img src="{{ object.image|thumbnail:'200x300_crop' }}" alt="image resize to 200x300 with centered croping"
# cropping on by deafult!
import os
import Image
from django.template import Library
import json
register = Library()
def thumbnail(imagefield, options='104x104'):
print type(imagefield)
# defining the size
x, y = [int(x) for x in options.split('x')]
# defining the filename and the miniature filename
filehead, filetail = os.path.split(imagefield.path)
basename, format = os.path.splitext(filetail)
miniature = basename + '_' + options + format
filename = imagefield.path
miniature_filename = os.path.join(filehead, miniature)
filehead, filetail = os.path.split(imagefield.url)
miniature_url = filehead + '/' + miniature
if os.path.exists(miniature_filename) and os.path.getmtime(filename)>os.path.getmtime(miniature_filename):
os.unlink(miniature_filename)
# if the image wasn't already resized, resize it
if not os.path.exists(miniature_filename):
image = Image.open(filename)
# crop if specified.
if (False):
# find out the x1,x2,y1,y2 cordinates for the croping of the image
# we are going to center this ractangel.
int_width = image.size[0]
int_height = image.size[1]
# find out biggest square size
int_box_side = 0
if int_width < int_height:
int_box_side = int_width
else:
int_box_side = int_height
# fidn out the offset
int_width_offset = (int_width - int_box_side) / 2 # since two sides
int_height_offset = (int_height - int_box_side) / 2
# crop it
image = image.crop([int_width_offset,int_height_offset,int_width-int_width_offset,int_height-int_height_offset])
image.thumbnail([x, y], Image.ANTIALIAS)
try:
image.save(miniature_filename, image.format, quality=90, optimize=1)
except:
image.save(miniature_filename, image.format, quality=90)
return miniature_url
register.filter(thumbnail)