0

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!

2 Answers2

2

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.

0

You have to save the cropped image in the datastore or the blobstore in your above code and you can use caching (memcache).

Now you can write a handler to serve the image url.

Blobstore Handler example :

class ImgServe(blobstore_handlers.BlobstoreDownloadHandler):

    def get(self, resource):    # resource contains the blob_key + image type (like .jpeg)                                   

        (key, _, _) = resource.rpartition('.')
        blob_info = blobstore.BlobInfo.get(key)
        self.response.headers[str('Content-Type')] = str(blob_info.content_type)     
        self.send_blob(blob_info, save_as=True)

As an alternative you can also use dynamic cropping using get_serving_url. In this case you do not have to crop the image yourself, but you use Google High Performace Image Serving and a get_serving_url to fetch it. In this case you do not have to write a handler.

See also this answer : Python Google App Engine Image object

Community
  • 1
  • 1
voscausa
  • 11,253
  • 2
  • 39
  • 67
  • Thanks for your answer. I wasn't expecting that I'd have to save the crop to the db in order to serve it. I'll try to apply what you've suggested and come back with more info. – Jaime Vaqueiro T. Jun 01 '13 at 21:07
  • As I said: You can use memcache, but it expires. And if the get_serving_url is an option, you do not have to save the image. – voscausa Jun 01 '13 at 23:03