Opened 14 years ago
Last modified 14 years ago
#13848 closed
Template filter bug when accessing foo_set manager. — at Version 1
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 (last modified by )
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@danols.com) # # 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)
Reformated code, please use preview in the future.