Opened 14 years ago

Last modified 14 years ago

#13848 closed

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

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 (last modified by Alex Gaynor)

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)

Change History (1)

comment:1 by Alex Gaynor, 14 years ago

Description: modified (diff)

Reformated code, please use preview in the future.

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