1

This is a follow up question for Django on Google App Engine: cannot upload images

I got part of the upload of images to GAE Blobstore working. Here's what I did:

In models.py I created a model PhotoFeature:

class PhotoFeature(models.Model):
    property = models.ForeignKey(
        Property,
        related_name = "photo_features"
    )
    caption = models.CharField(
        max_length = 100
    )
    blob_key = models.CharField(
        max_length = 100
    )

In admin.py I created an admin entry with an override for the rendering of the change_form to allow for insert of the correct action to the Blobstore upload url:

class PhotoFeatureAdmin(admin.ModelAdmin):
    list_display = ("property", "caption")
    form = PhotoFeatureForm

    def render_change_form(self, request, context, *args, **kwargs):
        from google.appengine.ext import blobstore
        if kwargs.has_key("add"):
            context['blobstore_url'] = blobstore.create_upload_url('/admin/add-photo-feature')
        else:
            context['blobstore_url'] = blobstore.create_upload_url('/admin/update-photo-feature')
        return super(PhotoFeatureAdmin, self).render_change_form(request, context, args, kwargs)

As I use standard Django, I want to use the Django views to process the result once GAE has updated the BlobStore in stead of BlobstoreUploadHandler. I created the following views (as per the render_change_form method) and updated urls.py:

def add_photo_feature(request):

def update_photo_feature(request):

This all works nicely but once I get into the view method I'm a bit lost. How do I get the Blob key from the request object so I can store it with PhotoFeature? I use standard Django, not Django non-rel. I found this related question but it appears not to contain a solution. I also inspected the request object which gets passed into the view but could not find anything relating to the blob key.

EDIT:

The Django request object contains a FILES dictionary which will give me an instance of InMemoryUploadedFile. I presume that somehow I should be able to retrieve the blob key from that...

EDIT 2:

Just to be clear: the uploaded photo appears in the Blobstore; that part works. It's just getting the key back from the Blobstore that's missing here.

EDIT 3:

As per Daniel's suggestion I added storage.py from the djangoappengine project which contains the suggested upload handler and added it to my SETTINGS.PY. This results in the following exception when trying to upload:

'BlobstoreFileUploadHandler' object has no attribute 'content_type_extra'
Community
  • 1
  • 1
Roger
  • 4,737
  • 4
  • 43
  • 68
  • Did you go through the first tutorial on GAE? https://developers.google.com/appengine/docs/python/gettingstartedpython27/ – Lipis Apr 16 '13 at 11:49
  • Yes. AFAIK it does not cover the problem I run into with the above question. – Roger Apr 16 '13 at 12:07
  • No it's not covering exactly what you are asking for.. but it covers some very basics on how GAE works regarding the models and what kind of properties you should use! From what I understand you are trying to port a Django app into App Engine.. so in order to do that you have to use for example the GAE properties https://developers.google.com/appengine/docs/python/ndb/properties#types – Lipis Apr 16 '13 at 12:16
  • @Lipis your comments have nothing whatsoever to do with the question. It is perfectly possible to use Django fields on GAE, assuming you are using Cloud SQL. Please concentrate on the actual problem. – Daniel Roseman Apr 16 '13 at 12:20
  • @DanielRoseman My bad.. didn't pay much attention.. and assumed whatever I wanted to assume.. I will just shut up now :) – Lipis Apr 16 '13 at 12:52
  • @Lipis It's not a port. It's a Django app (plain, not non-rel) using Google Cloud SQL. The upload to Blobstore works (local and in production). There's one piece missing which is what my question is about... – Roger Apr 16 '13 at 12:54

1 Answers1

2

This is really tricky to fix. The best solution I have found is to use the file upload handler from the djangoappengine project (which is associated with django-nonrel, but does not depend on it). That should handle the required logic to put the blob key into request.FILES, as you'd expect in Django.

Edit

I'd forgotten that django-nonrel uses a patched version of Django, and one of the patches is here to add the content-type-extra field. You can replicate the functionality by subclassing the upload handler as follows:

from djangoappengine import storage
class BlobstoreFileUploadHandler(storage.BlobstoreFileUploadHandler):
    """Handler that adds blob key info to the file object."""

    def new_file(self, field_name, *args, **kwargs):
      # We need to re-process the POST data to get the blobkey info.
      meta = self.request.META
      meta['wsgi.input'].seek(0)
      fields = cgi.FieldStorage(meta['wsgi.input'], environ=meta)
      if field_name in fields:
          current_field = fields[field_name]
          self.content_type_extra = current_field.type_options
      super(BlobstoreFileUploadHandler, self).new_file(field_name,
                                                       *args, **kwargs)

and reference this subclass in your settings.py rather than the original.

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • Thanks. I had a look at the file upload handler but wasn't sure how to integrate it into the Django views. Any ideas on this? – Roger Apr 16 '13 at 13:32
  • You don't need to integrate it into the view, it works as one of the pluggable upload handlers - just add it to settings.FILE_UPLOAD_HANDLERS. – Daniel Roseman Apr 16 '13 at 13:39
  • When I add the handler it throws an exception (see my edit 3 in the OP). I found no information on this. Any ideas? – Roger Apr 16 '13 at 15:25