一、背景
当前需求: 做一个小功能,要查询用户的所有订阅的信息,包括所订阅的应用和资源,而且要能分页查。
数据库设计: 但是用户订阅数据是放在两张表中,应用订阅表和资源订阅表。单表可以分页查询,但多表分页查询之前是在java中,使用sql语句用过。在django中还没试过。
解决方案:
- 通过sql查询
- 创建一个视图
- 使用union方法
对比了三个方案,union方案最简单,而且符合django项目设计,所以就选了union方法来实现这个小需求。之所以不选择方案一,是因为我们的项目中,基本不使用原生sql语句。方案二不可行,是因为我们使用的数据库是mysql,使用视图不方便。
二、代码实现
1. 数据库模型
class Application(BaseModel):
"""应用"""
# 省略其他字段...
subscribed_users = models.ManyToManyField('User', through='ApplicationSubscription', related_name='subscribed_apps')
class Meta:
verbose_name = "应用"
class ApplicationSubscription(BaseModel):
"""应用订阅"""
user = models.ForeignKey("User", on_delete=models.CASCADE, db_constraint=False)
application = models.ForeignKey("Application", on_delete=models.CASCADE, db_constraint=False)
class Meta:
verbose_name = "应用订阅"
class CheckObject(BaseModel):
# 省略其他字段...
subscribed_users = models.ManyToManyField('User', through='CheckObjectSubscription',
related_name='subscribed_checkobjs')
class Meta:
verbose_name = "巡检对象"
class CheckObjectSubscription(BaseModel):
"""对象订阅"""
user = models.ForeignKey("User", on_delete=models.CASCADE, db_constraint=False)
check_object = models.ForeignKey("CheckObject", on_delete=models.CASCADE, db_constraint=False)
class Meta:
unique_together = (("user", "check_object"),)
verbose_name = "对象订阅"
2.union方法的代码实现
query_set的union方法其实对应的就是数据库中的union
操作。使用union操作,要有一个前提条件,就是两个query_set
的字段名必须一致。
def search_user_subscribe(self, request):
"""查询用户订阅的应用和对象"""
username = request.user.username
user_obj = User.objects.filter(account=username).first()
if not user_obj:
return Response({"detail": "用户不存在,请重新登录"}, status=500)
app_obj = user_obj.subscribed_apps.all().order_by().annotate(
type=Value('a', output_field=CharField(max_length=1)),
key=F('bk_application_key'),
name=F('bk_application_name'),
class_key=Value('', output_field=CharField(max_length=1))
)
app_obj = app_obj.values("id", "type", "key", "name", "class_key")
check_obj = user_obj.subscribed_checkobjs.all().order_by().annotate(
type=Value('c', output_field=CharField(max_length=1)),
key=F('bk_object_key'),
name=F('bk_object_name'),
class_key=F("bk_object_classification_key")
)
check_obj = check_obj.values("id", "type", "key", "name", "class_key")
subscribe_obj = app_obj.union(check_obj)
subscribe_obj = subscribe_obj.order_by("type", "name")
page = self.paginate_queryset(subscribe_obj)
if page is not None:
page_data = self.get_paginated_response(page)
return Response(page_data.data)
return Response(page)