将djangotoolbox ListField保存到GAE数据存储区时出错

时间:2013-03-03 14:15:35

标签: django google-app-engine django-admin django-nonrel

在将ListField保存到GAE数据存储区时,我获得了属性标记的“不支持”类型:'
GAE数据存储区接收一个生成器对象以保存到数据库中:

name: 'tags'
value: <generator object <genexpr> at 0x049C6620>

dbindexer \ compiler.py或djangotoolbox \ db \ basecompiler.py可能是从传递给它的列表对象生成此对象。

(<djangotoolbox.fields.ListField object at 0x047A4070>, [1L])]

有关可能出现的问题的任何提示?

from django.db import models
from djangotoolbox.fields import ListField

class Tag(models.Model):
    name = models.CharField(max_length=255)

class Note(models.Model):
    tags = ListField(db.ForeignKey(Tag))

例外:

Django Version: 1.3.1
Exception Type: DatabaseError
Exception Value: Unsupported type for property tags: <type 'generator'>
Exception Location: google_appengine\google\appengine\api\datastore_types.py in ValidateProperty, line 1529

File "myapp\django\db\models\base.py" in save
  462. self.save_base(using=using, force_insert=force_insert, force_update=force_update)
File "myapp\django\db\models\base.py" in save_base
  573. result = manager._insert(values, return_id=update_pk, using=using)
File "myapp\django\db\models\manager.py" in _insert
  195. return insert_query(self.model, values, **kwargs)
File "myapp\django\db\models\query.py" in insert_query
  1438. return query.get_compiler(using=using).execute_sql(return_id)
File "myapp\dbindexer\compiler.py" in execute_sql
  38.  return super(SQLInsertCompiler, self).execute_sql(return_id=return_id)
File "myapp\djangotoolbox\db\basecompiler.py" in execute_sql
  549. key = self.insert(field_values, return_id=return_id)
File "myapp\djangoappengine\db\compiler.py" in _func
  68.   return func(*args, **kwargs)
File "myapp\djangoappengine\db\compiler.py" in insert
  387.  entity.update(properties)
File "google_appengine\google\appengine\api\datastore.py" in update
  890.  self.__setitem__(name, value)
File "google_appengine\google\appengine\api\datastore.py" in __setitem__
  868.  datastore_types.ValidateProperty(name, value)
File "google_appengine\google\appengine\api\datastore_types.py" in ValidateProperty
  1529. 'Unsupported type for property %s: %s' % (name, v.__class__))

生成器对象由djangotoolbox生成,但GAE不接受生成器类型。 代码来自djangotoolbox / db / base.py

   def _value_for_db_collection(self, value, field, field_kind, db_type,
                                 lookup):
      # Do convert filter parameters.
        if lookup:
            # Special case where we are looking for an empty list
            if lookup == 'exact' and db_type == 'list' and value == u'[]':
                return []
            value = self._value_for_db(value, subfield,
                                       subkind, db_subtype, lookup)

        # Convert list/set items or dict values.
        else:
            if field_kind == 'DictField':

                # Generator yielding pairs with converted values.
                value = (
                    (key, self._value_for_db(subvalue, subfield,
                                             subkind, db_subtype, lookup))
                    for key, subvalue in value.iteritems())

                # Return just a dict, a once-flattened list;
                if db_type == 'dict':
                    return dict(value)
                elif db_type == 'list':
                    return list(item for pair in value for item in pair)

            else:

                # Generator producing converted items.
                value = (
                    self._value_for_db(subvalue, subfield,
                                       subkind, db_subtype, lookup)
                    for subvalue in value)

                # "list" may be used for SetField.
                if db_type in 'list':
                    return list(value)
                elif db_type == 'set':
                    # assert field_kind != 'ListField'
                    return set(value)

            # Pickled formats may be used for all collection fields,
            # the fields "natural" type is serialized (something
            # concrete is needed, pickle can't handle generators :-)
            if db_type == 'bytes':
                return pickle.dumps(field._type(value), protocol=2)
            elif db_type == 'string':
                return pickle.dumps(field._type(value))

        # If nothing matched, pass the generator to the back-end.
        return value

尝试验证上述生成器并抛出错误的AppEngine代码:

def ValidateProperty(name, values, read_only=False):
  ValidateString(name, 'property name', datastore_errors.BadPropertyError)
  values_type = type(values)

  if values_type is tuple:
    raise datastore_errors.BadValueError('May not use tuple property value; property %s is %s.' %(name, repr(values)))

  **if values_type is not list:
    values = [values]**

  if not values:
    raise datastore_errors.BadValueError(
        'May not use the empty list as a property value; property %s is %s.' %
        (name, repr(values)))
  try:
    for v in values:
      prop_validator = _VALIDATE_PROPERTY_VALUES.get(v.__class__)
      if prop_validator is None:
        raise datastore_errors.BadValueError(
          'Unsupported type for property %s: %s' % (name, v.__class__))
      prop_validator(name, v)

  except (KeyError, ValueError, TypeError, IndexError, AttributeError), msg:
    raise datastore_errors.BadValueError(
      'Error type checking values for property %s: %s' % (name, msg))

1 个答案:

答案 0 :(得分:0)

对以下代码进行了更改(**新代码标记为**)并且问题似乎已修复(可能我做错了需要此修复:

def _value_for_db_collection(self, value, field, field_kind, db_type,
                                 lookup):
    subfield, subkind, db_subtype = self._convert_as(field.item_field,
                                                     lookup)

    # Do convert filter parameters.
    if lookup:
        # Special case where we are looking for an empty list
        if lookup == 'exact' and db_type == 'list' and value == u'[]':
            return []
        value = self._value_for_db(value, subfield,
                                   subkind, db_subtype, lookup)

    # Convert list/set items or dict values.
    else:
        if field_kind == 'DictField':

            # Generator yielding pairs with converted values.
            value = (
                (key, self._value_for_db(subvalue, subfield,
                                         subkind, db_subtype, lookup))
                for key, subvalue in value.iteritems())

            # Return just a dict, a once-flattened list;
            if db_type == 'dict':
                return dict(value)
            elif db_type == 'list':
                return list(item for pair in value for item in pair)

        else:

            # Generator producing converted items.
            value = (
                self._value_for_db(subvalue, subfield,
                                   subkind, db_subtype, lookup)
                for subvalue in value)

            # "list" may be used for SetField.
            if db_type in 'list':
                return list(value)
            elif db_type == 'set':
                # assert field_kind != 'ListField'
                return set(value)

        # Pickled formats may be used for all collection fields,
        # the fields "natural" type is serialized (something
        # concrete is needed, pickle can't handle generators :-)
        if db_type == 'bytes':
            return pickle.dumps(field._type(value), protocol=2)
        elif db_type == 'string':
            return pickle.dumps(field._type(value))

    # If nothing matched, pass the generator to the back-end.

    #****************NEW CODE - begin **********************
    import types
    if type(value) is types.GeneratorType:
        value = list(value)
    #****************NEW CODE - end****************************

return value