Как я могу использовать bulkuploader для заполнения класса db.SelfReferenceProperty? - PullRequest
3 голосов
/ 14 сентября 2010

У меня есть класс, который использует db.SelfReferenceProperty для создания древовидной структуры.

При попытке заполнить базу данных с помощью appcfg.py upload_data -- config_file=bulkloader.yaml --kind=Group --filename=group.csv (...), я получаю исключение, говорящее BadValueError: name must not be empty. (полный стек ниже)

Я попытался упорядочить данные, чтобы убедиться, что группы с внешним ключом, указывающим на них, были первыми.Это не сработало.

Путем комментирования из bulkloader.yaml строки, в которой выполняется преобразование "import_transform: transform.create_foreign_key ('Group')", данные загружаются, но оно сохраняет это свойство как строкунарушая логику моего приложения.

- kind: Group
  connector: csv
  connector_options:
  property_map:
    - property: __key__
      external_name: key
      export_transform: transform.key_id_or_name_as_string

    - property: name
      external_name: name
      # Type: String Stats: 9 properties of this type in this kind.

    - property: section
      external_name: section
      # Type: Key Stats: 6 properties of this type in this kind.
      import_transform: transform.create_foreign_key('Group')
      export_transform: transform.key_id_or_name_as_string

Есть ли способ заставить загрузчик учитывать собственные ссылки, или я должен либо выполнить преобразование серверной части загружаемых данных, либо реализовать собственный алгоритм массовой загрузки?

----
Traceback (most recent call last):
  File "/home/username/src/google_appengine/google/appengine/tools/adaptive_thread_pool.py", line 150, in WorkOnItems
    status, instruction = item.PerformWork(self.__thread_pool)
  File "/home/username/src/google_appengine/google/appengine/tools/bulkloader.py", line 691, in PerformWork
    transfer_time = self._TransferItem(thread_pool)
  File "/home/username/src/google_appengine/google/appengine/tools/bulkloader.py", line 846, in _TransferItem
    self.content = self.request_manager.EncodeContent(self.rows)
  File "/home/username/src/google_appengine/google/appengine/tools/bulkloader.py", line 1267, in EncodeContent
    entity = loader.create_entity(values, key_name=key, parent=parent)
  File "/home/username/src/google_appengine/google/appengine/ext/bulkload/bulkloader_config.py", line 382, in create_entity
    return self.dict_to_entity(input_dict, self.bulkload_state)
  File "/home/username/src/google_appengine/google/appengine/ext/bulkload/bulkloader_config.py", line 133, in dict_to_entity
    self.__run_import_transforms(input_dict, instance, bulkload_state_copy)
  File "/home/username/src/google_appengine/google/appengine/ext/bulkload/bulkloader_config.py", line 230, in __run_import_transforms
    value = self.__dict_to_prop(transform, input_dict, bulkload_state)
  File "/home/username/src/google_appengine/google/appengine/ext/bulkload/bulkloader_config.py", line 188, in __dict_to_prop
    value = transform.import_transform(value)
  File "/home/username/src/google_appengine/google/appengine/ext/bulkload/bulkloader_parser.py", line 93, in __call__
    return self.method(*args, **kwargs)
  File "/home/username/src/google_appengine/google/appengine/ext/bulkload/transform.py", line 114, in generate_foreign_key_lambda
        return datastore.Key.from_path(kind, value)
  File "/home/username/src/google_appengine/google/appengine/api/datastore_types.py", line 384, in from_path
    ValidateString(id_or_name, 'name')
  File "/home/username/src/google_appengine/google/appengine/api/datastore_types.py", line 109, in ValidateString
    raise exception('%s must not be empty.' % name)
BadValueError: name must not be empty.

Ответы [ 2 ]

4 голосов
/ 14 сентября 2010

Используя ответ на аналогичную проблему в качестве основы, я мог бы успешно решить эту проблему, создав небольшой файл helpers.py, который будет выполнять функцию обертки вокруг transform.create_foreign_key:

from google.appengine.api import datastore

def create_foreign_key(kind, key_is_id=False):
  def generate_foreign_key_lambda(value):
    if value is None:
      return None

    if key_is_id:
      value = int(value)
    try:
      return datastore.Key.from_path(kind, value)
    except:
      return None

  return generate_foreign_key_lambda

Когда этот файл находится в том же каталоге, что и ваш файл конфигурации загрузки yaml bolk (bulkloader.yaml), вы добавляете его в этот файл:

python_preamble:
- (...)
- import: helpers

transformers:

- kind: Group
  connector: csv
  connector_options:
  property_map:
    - property: __key__
      external_name: key
      export_transform: transform.key_id_or_name_as_string

    - property: name
      external_name: name

    - property: section
      external_name: section
      import_transform: helpers.create_foreign_key('Group')
                      # ^^^^^^^ we use the wrapper instead
      export_transform: transform.key_id_or_name_as_string

С этими изменениями массовая загрузкатеперь работает правильно.

Прежде чем использовать это, вам определенно нужно изменить catch all except и заменить его на except BadValueError.

2 голосов
/ 31 августа 2011

transform.py (возможно, только недавно) содержит декоратор, который решает эту проблему:

def none_if_empty(fn):
  """A decorator which returns None if its input is empty else fn(x).

  Useful on import.  Can be used in config files
  (e.g. "transform.none_if_empty(int)" or as a decorator.

  Args:
    fn: Single argument transform function.

  Returns:
    Wrapped function.
  """

  def wrapper(value):


    if value == '' or value is None or value == []:
      return None
    return fn(value)

  return wrapper

Таким образом, использование следующего также решит проблему без введения пользовательского helpers.py файла:

transform.none_if_empty(transform.create_foreign_key('Group'))
...