Django提取帖子哈希标签并存储至多对一关系表的问题咨询
解决方案
问题1:哈希标签存在判断与新建的性能优化
你当前循环调用get判断、不存在则新建的方式,每处理1个标签至少产生1次数据库查询,标签数量多的时候性能会线性下降,有两种优化方案可选:
- 简化单条逻辑可以直接用Django内置的
get_or_create方法,已经封装了异常捕获逻辑,写法更简洁:ht_obj, created = HashTags.objects.get_or_create(hash_tag=ht) - 批量处理的最优方案是先批量查询已存在的标签,再批量插入不存在的标签,全程仅需要2次查询+1次批量插入,性能不会随标签数量增长明显下降:
# 一次性查询所有已经存在的标签 existed_tags = HashTags.objects.filter(hash_tag__in=hash_tags_list).values_list('hash_tag', flat=True) # 筛选出需要新增的标签 need_create_tags = [ht for ht in hash_tags_list if ht not in existed_tags] # 批量插入新标签 HashTags.objects.bulk_create([HashTags(hash_tag=ht) for ht in need_create_tags]) # 此时所有标签都已在库中,直接批量取出所有标签对象即可 all_ht_objs = HashTags.objects.filter(hash_tag__in=hash_tags_list)
问题2:HashTagsInPost关联存储方案
首先你当前的模型定义存在错误:HashTagsInPost中的post_id字段使用了OneToOneField,这会限制一个帖子只能关联一个标签,完全不符合一对多的需求,需要先修改模型字段:
class HashTagsInPost(AbstractBaseModel): hash_tag = models.ForeignKey(HashTags, on_delete=models.CASCADE, db_index=True) # 把OneToOneField改为ForeignKey post = models.ForeignKey(Post, on_delete=models.CASCADE, db_index=True)
修改模型后执行迁移即可。
你问是否可以直接传入HashTag对象列表完成存储,答案是不能直接把列表传给单个hash_tag字段,每个标签和帖子的关联都是HashTagsInPost表的独立行,需要为每个标签生成一条关联记录,你可以用bulk_create批量插入关联记录,全程仅需要1次数据库操作,性能和直接传列表几乎无差异:
# 接上面批量处理完标签的代码 HashTagsInPost.objects.bulk_create([ HashTagsInPost(hash_tag=ht_obj, post=post_obj) for ht_obj in all_ht_objs ])
优化后完整视图代码
def post(self, request): serializer = PostSerializer(data=request.data) if serializer.is_valid(): post_body = request.data['body'] post_obj = serializer.save() hash_tags_list = extract_hashtags(post_body) # 无标签时跳过无用查询 if hash_tags_list: # 批量处理标签 existed_tags = HashTags.objects.filter(hash_tag__in=hash_tags_list).values_list('hash_tag', flat=True) need_create_tags = [ht for ht in hash_tags_list if ht not in existed_tags] HashTags.objects.bulk_create([HashTags(hash_tag=ht) for ht in need_create_tags]) all_ht_objs = HashTags.objects.filter(hash_tag__in=hash_tags_list) # 批量插入关联记录 HashTagsInPost.objects.bulk_create([ HashTagsInPost(hash_tag=ht_obj, post=post_obj) for ht_obj in all_ht_objs ]) return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
内容的提问来源于stack exchange,提问作者user12314098
相关产品推荐
相关产品推荐

