Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Django,为许多字段批量创建的正确方法?_Python_Django_Many To Many - Fatal编程技术网

Python Django,为许多字段批量创建的正确方法?

Python Django,为许多字段批量创建的正确方法?,python,django,many-to-many,Python,Django,Many To Many,我有这个填充表的代码 def add_tags(count): print "Add tags" insert_list = [] photo_pk_lower_bound = Photo.objects.all().order_by("id")[0].pk photo_pk_upper_bound = Photo.objects.all().order_by("-id")[0].pk for i in range(count): t =

我有这个填充表的代码

def add_tags(count):
    print "Add tags"
    insert_list = []
    photo_pk_lower_bound = Photo.objects.all().order_by("id")[0].pk
    photo_pk_upper_bound = Photo.objects.all().order_by("-id")[0].pk
    for i in range(count):
        t = Tag( tag = 'tag' + str(i) )
        insert_list.append(t)
    Tag.objects.bulk_create(insert_list)
    for i in range(count):
        random_photo_pk = randint(photo_pk_lower_bound, photo_pk_upper_bound)
        p = Photo.objects.get( pk = random_photo_pk )
        t = Tag.objects.get( tag = 'tag' + str(i) )
        t.photos.add(p)
这就是模型:

class Tag(models.Model):
    tag = models.CharField(max_length=20,unique=True)
    photos = models.ManyToManyField(Photo)
正如我所理解的那样:我必须首先保存标记对象(由于有许多字段),然后通过
add()
将照片附加到它们。但是对于大的
count
来说,这个过程花费的时间太长。有没有办法重构这段代码以使其更快

通常,我想用随机虚拟数据填充标记模型

编辑1(照片模型)

TL;DR 使用“至”模型批量插入m2m关系

"Tag.photos.through" => Model with 3 fields [ id, tag, photo ]
new_tag_photo = Tag.photos.through(tag_id=1, photo_id=2)
Tag.photos.through.objects.bulk_insert([new_tag_photo, ...])

这是我所知道的最快的方法,我一直使用它来创建测试数据。我可以在几分钟内生成数百万条记录

从Georgy编辑:

def add_tags(count):
    Tag.objects.bulk_create([Tag(tag='tag%s' % t) for t in range(count)])

    tag_ids = list(Tag.objects.values_list('id', flat=True))
    photo_ids = Photo.objects.values_list('id', flat=True)
    tag_count = len(tag_ids)
       
    for photo_id in photo_ids:
        tag_to_photo_links = []
        shuffle(tag_ids)

        rand_num_tags = randint(0, tag_count)
        photo_tags = tag_ids[:rand_num_tags]

        for tag_id in photo_tags:
            # through is the table generated by django to link m2m between tag and photo
            photo_tag = Tag.photos.through(tag_id=tag_id, photo_id=photo_id)
            tag_to_photo_links.append(photo_tag)

        Tag.photos.through.objects.bulk_create(tag_to_photo_links, batch_size=7000)
我没有创建要测试的模型,但是结构在那里,你可能需要调整一些东西才能让它工作。如果你遇到任何问题,请告诉我


[已编辑]

如Du D的回答所示,Django许多字段使用一个名为
的表,该表包含三列:关系的ID、链接到的对象的ID和链接到的对象的ID。您可以在
上通过
使用
bulk\u create
批量创建多个关系

举个简单的例子,您可以批量创建标记到照片的关系,如下所示:

tag1 = Tag.objects.get(id=1)
tag2 = Tag.objects.get(id=2)
photo1 = Photo.objects.get(id=1)
photo2 = Photo.objects.get(id=2)


through_objs = [
    Tag.photos.through(
        photo_id=photo1.id,
        tag_id=tag1.id,
    ),
    Tag.photos.through(
        photo_id=photo1.id,
        tag_id=tag2.id,
    ),
    Tag.photos.through(
        photo_id=photo2.id,
        tag_id=tag2.id,
    ),
]
Tag.photos.through.objects.bulk_create(through_objs)
通解 这是一个通用的解决方案,您可以运行它在任何对象对列表之间建立许多关系

from typing import Iterable
from collections import namedtuple


ManyToManySpec = namedtuple(
    "ManyToManySpec", ["from_object", "to_object"]
)


def bulk_create_manytomany_relations(
    model_from,
    field_name: str,
    model_from_name: str,
    model_to_name: str,
    specs: Iterable[ManyToManySpec]
):
    through_objs = []
    for spec in specs:
        through_objs.append(
            getattr(model_from, field_name).through(
                **{
                    f"{model_from_name.lower()}_id": spec.from_object.id,
                    f"{model_to_name.lower()}_id": spec.to_object.id,
                }
            )
        )
    getattr(model_from, field_name).through.objects.bulk_create(through_objs)
示例用法
您好,很抱歉延迟答复。我可以说,您确实有正确的想法通过
使用
,我确实为自己找到了相同的解决方案,尽管此功能在文档上很短,您能为我提供一些建议吗?至少与我相比,你是一个高级Python开发人员,我必须阅读一些文档才能完全理解你的答案,尽管我不得不承认简单的复制&过去对我来说不起作用。非常感谢你的帮助!我稍后会尝试添加其他信息。您可以发布照片的模型定义吗?对不起,我花了一些时间。模型位于EDIT section.Hi中。我早些时候对你的答案进行了编辑,但被拒绝了。您的代码有一些错误,请您修改一下以备记录?1) 它不是
Tag.Photos.through
,而是
Tag.Photos.through
。2)
Photo.objects.value\u list
Photo.objects.values\u list
(此处键入)。3) 您不能以这种方式洗牌标记ID,请使用
list()
对其进行转换。4) 您必须将最后一行
bulk\u create()
移出
forloop
,否则代码会尝试添加重复项。提前感谢您抽出时间!只是更新一下。。Tag.photos.through.bulk\u insert()将导致没有属性bulk\u insert()的内容。相反,我们应该使用Tag.photos.through.objects.bulk\u create()。
from typing import Iterable
from collections import namedtuple


ManyToManySpec = namedtuple(
    "ManyToManySpec", ["from_object", "to_object"]
)


def bulk_create_manytomany_relations(
    model_from,
    field_name: str,
    model_from_name: str,
    model_to_name: str,
    specs: Iterable[ManyToManySpec]
):
    through_objs = []
    for spec in specs:
        through_objs.append(
            getattr(model_from, field_name).through(
                **{
                    f"{model_from_name.lower()}_id": spec.from_object.id,
                    f"{model_to_name.lower()}_id": spec.to_object.id,
                }
            )
        )
    getattr(model_from, field_name).through.objects.bulk_create(through_objs)
tag1 = Tag.objects.get(id=1)
tag2 = Tag.objects.get(id=2)
photo1 = Photo.objects.get(id=1)
photo2 = Photo.objects.get(id=2)

bulk_create_manytomany_relations(
    model_from=Tag,
    field_name="photos",
    model_from_name="tag",
    model_to_name="photo",
    specs=[
        ManyToManySpec(from_object=tag1, to_object=photo1),
        ManyToManySpec(from_object=tag1, to_object=photo2),
        ManyToManySpec(from_object=tag2, to_object=photo2),
    ]
)