pythongoogle-app-enginedictionarycheetah

AppEngine database model has has_key() method but is not iterable?


I am getting: argument of type 'Lantern' is not iterable in one of the template engine files (Cheetah). As you can guess the obj is a Lantern (see below).

NameWrapper.py:

if hasattr(obj, 'has_key') and key in obj:

This is a simplified version of my models. Nothing fancy, no additional methods just attribute declarations.

models.py:

from google.appengine.ext import db

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

class Lantern(Product):
    height = db.IntegerProperty()
  1. How can I solve this issue?
  2. Is it correct, that AppEngine models have a function has_key but are not iterable?

Solution(edit):

I have replaced the line.

if hasattr(obj, 'has_key') and isinstance(obj, collections.Iterable) and key in obj:

Solution

  • The NameMapper implementation makes the erroneous assumption that having a has_key() method makes the Model class a mapping and tries to test for key membership.

    This is a bug in the Cheetah NameMapper implementation and should be reported to the project. You could try to disable the NameMapper functionality, the documentation suggests it is optional and can be switched of with the useNameMapper compiler setting. I'm not familiar with the syntax, but try to avoid relying on the functionality in your templates.

    If you are not averse to editing the Cheetah code, you could replace the tests with:

    from collections import Mapping
    
    if isinstance(obj, Mapping) and key in obj:
    

    which uses the correct Abstract Base Class to detect a mapping object.

    Model objects are not mappings. The Model.has_key() function does not test for the presence of a mapping key, it is a method that tests if the object has a datastore key.

    The documentation string on the method is:

    def has_key(self):
        """Determine if this model instance has a complete key.
    
        When not using a fully self-assigned Key, ids are not assigned until the
        data is saved to the Datastore, but instances with a key name always have
        a full key.
    
        Returns:
          True if the object has been persisted to the datastore or has a key
          or has a key_name, otherwise False.
        """
    

    Note that the above method does not take an argument, apart from the automatically-bound self.

    Model.has_key() appears to be a convenience method that Google didn't include in the Model Class documentation; it'll return False when the Model.key() method would throw the NotSavedError exception instead.

    In any case, Model objects are not sequences; they have no __iter__ method, nor do they have a length or support indexing. As such they are not iterable. Having an has_key() method does not imply that they should be.