views:

20

answers:

1

I have a Model in an existing Datastore. Which looks like this:

class SomeKind(db.Model):
  name = db.StringProperty(required=True)
  someField = db.BlobProperty(required=True)

There are about 20000+ entities, of this kind, in the datastore Now I want to restructure the kind and make it this way:

class SomeKind(db.Model):
  name = db.StringProperty(required=True)
  someField = db.StringProperty(required=True)

I think I have to

  1. Cycle through the datastore to remove existing "someField" data. from models.py
  2. Remove the property
  3. Add the property, with new definition, to models.py

I'm stucked at (1) where I want to delete existing attribute with remote API:

import sys, time, urllib2

sys.path.append("gae/paths")
...
sys.path.append("myapp/path")
from google.appengine.ext import db
from google.appengine.api import memcache
from google.appengine.ext.remote_api import remote_api_stub
from models import *

def tryPut(db, set, tryLimit=10, seconds=5, trying=1):
  try:
    db.put(set)
    return True
  except urllib2.HTTPError:
    if trying <= tryLimit:
      print "retry (%d of %d) in 5 seconds" % (trying, tryLimit)
      time.sleep(5)
      tryPut(db, set, seconds, trying+1)
    else:
      print urllib2.HTTPError
      sys.exit()

def main():

    remote_api_stub.ConfigureRemoteDatastore(None, 
      '/remote_api', my_auth_func, 'myapp.appspot.com')
    q = db.GqlQuery("SELECT * FROM SomeKind")
    last_cursor = memcache.get('SomeKind/update')

    if last_cursor:
        q.with_cursor(last_cursor)

    set = q.fetch(100)

    while len(set) != 0:
      for someKind in set:
        print someKind.name

        # this doesn't work
        delattr(someKind, "someField")

        # this doesn't work either
        del someKind.someField

      print "update to Google"
      if tryPut(db, set):
        cursor = q.cursor()
        memcache.set('SomeKind/update', cursor)

if __name__ == "__main__":
  main()

I run this on my machine. The problem is this script, with either method, always raises error:

Traceback (most recent call last):
File "./query.py", line 91, in <module>
  main()
File "./query.py", line 66, in main
  del someKind.someField
AttributeError: __delete__

The documentation ( http://code.google.com/intl/en/appengine/articles/update_schema.html ) said something like "use delattr to delete the obsolete property and then save the entity". But there is no example what so ever.

How can I do this? Was my steps correct? How am I to delete the property?

+1  A: 

You can't delete properties from a Model - every model instance has the same set of properties. Expando, however, allows you to have dynamic properties.

The easiest path is probably this:

  1. Change your model class to extend db.Expando instead of db.Model
  2. Add the new property to the model class (use the 'name' keyword argument if you want to give it a different name in the datastore to what you access it with in Python) and remove the old one.
  3. Use the mapreduce API to iterate over every entity, calling "del mymodel.oldprop", and setting the new property as appropriate.
  4. Update the model definition again, setting it back to extending db.Model.
Nick Johnson
Sorry. I've updated the snippet now.The old field is binary, that's why its hard to migrate.
Koala Yeung
Updated my post with more specific instructions.
Nick Johnson