Search code examples
google-app-enginedjango-nonreldjangoappengine

How do you display an image cropped with AppEngine's Image API in a Django template?


I'm using django non-rel for appengine (djangoappengine) and have an app where the user selects an image and I have to return a crop from the selected image.

The images in my app are uploaded to the Blobstore following the django-filetransfers instructions. I've managed to upload (and even download) files just fine.

The problem I have is that I don't know how to display an image in template once it's been cropped.

The (simplified) code of my view is as follows:

def canvas_size(request):
if request.method == 'POST':
    #some code here
else:
    #At this point the user has selected an image, and I store its pk in session
    img_file = ImageModel.objects.get(pk=request.session[SESSION_KEY]['image_pk'])
    img = images.Image(blob_key=str(img_file.file.file.blobstore_info.key())) 
    img.resize(height=300)
    img.crop(left_x=0.0, top_y=0.0, right_x=0.5, bottom_y=1.0)
    crop_img = img.execute_transforms(output_encoding=images.JPEG)
    #I know that the image is being cropped because if I do
    #print crop_img
    #I get to see the image in browser
    response_dict = {
        'crop_img' : crop_img,
    }

    template_name = 'canvas/step7.html'
    response = render_to_response(template_name, response_dict, context_instance=RequestContext(request))
    return response

In canvas/step7.html I've tried the following:

<img src="{{ crop_img.url }}" />
<img src="{{ crop_img.file.url }}" />

But of course that does not work.

Based on the Google AppEngine Image documentation, I know that the execute_transforms() function returns the image's encoded representation as a string. So I suppose I'm missing a step where I transform the string to a file... maybe?

Could someone point me in the right direction in order to display a crop in template using django?

Thanks for your help!


Solution

  • I finally managed to solve my problem. I followed voscausa's advice, but I'm posting a solution adapted to Django.

    Background: I couldn't use get_serving_url since I'll need to crop specific coordinates. The method execute_transforms returns a string. Crops are better served from the blobstore

    Solution

    models.py

    from djangotoolbox.fields import BlobField
    class ImageModel(models.Model):
        file = models.FileField(upload_to="images")
    
    class MiniCanvas(models.Model):
        crop = BlobField()
    

    views.py

    from my_app.models import ImageModel, MiniCanvas
    from google.appengine.api import images
    
    def view_that_crops(request):
     if request.method == 'POST':
            #Do stuff here
        else:
            #The pk of the selected image is stored in session
            img_file = ImageModel.objects.get(pk=request.session[SESSION_KEY]['image_pk'])
            img = images.Image(blob_key=str(img_file.file.file.blobstore_info.key())) 
            img.resize(height=300)
            img.crop(left_x=0.0, top_y=0.0, right_x=0.5, bottom_y=1.0)
    
            #This method returns the image's encoded representation as a string
            crop_img = img.execute_transforms(output_encoding=images.JPEG)
            #I can save the string as a BlobField in my model
            mini_canvas = MiniCanvas.objects.create(crop=crop_img)
            response_dict = {
                'mini_canvas_pk' : mini_canvas.pk,
            }
    
            template_name = 'canvas/step7.html'
            response = render_to_response(template_name, response_dict, context_instance=RequestContext(request))
            return response
    
    #This function will be called in template
    def show_crop(request, crop_pk):
        try:
            crop = MiniCanvas.objects.get(pk=crop_pk)
        except MiniCanvas.DoesNotExist:
            crop = None
        if not crop:
            #TODO: return a default image maybe?
            return HttpResponse()
        #Don't forget content_type
        return HttpResponse(crop.crop, content_type="image/jpeg")
    

    urls.py

    from my_app.views import show_crop, view_that_crops
    urlpatterns = patterns('',
        url(r'^cropper/(?P<crop_pk>\d+)/$', show_crop, name='show_crop'),
    )
    

    template 'canvas/step7.html'

    <img src="{% url show_crop mini_canvas_pk %}" alt="The crop you were looking for" />
    

    And this is it. What I didn't understand was how to call the handler suggested by voscausa in template. (I'm new at this :( )

    This is a full example on how to crop an image and display it in template. However, I believe that some improvements can (and should) be made. Such as: cropping as a background task to avoid performance issues; or using memecache...

    I hope this helps someone else!

    EDIT I should probably add that the BlobField in models.py is not evident at first. I found about it thanks to the discussion in this google group.