So I've been preparing my Google AppEngine talk for PyConUK, and have
struggled with one of my demos because of what appears to be a bug in
the AppEngine to Django conversion code.

I want the facility to upload a file, storing it into a Blob field on
the datastore.

My model looks like

class Kitten(db.Model):
  index = db.IntegerProperty(default=0)
  name = db.StringProperty(required=True)
  picture = db.BlobProperty()
  votes = db.IntegerProperty(default=0)

Then we create a form called that we can use to hold that data,

class KittenForm(djangoforms.ModelForm):
    class Meta:
      model = Kitten
      exclude = [ 'index' ]

In our add method, we attempt to load the form, and validate it if we
have been posted back,

if request.method == "POST":
    # Handle form submission
    form = KittenForm(request.POST, request.FILES)
    if (form.is_valid()):
      # Do stuff
  else:
    form = KittenForm()
  return render_to_response('addkitten.html', {'form': form})


At first we just don't get a file upload button in our form, this is
because Django 0.97.1 doesn't support FileFields properly, so AppEngine
wont map a Blob to one unless you upgrade to django 1.0

Once that's done, and we're running AppEngine from subversion as well to
get latest code fixes there. when we post to the method, we get an
attribute error, 

'NoneType' object has no attribute 'validate'

This appears to be caused by the clean_for_property_field calling
property_clean.

When the model is created, it sets up a set of property cleaners, one
for each field on the model.

The code to do so looks like:

in google/appengine/ext/db/djangoforms.py (line 703):
for name, field in model_fields.iteritems():
  prop = props.get(name)
  if prop:
    def clean_for_property_field(value, prop=prop, old_clean=field.clean):
      value = old_clean(value)
      property_clean(prop, value)
      return value
    field.clean = clean_for_property_field

According to my traceback, prop is equal to None when
clean_for_property_field is called back by django.

However, it looks to me like what has happened is that the code that
calls the above code has changed in how it calls it, passing in a second
parameter called initial, but only when it's a FileField.

django/forms/forms.py (line 220)
if isinstance(field, FileField):
    initial = self.initial.get(name, field.initial)
    value = field.clean(value, initial)
else:
    value = field.clean(value)

So I can see how I could potentially fix this, by adding an initial
field to the clean_for_property_field method in appengine's code, but
I'm unsure as to the ramifications of that.

Attempting it simply gives me a different error about how a Blob cannot
be converted from an InMemoryUploadedFile, but should be convertable
from a string.  Fixing that again might be easy, but it starts to feel
like I've missed something fundemental somewhere.

Any clues would be really nice about now.

Michael Brunton-Spall

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to