使用ManyToMany字段get_or_create django模型

djs*_*djs 5 python django

假设我有三个django模型:

class Section(models.Model):
    name = models.CharField()

class Size(models.Model):
    section = models.ForeignKey(Section)
    size = models.IntegerField()

class Obj(models.Model):
    name = models.CharField()
    sizes = models.ManyToManyField(Size)
Run Code Online (Sandbox Code Playgroud)

我想导入大量的Obj数据,其中许多大小的字段将是相同的.但是,由于Obj有一个ManyToMany字段,我不能像往常一样测试存在.我希望能够做到这样的事情:

try:
    x = Obj(name='foo')
    x.sizes.add(sizemodel1) # these can be looked up with get_or_create
    ...
    x.sizes.add(sizemodelN) # these can be looked up with get_or_create
    # Now test whether x already exists, so I don't add a duplicate
    try:
        Obj.objects.get(x)
    except Obj.DoesNotExist:
        x.save()
Run Code Online (Sandbox Code Playgroud)

但是,我不知道以这种方式获取对象的方法,你必须传递关键字参数,这对于ManyToManyFields不起作用.

有什么好方法可以做到这一点吗?我唯一的想法就是建立一组Q对象来传递给:

myq = myq & Q(sizes__id=sizemodelN.id)
Run Code Online (Sandbox Code Playgroud)

但我不确定这甚至会起作用......

Yuj*_*ita 1

您的示例没有多大意义,因为您无法在x保存之前添加 m2m 关系,但它很好地说明了您正在尝试做的事情。Size您有一个通过 创建的对象列表get_or_create(),并且想要创建一个Obj(如果不存在重复的对象大小关系)?

不幸的是,这并不容易实现。链接Q(id=F) & Q(id=O) & Q(id=O)不适用于 m2m。

您当然可以使用,但这意味着您将在一个巨大的尺寸列表中得到与 1 的Obj.objects.filter(size__in=Sizes)匹配。Objsize

查看这篇文章以了解马尔科姆回​​答的__in确切问题,所以我非常相信它。

我为了好玩写了一些 python 来解决这个问题。
这是一次性导入吗?

def has_exact_m2m_match(match_list):
    """
    Get exact Obj m2m match 
    """
    if isinstance(match_list, QuerySet):
        match_list = [x.id for x in match_list]

    results = {}
    match = set(match_list)
    for obj, size in \
        Obj.sizes.through.objects.filter(size__in=match).values_list('obj', 'size'):
        # note: we are accessing the auto generated through model for the sizes m2m
        try:
            results[obj].append(size)
        except KeyError:
            results[obj] = [size]

    return bool(filter(lambda x: set(x) == match, results.values()))
    # filter any specific objects that have the exact same size IDs
    # if there is a match, it means an Obj exists with exactly 
    # the sizes you provided to the function, no more.


sizes = [size1, size2, size3, sizeN...]
if has_exact_m2m_match(sizes):
    x = Obj.objects.create(name=foo) # saves so you can use x.sizes.add
    x.sizes.add(sizes)
Run Code Online (Sandbox Code Playgroud)