简体   繁体   English

使用ManyToMany字段get_or_create django模型

[英]get_or_create django model with ManyToMany field

Suppose I have three django models: 假设我有三个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)

I would like to import a large amount of Obj data where many of the sizes fields will be identical. 我想导入大量的Obj数据,其中许多大小的字段将是相同的。 However, since Obj has a ManyToMany field, I can't just test for existence like I normally would. 但是,由于Obj有一个ManyToMany字段,我不能像往常一样测试存在。 I would like to be able to do something like this: 我希望能够做到这样的事情:

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()

However, I'm not aware of a way to get an object this way, you have to just pass in keyword parameters, which don't work for ManyToManyFields. 但是,我不知道以这种方式获取对象的方法,你必须传递关键字参数,这对于ManyToManyFields不起作用。

Is there any good way I can do this? 有什么好方法可以做到这一点吗? The only idea I've had is to build up a set of Q objects to pass to get: 我唯一的想法就是建立一组Q对象来传递给:

myq = myq & Q(sizes__id=sizemodelN.id)

But I am not sure this will even work... 但我不确定这甚至会起作用......

Use a through model and then .get() against that. 使用直通模型,然后使用.get()。

http://docs.djangoproject.com/en/dev/topics/db/models/#extra-fields-on-many-to-many-relationships http://docs.djangoproject.com/en/dev/topics/db/models/#extra-fields-on-many-to-many-relationships

Once you have a through model, you can .get() or .filter() or .exists() to determine the existence of an object that you might otherwise want to create. 一旦有了直通模型,就可以使用.get()或.filter()或.exists()来确定是否存在您可能想要创建的对象。 Note that .get() is really intended for columns where unique is enforced by the DB - you might have better performance with .exists() for your purposes. 请注意,.get()实际上适用于DB强制执行唯一的列 - 您可能会出于更好的性能使用.exists()。

If this is too radical or inconvenient a solution, you can also just grab the ManyRelatedManager and iterate through to determine if the object exists: 如果这是一个过于激进或不方便的解决方案,您也可以抓住ManyRelatedManager并迭代以确定该对象是否存在:

object_sizes = obj.sizes.all()
exists = object_sizes.filter(id__in = some_bunch_of_size_object_ids_you_are_curious_about).exists()
if not exists: 
    (your creation code here)

Your example doesn't make much sense because you can't add m2m relationships before an x is saved, but it illustrated what you are trying to do pretty well. 你的例子没有多大意义,因为在保存x之前你不能添加m2m关系,但它说明了你想要做的很好。 You have a list of Size objects created via get_or_create() , and want to create an Obj if no duplicate obj-size relationship exists? 你有一个通过get_or_create()创建的Size对象列表,如果不存在重复的obj-size关系,你想创建一个Obj吗?

Unfortunately, this is not possible very easily. 不幸的是,这是不可能的。 Chaining Q(id=F) & Q(id=O) & Q(id=O) doesn't work for m2m. 链接Q(id=F) & Q(id=O) & Q(id=O)对m2m不起作用。

You could certainly use Obj.objects.filter(size__in=Sizes) but that means you'd get a match for an Obj with 1 size in a huge list of sizes. 你当然可以使用Obj.objects.filter(size__in=Sizes)但这意味着你可以在一个庞大的大小列表中获得一个具有1个sizeObj的匹配。

Check out this post for an __in exact question , answered by Malcolm, so I trust it quite a bit. 看看这个职位的__in准确的问题 ,由Malcolm回答,所以我相信它颇有几分。

I wrote some python for fun that could take care of this. 我写了一些python以获得乐趣,可以解决这个问题。
This is a one time import right? 这是一次性导入权吗?

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM