Mutually referencing class deadlock - python

I have two SQLAlchemy class declarations that refer to each other so the first one gives an error because the second one is not yet declared.
The records in Key with key_type_id == 4 are references from one Entity to a parent Entity via the parent_entity_id.
To define the relationship in Entity that collects all the child entities I need to add a backref, but that references key which is declared later.
class Entity(db.Model):
__tablename__ = 'entity'
entity_id = db.Column(db.INTEGER, primary_key=True)
...
children = db.relationship(
'Entity', secondary=key,
primaryjoin="and_(key.c.entity_id == entity_id, "
"key.c.key_type_id == 4)",
secondaryjoin=(key.c.parent_entity_id == entity_id),
backref=db.backref('key', lazy='dynamic'), lazy='dynamic')
class Key(db.Model):
__tablename__ = 'key'
...
entity_id = db.Column(db.ForeignKey('entity.entity_id'), nullable=False,
index=True)
...
key_type_id = db.Column(db.ForeignKey('key_type.key_type_id'), index=True)
# 4 for a foreign key
...
parent_entity_id = db.Column(db.INTEGER, index=True)
...
The error traceback is.
ipdb> Traceback (most recent call last):
File "<ipython-input-1-a3063c2d9856>", line 1, in <module>
debugfile('C:/Users/Mark Kortink/Dropbox/Python/projects/metapplica/_dev/Scraps/ooClass2DBs.py', wdir='C:/Users/Mark Kortink/Dropbox/Python/projects/metapplica/_dev/Scraps')
File "C:\ProgramData\Anaconda3\lib\site-packages\spyder_kernels\customize\spydercustomize.py", line 856, in debugfile
debugger.run("runfile(%r, args=%r, wdir=%r)" % (filename, args, wdir))
File "C:\ProgramData\Anaconda3\lib\bdb.py", line 585, in run
exec(cmd, globals, locals)
File "<string>", line 1, in <module>
File "C:\ProgramData\Anaconda3\lib\site-packages\spyder_kernels\customize\spydercustomize.py", line 827, in runfile
execfile(filename, namespace)
File "C:\ProgramData\Anaconda3\lib\site-packages\spyder_kernels\customize\spydercustomize.py", line 110, in execfile
exec(compile(f.read(), filename, 'exec'), namespace)
File "c:/users/mark kortink/dropbox/python/projects/metapplica/_dev/scraps/ooclass2dbs.py", line 207, in <module>
class Entity(db.Model):
File "c:/users/mark kortink/dropbox/python/projects/metapplica/_dev/scraps/ooclass2dbs.py", line 254, in Entity
'Entity', secondary=key,
NameError: name 'key' is not defined
First am i declaring the backref relationship correctly?
Second how do I break the deadlock?

The name key doesn't exist in the module you are using. Even if you define your Key class first, there would never actually be a reference to the Table object named key in the scope of your module. In recognition of this, you can pass the name of the table as a string to the arguments of relationship() that would otherwise require a reference to that name in your module scope.
From the docs:
When using the Declarative extension, the Declarative initializer
allows string arguments to be passed to relationship(). These string
arguments are converted into callables that evaluate the string as
Python code, using the Declarative class-registry as a namespace. This
allows the lookup of related classes to be automatic via their string
name, and removes the need for related classes to be imported into the
local module space before the dependent classes have been declared.
That means secondary=key, can be declared as secondary="key". SQLAlchemy will then only try to resolve the string "key" to the object that it references after all of the classes have been mapped and the table will exist in the internal registry that SQLAlchemy uses to track such objects. Similarly, secondaryjoin=(key.c.parent_entity_id == entity_id) can be defined as secondaryjoin="key.c.parent_entity_id == entity_id".

Related

UnboundLocalError initializing a python class

I am creating a Python class but it seems I can't get the constructor class to work properly. Here is my class:
class IQM_Prep(SBconcat):
def __init__(self,project_dir):
self.project_dir=project_dir #path to parent project dir
self.models_path=self.__get_models_path__() #path to parent models dir
self.experiments_path=self.__get_experiments_path__() #path to parent experiemnts dir
def __get_models_path__(self):
for i in os.listdir(self.project_dir):
if i=='models':
models_path=os.path.join(self.project_dir,i)
return models_path
def __get_experiments_path__(self):
for i in os.listdir(self.project_dir):
if i == 'experiments':
experiments_path= os.path.join(self.project_dir,i)
return experiments
When I initialize this class:
project_dir='D:\\MPhil\\Model_Building\\Models\\TGFB\\Vilar2006\\SBML_sh_ver\\vilar2006_SBSH_test7\\Python_project'
IQM= Modelling_Tools.IQM_Prep(project_dir)
I get the following error:
Traceback (most recent call last):
File "<ipython-input-49-7c46385755ce>", line 1, in <module>
runfile('D:/MPhil/Python/My_Python_Modules/Modelling_Tools/Modelling_Tools.py', wdir='D:/MPhil/Python/My_Python_Modules/Modelling_Tools')
File "C:\Anaconda1\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 585, in runfile
execfile(filename, namespace)
File "D:/MPhil/Python/My_Python_Modules/Modelling_Tools/Modelling_Tools.py", line 1655, in <module>
import test
File "test.py", line 19, in <module>
print parameter_file
File "Modelling_Tools.py", line 1536, in __init__
self.models_path=self.__get_models_path__() #path to parent models dir
File "Modelling_Tools.py", line 1543, in __get_models_path__
return models_path
UnboundLocalError: local variable 'models_path' referenced before assignment
Modelling_Tools is the name of my custom module.
Based on the traceback, it seems that either:
def __get_models_path__(self):
for i in os.listdir(self.project_dir): # 1. this never loops; or
if i=='models': # 2. this never evaluates True
models_path=os.path.join(self.project_dir,i) # hence this never happens
return models_path # and this causes an error
You should review the result of os.listdir(self.project_dir) to find out why; either the directory is empty or nothing in it is named models. You could initialise e.g. models_path = None at the start of the method, but that would just hide the problem until later.
Sidenote: per my comments, you should check out the style guide, particularly on naming conventions for methods...
models_path is initialized only when:
self.project_dir has some files/dirs and
one of this file/dir has name models
If one of this condition is not fullfiled, then models_path is not initialized.

Why does my use of NDB's `populate()` not accept `id` or `parent`, but only `key`?

I want to create an entity object and after its construction, before writing it into the datastore, I want to set parent and id.
According to App Engine docs, the constructor accepts these keyword arguments:
- id
- key
- parent
You cannot easily define a property named "key", "id", "parent", or
"namespace". If you pass, for example, key="foo" in a constructor or
populate() call, it sets the entity's key, not a property attribute
named "key".
For populate(), it says it would accept the same keyword arguments as the constructor. However, it seems I'm doing something wrong, because the only keyword argument that works is key. Using id and/or parent gives me errors.
class Foo(ndb.Model):
pass
foo = Foo()
foo.populate(key=ndb.Key('Bar', 1, 'Foo', 123))
foo.key == ndb.Key('Bar', 1, 'Foo', 123) # True
When instead I use the keyword parent...
f.populate(parent=ndb.Key('Bar', 1))
...I get this traceback:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/ndb/model.py", line 2960, in _populate
self._set_attributes(kwds)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/ndb/model.py", line 2970, in _set_attributes
prop = getattr(cls, name) # Raises AttributeError for unknown properties.
AttributeError: type object 'Foo' has no attribute 'parent'
or sometimes this (not sure what makes the difference):
File "<console>", line 1, in <module>
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/ndb/model.py", line 2960, in _populate
self._set_attributes(kwds)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/ndb/model.py", line 2972, in _set_attributes
raise TypeError('Cannot set non-property %s' % name)
TypeError: Cannot set non-property parent
If I use id...
f.populate(id=123)
I get again an attribute error:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/ndb/model.py", line 2960, in _populate
self._set_attributes(kwds)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/ndb/model.py", line 2970, in _set_attributes
prop = getattr(cls, name) # Raises AttributeError for unknown properties.
AttributeError: type object 'Foo' has no attribute 'id'
Shouldn't all of my populate() examples above work with any of the keyword arguments?
I know, I could only use key to achieve the same as with parent and id together, but I would like to know what I'm missing here.
parent is a property of a Key when using ancestor paths. The constructor accepts it as a convenience but since it is not its own property, populate() will complain that it does not exist. Same goes for id. The constructor uses id to construct a Key using _get_kind() and the value of id.
An example is worth 1000 comments. See how id and parent are used to construct a key
>>> from google.appengine.ext import ndb
>>>>
>>> class Foo(ndb.Model):
... pass
...
>>> foo = Foo(id=123, parent=ndb.Key('Bar', 1))
>>> foo.key
Key('Bar', 1, 'Foo', 123)
>>> foo.id
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'id'
>>> foo.parent
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'parent'
The docs may not be clear enough on this issue, but you're only supposed to use populate to update properties of the model (actual data).
This is clear looking at the source code, for example this line of the model constructor:
Note: you cannot define a property named key; the .key attribute always refers to the entity's key. But you can define properties named id or parent. Values for the latter cannot be passed through the constructor, but can be assigned to entity attributes after the entity has been created.
Suggesting we could use id and parent as properties/attributes, thus populate calls will try to set them.
It gets even clearer once we get to the populate implementation, where the inline documentation has a provision for your exact question:
Each keyword argument will be used to set a corresponding property. Keywords must refer to valid property name. This is similar to passing keyword arguments to the Model constructor, except that no provisions for key, id or parent are made.
Maybe the docs should be updated with this information to avoid the confusion. I never ran into this issue myself, maybe because I've been following the recommendation of "only setting the key when instantiating a model", however I can't find a quote for this statement; I take it as a rule of thumb, and am under the impression that trying to assign it afterwards should raise exceptions everywhere.
And as if the previous references weren't enough, look at this line in the constructor:
self._key = _validate_key(key, entity=self)
You won't find that anywhere else, so this is the only instance of a key being assigned [properly] to a model (as you can imagine, populate only iterates and sets values).

Modifying django's default "_id" suffix for related objects

I am integrating django with a legacy system and database and have a model that looks like this
class Label(models.Model)
iLabelID = models.IntegerField(db_column='LABELID', primary_key=True)
organization = models.OneToOneField(Organization, related_name='labels', db_column='ORGANIZATION')
sLabelText = models.CharField(max_length=42)
Using this notation (more or less hungarian notation) is a requirement of the project.
The following will work in django:
>>> Label.objects.get(organization_id=1)
But I want to be able to write this:
>>> Label.objects.get(iOrganizationID=1)
I tried subclassing models.OneToOneField with
class MyOneToOneField(models.OneToOneField):
def get_attname(self):
# default is:
# return '%s_id' % self.name
return 'i%s%sID' % (self.name[0].upper(), self.name[1:])
But this is the error I get when trying to use it:
>>> Label.objects.get(iOrganizationID=1)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "django/db/models/manager.py", line 151, in get
return self.get_queryset().get(*args, **kwargs)
File "django/db/models/query.py", line 301, in get
num = len(clone)
File "django/db/models/query.py", line 77, in __len__
self._fetch_all()
File "django/db/models/query.py", line 854, in _fetch_all
self._result_cache = list(self.iterator())
File "django/db/models/query.py", line 230, in iterator
obj = model(*row_data)
File "django/db/models/base.py", line 347, in __init__
setattr(self, field.attname, val)
AttributeError: can't set attribute
EDIT: here's another pain point:
I wish to generate some JSON. This JSON will be fed to another part of the system on which I have no control (not possible to change names). I wish I could do the following:
json.dumps(list(Label.objects.values('iLabelID', 'iOrganizationID', 'sAnotherValue')))
But this is not possible. I have to do this
list(Label.objects.values('iLabelID', 'organization_id', 'sAnotherValue'))
And then manually map organization_id to iOrganizationID, although this is not a problem for the label's ID. It makes code more difficult to maintain, to read, and slower to execute.
Note that this is not specific to hungarian notation, you may need to suffix with _identifier or _pk or whatever instead of _id.
EDIT2: I must have made another error because as lanzz pointed out get_attname does work -_-
I solved it using some modifications of your code:
class CustomAttrNameForeignKey(djangoFields.related.ForeignKey):
def __init__(self, *args, **kwargs):
attname = kwargs.pop('attrname', None)
super(CustomAttrNameForeignKey, self).__init__(*args, **kwargs)
self.attname = attname or super(CustomAttrNameForeignKey, self).get_attname()
def get_attname(self):
return self.attname
class AModelUsingThis(djangoModels.Model):
snapshot = CustomAttrNameForeignKey(
ParentalModel, db_column="timestamp", to_field="timestamp",
attrname='timestamp', related_name="children")
In this case we get in the model an attribute for FK with no suffix at all, but with the name given. I haven't tested it yet on DB, but just tried to instantiate model also with this custom FK given -- it works fine.

'RelationshipProperty' object has no attribute 'c' error in sqlalchemy

I'm having a lot of trouble implementing SQLalchemy for a legacy MSSQL database. It is a big existing database, so I wanted to use sqlautocode to generate the files for me, because using autoload to reflect the database takes too long.
The first problem was that sqlautocode no longer seems to work on SQLalchemy 0.8. I did still have existing output from an earlier version, so I thought I'd use that, just to test with.
Now sqlautocode outputs the 'classical mapping', which is not really a problem, but whenever I tried to use a foreign key, 'RelationshipProperty' object has no attribute 'c' would show up. An error somewhere deep inside the SQLalchemy library.
So next, I tried just skipping sqlautocode and writing the classes and relations myself, going by this code for SQLalchemy 0.8. I used two example tables and I got the exact same error. Then I commented out most of the columns, all of the relations and I -STILL- get the error.
Below is my code:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship, backref
from sqlalchemy.dialects.mssql import *
Base = declarative_base()
class PeopleMemberhip(Base):
__tablename__ = 'people_memberships'
ppl_mshp_id = Column(VARCHAR(length=36), primary_key=True, nullable=False)
ppl_mshp_startdate = Column(DATETIME())
ppl_mshp_enddate = Column(DATETIME())
# ppl_mshp_pmsd_id = Column(VARCHAR(length=36), ForeignKey('paymentschedules.pmsd_id'))
# paymentschedule = relationship("PaymentSchedule", backref=backref('people_memberships'))
def __repr__(self):
return "<people_membership('%s','%s')>" % (self.ppl_mshp_id, self.ppl_mshp_startdate)
class PaymentSchedule(Base):
__tablename__ = 'paymentschedules'
pmsd_id = Column(VARCHAR(length=36), primary_key=True, nullable=False)
pmsd_name = Column(NVARCHAR(length=60))
pmsd_startdate = Column(DATETIME())
pmsd_enddate = Column(DATETIME())
# paymentschedule = relationship("PaymentSchedule", backref=backref('people_memberships'))
def __repr__(self):
return "<paymentschedule('%s','%s')>" % (self.pmsd_id, self.pmsd_name)
And the resulting Error:
Traceback (most recent call last):
File "C:\Program Files (x86)\JetBrains\PyCharm 2.7\helpers\pydev\pydevd.py", line 1472, in <module>
debugger.run(setup['file'], None, None)
File "C:\Program Files (x86)\JetBrains\PyCharm 2.7\helpers\pydev\pydevd.py", line 1116, in run
pydev_imports.execfile(file, globals, locals) #execute the script
File "C:/Users/erik/workspace/flasktest/test.py", line 16, in <module>
contract = db.session.query(PeopleMemberhip).filter_by(ppl_mshp_id='98ABD7E9-4CFF-4F7B-8537-8E46FD5C79D5').one()
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\scoping.py", line 149, in do
return getattr(self.registry(), name)(*args, **kwargs)
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\session.py", line 1105, in query
return self._query_cls(entities, self, **kwargs)
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\query.py", line 115, in __init__
self._set_entities(entities)
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\query.py", line 124, in _set_entities
self._set_entity_selectables(self._entities)
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\query.py", line 157, in _set_entity_selectables
ent.setup_entity(*d[entity])
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\query.py", line 2744, in setup_entity
self._with_polymorphic = ext_info.with_polymorphic_mappers
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\util\langhelpers.py", line 582, in __get__
obj.__dict__[self.__name__] = result = self.fget(obj)
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\mapper.py", line 1425, in _with_polymorphic_mappers
configure_mappers()
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\mapper.py", line 2106, in configure_mappers
mapper._post_configure_properties()
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\mapper.py", line 1242, in _post_configure_properties
prop.init()
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\interfaces.py", line 231, in init
self.do_init()
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\properties.py", line 1028, in do_init
self._setup_join_conditions()
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\properties.py", line 1102, in _setup_join_conditions
can_be_synced_fn=self._columns_are_mapped
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\relationships.py", line 115, in __init__
self._annotate_fks()
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\relationships.py", line 311, in _annotate_fks
self._annotate_present_fks()
File "C:\Users\erik\workspace\flasktest\lib\site-packages\sqlalchemy\orm\relationships.py", line 331, in _annotate_present_fks
secondarycols = util.column_set(self.secondary.c)
AttributeError: 'RelationshipProperty' object has no attribute 'c'
I'm really at a loss, any help with this error is appreciated, but also if someone can suggest a different approach that can make SQLalchemy work with our legacy MSSQL database it is also a solution.
As I said, sqlautocode doesn't seem to work any more, but maybe I'm using it the wrong way, or maybe there is an alternative tool I don't know about.
Erik
Okay guys, figured it out myself.
I had a couple of files I was messing with that had table definitions in them (output from sqlautocode). One was called 'database.py' another 'model.py' and the last one 'ORM.py'.
I had a test.py file that imported 'model.py'. Model.py was the file I had written my table definitions in. However - the test.py page was also importing the database from within Flask (from app import app, db), and in the __init__() function of the Flask app, Flask was still loading 'ORM.py'.
So some of the objects were coming from ORM.py, which was an old file generated by sqlautocode, instead of from model.py, which I was experimenting with.
Renaming ORM.py gave me a clue. I have written a very simple script in Python that traverses through the MSSQL tables and columns and generates a model.py for me. I exclusively load that model.py file now and the whole thing works!
Sorry if anyone was spending time on this. Hope it helps somebody Googleing for the same problem though.
Erik

BadValueError: Property X is required

I've added a list property to an entity model with a large number of existing instances.
class MyModel(db.Model):
new_property = db.ListProperty(item_type=str, default=None)
Upon deployment to the live environment the app runs without issues for a short time and then starts throwing BadValueError error's as it tries to retrieve records from the datastore.
The code throwing the error is just a straight call to the datastore:
app_item = db.get(app_item_key)
I'm using 1.7.5. of the Python 2.7 runtime.
Any ideas on what I can do to prevent this, or at least trap it so that I can get data from the store?
Traceback (most recent call last):
File "/base/data/home/apps/app/4-15.365909351579418812/app.py", line 1739, in app_get
app_item = db.get(app_item_key)
File "/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 1533, in get
return get_async(keys, **kwargs).get_result()
File "/python27_runtime/python27_lib/versions/1/google/appengine/api/apiproxy_stub_map.py", line 604, in get_result
return self.__get_result_hook(self)
File "/python27_runtime/python27_lib/versions/1/google/appengine/datastore/datastore_rpc.py", line 1459, in __get_hook
entities = rpc.user_data(entities)
File "/python27_runtime/python27_lib/versions/1/google/appengine/api/datastore.py", line 600, in local_extra_hook
return extra_hook(result)
File "/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 1503, in extra_hook
model = cls1.from_entity(entity)
File "/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 1438, in from_entity
return cls(None, _from_entity=entity, **entity_values)
File "/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 970, in __init__
prop.__set__(self, value)
File "/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 614, in __set__
value = self.validate(value)
File "/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 3460, in validate
value = super(ListProperty, self).validate(value)
File "/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 641, in validate
raise BadValueError('Property %s is required' % self.name)
BadValueError: Property new_property is required
For those that follow:
As per Aaron D's suggestion, changing the default value to an empty list resolved this issue, so:
new_property = db.ListProperty(item_type=str, default=None)
Should read:
new_property = db.ListProperty(item_type=str, default=[])
Looking at the source code of the Google App Engine in the __init__.py referenced in your traceback, you can see a comment in the ListProperty doc comments (line 3428) that says:
Note that the only permissible value for 'required' is True.
So, even though you are not providing it, it looks like line 3442 is setting it automatically:
self._require_parameter(kwds, 'required', True)
If you look further into the source code (line 3500), you can see the definition of empty() for a ListProperty:
def empty(self, value):
"""Is list property empty.
[] is not an empty value.
Returns:
True if value is None, else false.
"""
return value is None
I could think of two issues that might cause error but I haven't verified through testing.
1) If for some reason, you already have data in that field (perhaps you are reusing the new_property name?) and it was empty, then it seems likely to generate the error you have. I am not sure how to fix this problem, except to suggest that you use a unique name for your new_property instead. The post I referenced in my comment explains how to "fix" the data.
2) Since you already have records, your code is trying to populate those using your default value of None, which matches the empty() test and then throws the exception. In that case, if you just provide a default value of [] instead, it should work.
I am pretty sure you example code here is not what you are using. I would bet you have required=True in the new property. You are then retrieving an old record which doesn't have a value for the required property. Just dropping 'required=True` will make those errors go away. If you need to have that value required then you need to add the default value to the field before enforcing the constraint.
* removed some complete garbage about None not being a valid default value for ListProperty *
So I tried to replicate the situation based on the information you have supplied
and I have the answer. I can generate the problem by first creating a model that has a name new_property of type StringProperty with a default of None. put() the record with no value for new_property getting the default of None written, then change the model definition of new_property toListProperty`, and the fetch the record. We get the same stack trace. See shell log below.
s~lightning-catfish> class MyModel(db.Model):
... pass
...
s~lightning-catfish> x = MyModel()
s~lightning-catfish> x.put()
datastore_types.Key.from_path(u'MyModel', 1001L, _app=u's~lightning-catfish')
s~lightning-catfish> class MyModel(db.Model):
... new_property = db.ListProperty(item_type=str,default=None)
...
s~lightning-catfish> y = db.get(x.key())
s~lightning-catfish> y
<MyModel object at 0x9e09dcc>
s~lightning-catfish> y.new_property
[]
s~lightning-catfish> new_property = db.StringProperty(defaul
KeyboardInterrupt
s~lightning-catfish> class MyModel(db.Model):
... new_property = db.StringProperty(default=None)
...
s~lightning-catfish> z = MyModel()
s~lightning-catfish> z.put()
datastore_types.Key.from_path(u'MyModel', 2001L, _app=u's~lightning-catfish')
s~lightning-catfish> class MyModel(db.Model):
... new_property = db.ListProperty(item_type=str,default=None)
...
s~lightning-catfish> a1 = db.get(z.key())
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/timh/google_appengine/google/appengine/ext/db/__init__.py", line 1533, in get
return get_async(keys, **kwargs).get_result()
File "/home/timh/google_appengine/google/appengine/api/apiproxy_stub_map.py", line 604, in get_result
return self.__get_result_hook(self)
File "/home/timh/google_appengine/google/appengine/datastore/datastore_rpc.py", line 1459, in __get_hook
entities = rpc.user_data(entities)
File "/home/timh/google_appengine/google/appengine/api/datastore.py", line 600, in local_extra_hook
return extra_hook(result)
File "/home/timh/google_appengine/google/appengine/ext/db/__init__.py", line 1503, in extra_hook
model = cls1.from_entity(entity)
File "/home/timh/google_appengine/google/appengine/ext/db/__init__.py", line 1438, in from_entity
return cls(None, _from_entity=entity, **entity_values)
File "/home/timh/google_appengine/google/appengine/ext/db/__init__.py", line 970, in __init__
prop.__set__(self, value)
File "/home/timh/google_appengine/google/appengine/ext/db/__init__.py", line 614, in __set__
value = self.validate(value)
File "/home/timh/google_appengine/google/appengine/ext/db/__init__.py", line 3460, in validate
value = super(ListProperty, self).validate(value)
File "/home/timh/google_appengine/google/appengine/ext/db/__init__.py", line 641, in validate
raise BadValueError('Property %s is required' % self.name)
BadValueError: Property new_property is required
s~lightning-catfish>
To fix the data you will need to access it at a low level and change the data types stored in the record.
I have code for fetching and putting entities without using models if you want it.
* last thing you should try *
Try using the following code or something like it to fetch objects without using the model.
You get the underlying data back, with types etc.. in dicts. That will show you what is in the datastore.
from google.appengine.api import datastore
from google.appengine.api import datastore_errors
def get_entities(keys):
rpc = datastore.GetRpcFromKwargs({})
keys, multiple = datastore.NormalizeAndTypeCheckKeys(keys)
entities = None
try:
entities = datastore.Get(keys, rpc=rpc)
except datastore_errors.EntityNotFoundError:
assert not multiple
return entities
x = get_entities([some_key])

Categories