AppEngine массовый загрузчик загружает сущность, задав ключ - PullRequest
0 голосов
/ 27 августа 2011

Я потратил как минимум два часа, пытаясь заставить это работать.Я видел довольно много разных вопросов по SO и в группах Google, но ни один из ответов, похоже, не работает для меня.

Вопрос: Как выполнить массовую загрузку данных, как вФайл CSV ниже в хранилище данных, чтобы создать объекты, которые имеют имя_ключа, определенное в файле CSV (тот же результат, что и при использовании функции добавления ниже).

Это моя модель:

class RegisteredDomain(db.Model):
    """
    Domain object class. It has no fields because it's existence is
    proof that it has been registered. Indivdual registered domains
    can be found using keys.
    """
    pass

Вот как я обычно добавляю / удаляю домены и т.д .:

def add(domains):
    """
    Add domains. This functions accepts a single domain string or a
    list of domain strings and adds them to the database. The domain(s)
    must be valid unicode strings (a ValueError is thrown if the domain
    strings are not valid.
    """
    if not isinstance(domains, list):
        domains = [domains]

    cleaned_domains = []
    for domain in domains:
        clean_domain_ = clean_domain(domain)
        is_valid_domain(clean_domain_)
        cleaned_domains.append(clean_domain_)

    domains = cleaned_domains

    db.put([RegisteredDomain(key_name=make_key(domain)) for domain in domains])


def get(domains):
    """
    Get domains. This function accepts a single domain string or a list
    of domain strings and queries the database for them. It returns a
    dictionary containing the domain name and RegisteredDomain object or
    None if the entity was not found.
    """
    if not isinstance(domains, list):
        domains = [domains]

    entities = db.get([Key.from_path('RegisteredDomain', make_key(domain)) for domain in domains])
    return dict(zip(domains, entities))

Примечание: в приведенном выше коде make_key просто делаетдомен в нижнем регистре и добавляет 'd'.

Так что это так.Теперь я схожу с ума, пытаясь загрузить некоторые объекты RegisteredDomain из файла CSV.Вот файл CSV (обратите внимание, что первый символ 'd' существует из-за того факта, что имя ключа может не начинаться с цифры):

key
dgoogle.com
dgoogle11.com
dfacebook.com
dcool.com
duuuuuuu.com
dsdsdsds.com
dffffooo.com
dgmail.com

Я не смог автоматически сгенерироватьФайл yaml для массового загрузчика, потому что ядро ​​приложения все еще не обновляет мою статистику хранилища данных (1 день плюс несколько часов).Итак, это (и многие подобные перестановки) то, что я придумал (в основном изменяя бит import_transform):

python_preamble:
- import: google.appengine.ext.bulkload.transform
- import: google.appengine.api.datastore
- import: google.appengine.ext.db
- import: utils
- import: bulk_helper

transformers:
- kind: RegisteredDomain
  connector: csv
  connector_options:
    encoding: utf-8
  property_map:
    - property: __key__
      external_name: key
      export_transform: bulk_helper.key_to_reverse_str
      import_template: transform.create_foreign_key('RegisteredDomain')

Теперь по какой-то причине, когда я пытаюсь загрузить его, говорит, что все идет хорошо, и х сущностейбыли перенесены и т.д., но ничего не обновляется в хранилище данных (как я вижу из консоли администратора).Вот как я загружаю:

appcfg.py upload_data --application=domain-sandwich --kind=RegisteredDomain --config_file=bulk.yaml --url=http://domain-sandwich.appspot.com/remote_api --filename=data.csv 

И, наконец, вот так выглядит мой просмотрщик хранилища данных: Datastore Viewer

Примечание: я делаю это как на dev-сервере, так и на appengine(что бы ни работало ...).

Спасибо за любую помощь!

1 Ответ

0 голосов
/ 27 августа 2011

Проблема в баговом загрузчике appengine (или API хранилища данных).Я опубликовал несколько выпусков об этой проблеме ( выпуск 1 , выпуск 2 , выпуск 3 , выпуск 4 ), но воттекст ошибки об ошибке загрузчика для дальнейшего использования:

VERSION:
release: "1.5.2"
timestamp: 1308730906
api_versions: ['1']

Загрузчик не будет импортировать модели без свойств.Пример:

class MetaObject(db.Model):
    """
    Property-less object. Identified by application set key.
    """
    pass

В приложении вы можете использовать эти объекты следующим образом:

db.put([MetaObject(key_name=make_key(obj)) for obj in objs])
db.get([Key.from_path('MetaObject', make_key(obj)) for obj in objs])
db.delete([Key.from_path('MetaObject', make_key(obj)) for obj in objs])

Теперь проблема возникла, когда я пытался импортировать данные с помощью массового загрузчика.После просмотра кода массового загрузчика обнаружена ошибка в методе EncodeContent (строки 1400-1406):

1365   def EncodeContent(self, rows, loader=None):
1366     """Encodes row data to the wire format.
1367
1368     Args:
1369       rows: A list of pairs of a line number and a list of column values.
1370       loader: Used for dependency injection.
1371
1372     Returns:
1373       A list of datastore.Entity instances.
1374
1375     Raises:
1376       ConfigurationError: if no loader is defined for self.kind
1377     """
1378     if not loader:
1379       try:
1380         loader = Loader.RegisteredLoader(self.kind)
1381       except KeyError:
1382         logger.error('No Loader defined for kind %s.' % self.kind)
1383         raise ConfigurationError('No Loader defined for kind %s.' % self.kind)
1384     entities = []
1385     for line_number, values in rows:
1386       key = loader.generate_key(line_number, values)
1387       if isinstance(key, datastore.Key):
1388         parent = key.parent()
1389         key = key.name()
1390       else:
1391         parent = None
1392       entity = loader.create_entity(values, key_name=key, parent=parent)
1393
1394       def ToEntity(entity):
1395         if isinstance(entity, db.Model):
1396           return entity._populate_entity()
1397         else:
1398           return entity
1399
1400       if not entity:
1401
1402         continue
1403       if isinstance(entity, list):
1404         entities.extend(map(ToEntity, entity))
1405       elif entity:
1406         entities.append(ToEntity(entity))
1407
1408     return entities

Поскольку (будет также опубликована проблема для этого), подклассы объекта Entity хранилища dictбез переопределения ненулевых или len методов сущность, которая не содержит каких-либо свойств, но имеет ключ, не будет быть True (делает ", если не сущность"истина, даже когда ключ был установлен) и, следовательно, не будет добавляться к сущностям.

Вот различие, которое исправляет это в массовом загрузчике ИЛИ путем переопределения ненулевого в сущности (любой из них работает):

--- bulkloader.py       2011-08-27 18:21:36.000000000 +0200
+++ bulkloader_fixed.py 2011-08-27 18:22:48.000000000 +0200
@@ -1397,12 +1397,9 @@
         else:
           return entity

-      if not entity:
-
-        continue
       if isinstance(entity, list):
         entities.extend(map(ToEntity, entity))
-      elif entity:
+      else:
         entities.append(ToEntity(entity))

     return entities
--- datastore.py        2011-08-27 18:41:16.000000000 +0200
+++ datastore_fixed.py  2011-08-27 18:40:50.000000000 +0200
@@ -644,6 +644,12 @@

     self.__key = Key._FromPb(ref)

+  def __nonzero__(self):
+      if len(self):
+          return True
+      if self.__key:
+          return True
+
   def app(self):
     """Returns the name of the application that created this entity, a
     string or None if not set.

Опубликованные отчеты об ошибках:

Выпуск 1: http://code.google.com/p/googleappengine/issues/detail?id=5712

Выпуск 2: http://code.google.com/p/googleappengine/issues/detail?id=5713

Выпуск 3: http://code.google.com/p/googleappengine/issues/detail?id=5714

Выпуск 4: http://code.google.com/p/googleappengine/issues/detail?id=5715

...