如何高效地从单个Django模型查询一对对象?

如何高效地从单个Django模型查询一对对象?,django,django-models,django-queryset,Django,Django Models,Django Queryset,我有一个轨迹模型,目前我通过模型id进行嵌套循环,以获得该对,然后将其传递到一个函数中,以计算这两个非等效轨迹对象之间的相似性 track_set = Track.objects.all() track_ids = [track.id for track in track_set] pointer_a = 0 pointer_b = 1 for pointer_a in range(len(track_ids) - 1): for pointer_b in range(pointer

我有一个轨迹模型,目前我通过模型id进行嵌套循环,以获得该对,然后将其传递到一个函数中,以计算这两个非等效轨迹对象之间的相似性

track_set = Track.objects.all()

track_ids = [track.id for track in track_set]
pointer_a = 0
pointer_b = 1

for pointer_a in range(len(track_ids) - 1):
    for pointer_b in range(pointer_a + 1, len(track_ids)):
        track_a = Track.objects.get(pk=track_ids[pointer_a])
        track_b = Track.objects.get(pk=track_ids[pointer_b])
        counter += 1
        count_it_sim(track_a, track_b)
我认为我获取对象的方式不是很有效,有没有办法优化它

编辑:
count\u it\u sim
将计算轨迹a和轨迹b之间的相似性值,我需要计算轨迹模型中所有对的相似性值
models.py

class Tag(models.Model):
    name = models.CharField(max_length=255, unique=True)

class Tagged(models.Model):
    track = models.ForeignKey(Track, on_delete=models.CASCADE)
    tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
    frequency = models.IntegerField(
        default=0,
        validators=[MinValueValidator(0)],
    )
    class Meta:
        unique_together = ('track', 'tag')

class Track(models.Model):
    track_id = models.CharField(max_length=24)
    title = models.CharField(max_length=120)
    link = models.URLField(max_length=120, blank=True)
    tags = models.ManyToManyField(Tag, through='Tagged', blank=True)
    similarity = models.ManyToManyField(
        'self',
        blank=True,
        through='Similar',
        related_name='similar_to',
        symmetrical=False
    )
    users = models.ManyToManyField(User, through='PlayTrack', blank=True)

class Similar(models.Model):
    track1 = models.ForeignKey(Track, on_delete=models.CASCADE, related_name='track1')
    track2 = models.ForeignKey(Track, on_delete=models.CASCADE, related_name='track2')
    similarity = models.FloatField(
        validators=[MinValueValidator(0), MaxValueValidator(1)],
    )
count_it_sim将要做的是,它将通过一个关联实体,即标记模型,获得轨迹a和轨迹b的所有标记频率,并计算它以获得轨迹a和轨迹b之间的相似值

def count_it_sim(track_a: Track, track_b: Track):
    tag_set = Tag.objects.all()
    part1 = 0
    part2 = 0
    part3 = 0
    for tag in tag_set:
        try:
            freq_tag_of_track_a = Tagged.objects.get(track=track_a, tag=tag).frequency
        except Tagged.DoesNotExist:
            continue
        try:
            freq_tag_of_track_b = Tagged.objects.get(track=track_b, tag=tag).frequency
        except Tagged.DoesNotExist:
            continue
        part1 += freq_tag_of_track_a * freq_tag_of_track_b
        part2 += freq_tag_of_track_a ** 2
        part3 += freq_tag_of_track_b ** 2
    try:
        it_sim = part1 / (math.sqrt(part2) * math.sqrt(part3))
    except ZeroDivisionError:
        it_sim = None
编辑2:在count\u it\u sim上,我没有遍历
Tag.objects.all()
中的所有标记,而是只查询
taged
中存在的那些标记,结果比前一个快得多,这是我当前的代码

def count_it_sim(track_a: Track, track_b: Track):
    filtered_tagged = Tagged.objects.filter(Q(track=track_a) | Q(track=track_b))
    tag_ids = filtered_tagged.values_list('tag', flat=True).distinct()
    part1 = 0
    part2 = 0
    part3 = 0
    for tag_id in tag_ids:
        try:
            freq_tag_of_track_a = filtered_tagged.get(track=track_a, tag__id=tag_id).frequency
        except Tagged.DoesNotExist:
            freq_tag_of_track_a = 0
        try:
            freq_tag_of_track_b = filtered_tagged.get(track=track_b, tag__id=tag_id).frequency
        except Tagged.DoesNotExist:
            freq_tag_of_track_b = 0
        part1 += freq_tag_of_track_a * freq_tag_of_track_b
        part2 += freq_tag_of_track_a ** 2
        part3 += freq_tag_of_track_b ** 2
    try:
        it_sim = part1 / (math.sqrt(part2) * math.sqrt(part3))
    except ZeroDivisionError:
        it_sim = None
编辑3:模型中有一些更改。现在将通过计算有多少
用户
使用特定的
标记对
轨道进行标记来计算
频率
,而不是存储
轨道的每个
标记的
频率
。这是最新消息

# models.py
...
class Tagged(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
    track = models.ForeignKey(Track, on_delete=models.CASCADE)
    tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
...
并且计数功能变成

def count_it_sim(track_a: Track, track_b: Track):
    filtered_tagged = Tagged.objects.filter(Q(track=track_a) | Q(track=track_b))
    tag_ids = filtered_tagged.values_list('tag', flat=True).distinct()
    part1 = 0
    part2 = 0
    part3 = 0
    for tag_id in tag_ids:
        try:
            freq_tag_of_track_a = filtered_tagged.filter(track=track_a, tag__id=tag_id).count()  # UPDATED LINE
        except Tagged.DoesNotExist:
            freq_tag_of_track_a = 0

        try:
            freq_tag_of_track_b = filtered_tagged.filter(track=track_b, tag__id=tag_id).count()  #UPDATED LINE
        except Tagged.DoesNotExist:
            freq_tag_of_track_b = 0
        part1 = accumulate(part1, freq_tag_of_track_a * freq_tag_of_track_b)
        part2 = accumulate(part2, freq_tag_of_track_a ** 2)
        part3 = accumulate(part3, freq_tag_of_track_b ** 2)
    try:
        it_sim = final_calc(part1, part2, part3)
    except ZeroDivisionError:
        it_sim = None
    return it_sim
我认为我获取对象的方式不是很有效,有没有办法优化它

嗯,您已经有了对象(在
track\u set
中),因此不需要再次获取它们;您只需要获得成对的对象

我需要的是(1,2)(1,3)(1,4)(2,3)(2,4)(3,4)

为此,您可以使用
itertools.compositions

import itertools

for a,b in itertools.combinations(track_set, 2):
   count_it_sim(a, b)
您必须确保以正确的顺序从数据库中获取对象;因为无法保证如何归还物品:

如果查询没有指定顺序,则返回结果 以未指定的顺序从数据库中删除。特定的顺序是 仅当由一组唯一 识别结果中的每个对象

在您的情况下,似乎需要按主键顺序排列它们;因此,我将初始查询修改为:

track_set = Track.objects.order_by('pk')
包含有关按
订购的详细信息,以及指定默认订购的详细信息

我认为我获取对象的方式不是很有效,有没有办法优化它

嗯,您已经有了对象(在
track\u set
中),因此不需要再次获取它们;您只需要获得成对的对象

我需要的是(1,2)(1,3)(1,4)(2,3)(2,4)(3,4)

为此,您可以使用
itertools.compositions

import itertools

for a,b in itertools.combinations(track_set, 2):
   count_it_sim(a, b)
您必须确保以正确的顺序从数据库中获取对象;因为无法保证如何归还物品:

如果查询没有指定顺序,则返回结果 以未指定的顺序从数据库中删除。特定的顺序是 仅当由一组唯一 识别结果中的每个对象

在您的情况下,似乎需要按主键顺序排列它们;因此,我将初始查询修改为:

track_set = Track.objects.order_by('pk')

有关于
顺序的详细信息,有关于指定默认顺序的详细信息。

根据我们在评论中的讨论,目的是使用最少数量的数据库查询获得所需的数据,其结构便于进一步的数据操作

我们将尝试获取与以下字典结构中的每个
曲目
对应的所有
标记的.frequency
数据:

{
    track_id1: list(all_related_frequencies), 
    track_id2: list(all_related_frequencies),
}
其思想是在名为
prefetched\u taged
的属性中获取所有
Track
实例及其相关的
标记的
实例

Track
的每个实例都有一个属性
taged\u set
,该属性允许所有与其相关的
taged
实例。这是预取它们的查询:

from django.db.models import Prefetch

the_data = (
    Track.objects.prefetch_related(
        Prefetch(
            'tagged_set',
            to_attr='prefetched_tagged',
        )
    ).all()
)
现在,我们在
变量
中保存了
Track
的所有实例,其中每个实例都有一个属性
prefetched\u taged
,该属性包含与每个
Track
实例相关的所有
taged
实例的
列表

通过以下字典理解,我们迭代
变量以创建一个字典,其中所有
Track.Track\u id
作为键。每个键都有一个
列表
作为其值,其中包含其所有相关的
标记。频率

要创建列表,我们将在字典理解中使用列表理解:

result = {
    each_track.track_id: [
        each_tagged.frequency for each_tagged in each_track.prefetched_tagged
    ] for each_track in the_data
}
现在,变量
result
包含我们进一步操作它们所需的结构中的数据。为了将所有数据库数据加载到内存中,需要进行2次数据库点击。这是也测量数据库命中率的全部代码:

from django import db
from django.db.models import Prefetch

db.reset_queries()    

the_data = (
    Track.objects.prefetch_related(
        Prefetch(
            'tagged_set',
            to_attr='prefetched_tagged',
        )
    ).all()
)

result = {
    each_track.track_id: [
        each_tagged.frequency for each_tagged in each_track.prefetched_tagged
    ] for each_track in the_data
}

print(result)
print ("Queries Used: {0}".format(len(db.connection.queries))

根据我们在评论中的讨论,我们的目标是使用最少数量的数据库查询获得所需的数据,其结构将轻松允许进一步的数据操作

我们将尝试获取与以下字典结构中的每个
曲目
对应的所有
标记的.frequency
数据:

{
    track_id1: list(all_related_frequencies), 
    track_id2: list(all_related_frequencies),
}
其思想是在名为
prefetched\u taged
的属性中获取所有
Track
实例及其相关的
标记的
实例

Track
的每个实例都有一个属性
taged\u set
,该属性允许所有与其相关的
taged
实例。这是预取它们的查询:

from django.db.models import Prefetch

the_data = (
    Track.objects.prefetch_related(
        Prefetch(
            'tagged_set',
            to_attr='prefetched_tagged',
        )
    ).all()
)
现在,我们在
变量
中保存了
Track
的所有实例,其中每个实例都有一个属性
prefetched\u taged
,该属性包含所有
Tagg的
列表