Opened 14 years ago

Last modified 14 years ago

#13848 closed

Template filter bug when accessing foo_set manager. — at Initial Version

Reported by: danols@… 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)

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top