首页 > 解决方案 > 意外的 Django ORM 行为

问题描述

我有一个函数,它必须按照它的名字做东西,但它对我来说很奇怪。

def get_or_create_artists_by_metadata(metadata: Dict[str, str]) -> List[Optional[Artist]]:
    if artists := metadata.get('artist'):
        artists = parse_artists_from_string(artists) # returns list of strings

        try:
            existing = Artist.objects.filter(title__in=artists) # 1
        except Exception as err:
            log.exception('Database error!')
            log.exception(err)

            return []

        new_artists_list = list(set(artists) - set(itertools.chain(*existing.values_list('title'))))

        objs = [Artist(title=artist) for artist in new_artists_list]

        try:
            new_artists = Artist.objects.bulk_create(objs) # 2
        except Exception as err:
            log.exception('Error during artists creating!')

            return [*existing]

        result = [*existing]

        if new_artists: # 3
            result.extend(new_artists)

        return result
    else:
        return []  

例如,我在artistsvar 中有 2 个字符串。

在第 1 步中,我一开始什么都没有,清空 QuerySet。然后在第 2 步中,我创建了新艺术家,同时我在existingvar 中有一些条目!所以在第 3 步我有 true, extend result,并且已经有 4 个条目。

请解释一下这种行为以及我应该如何操作它。

标签: pythondjango

解决方案


首先为了检查是否存在,最好使用exists().

existing = Artist.objects.filter(title__in=artists)
existing.exists()  # return True or False

如果要说您的问题,我相信它可能与value_list此处的错误使用有关:

existing.values_list('title')

因为您将返回元组列表的格式,例如:

[('125',), ('126',), ('219',)]

所以在这里你比较一个字符串列表和一个元组列表,这就是它不删除现有字符串的原因。

但你期望这样:

['125', '126', '219',]

因此,为了获得字符串列表而不是元组列表,您需要添加flat=True

existing.values_list('title', flat=True)

以及声明的结果:

new_artists_list = list(set(artists) - set(itertools.chain(*existing.values_list('title', flat=True))))

将仅返回未使用的 id 字符串。


推荐阅读