如何高效地从单个Django模型查询一对对象?
我有一个轨迹模型,目前我通过模型id进行嵌套循环,以获得该对,然后将其传递到一个函数中,以计算这两个非等效轨迹对象之间的相似性如何高效地从单个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
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的列表