使用bulkloader上传数据
简单来说:我该如何配置批量加载器,让它把数据插入到两个有关系的模型中呢?
我有一个“人”和一个“水果”的类,其中“人”类与“水果”类有关联:
class Fruit(db.Model):
name = db.StringProperty()
class Person(db.Model):
name = db.StringProperty()
customer = db.ReferenceProperty(Fruit)
我想上传这个CSV数据:
Name,Fruit
Bob,Banana
Joe,Apple
Tim,Banana
我尝试使用文档中提到的create_foreign_key:
transformers:
- kind: fruit
connector: csv
property_map:
- property: fruit
external_name: Fruit
- kind: person
connector: csv
connector_options:
encoding: utf-8
columns: from_header
property_map:
- property: title
external_name: Name
- property: fruit
external_name: Fruit
import_transform: transform.create_foreign_key('fruit')
当我运行这个命令时:
appcfg.py upload_data --config_file=bulkloader.yaml --filename=food.csv --kind=person .
人被上传了,并且他们有指向水果的外键,但他们指向的水果实体并不存在。
当我尝试 --kind=fruit
时,水果被上传了,但出现了很多重复项。
我想把“人”和“水果”关联起来,而且不想有重复的水果,这通过批量加载器可以实现吗?
3 个回答
可以通过一个叫做post_import_function的功能来实现。
在你的模型里,不要导入外键。相反,你需要添加一个post_import_function,内容大概是这样的:
def fkeyLocation(input_dict, entity_instance, bulkload_state): entity_instance.availableAt = Location.all().filter('name = ',input_dict['availableAt']).get().key() return entity_instance
关键在于使用input_dict来查找。如果你在使用多态模型(polymodels),那么你不能使用向导自动生成的“kind”,而是要用示例代码中的model.modelName,具体可以参考这里。
当然可以。
这里的基本问题是缺少一个步骤。你有一个水果的名字,但你想存储的是水果的键。你可以通过几种方式来实现这个目标。
如果Banana
(香蕉)或Apple
(苹果)是水果的永久唯一标识符,你可以使用transform.create_foreign_key('Fruit')
。这样你就能得到一个水果键,其中水果名字就是键的名称。这样上传的人会指向那些不存在的水果实体,这没问题。只要在__key__
属性上使用相同的导入转换来上传水果,就能创建相应的实体。
如果你不想用水果名字作为水果键的名称,那你就需要做一些更复杂的后期处理。你可以写一个post_import_function
,这个函数会根据名字查询水果,看看是否已经存在匹配的实体,如果没有,就创建一个,然后在新创建的人实体上设置对它的引用。
我没找到一个简单的方法来做到这一点,所以最后我只是把数据分成了多个文件,并提前生成了ID。