如何基于相同字段关联Django两模型并实现用户消息过滤?
模型关联逻辑修正与实现方案
一、核心逻辑梳理
你的需求是让用户查看自己拥有的模块的所有消息,但原模型设计存在逻辑错误:
- 原
ModulMess直接关联Owner,意味着每条消息只能绑定到某个用户的模块,但实际上模块的消息是属于模块本身的,和归属用户无关——只要用户拥有该模块,就能查看它的所有消息。 - 正确的关联链应该是:
用户 -> 模块归属关系(Owner) -> 模块(Module) -> 模块消息(ModulMess),通过这条链可以精准过滤出当前用户有权查看的消息。
二、修正后的模型设计
推荐采用更规范的ORM关联设计(单独抽离Module模型,便于扩展模块属性):
from django.db import models from django.contrib.auth.models import User from django.utils import timezone # 模块基础信息表,存储唯一的模块编号 class Module(models.Model): key = models.CharField(max_length=255, unique=True, verbose_name="模块编号") # 如moduleA、moduleB def __str__(self): return self.key # 用户-模块归属关系表,记录哪个用户拥有哪个模块 class Owner(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='owned_modules', verbose_name="所属用户") module = models.ForeignKey(Module, on_delete=models.CASCADE, verbose_name="关联模块") date_in = models.DateTimeField(default=timezone.now, verbose_name="绑定时间") class Meta: unique_together = ('user', 'module') # 限制一个用户不能重复绑定同一个模块 def __str__(self): return f"{self.user.username} | {self.module.key}" # 模块消息表,记录每个模块的消息 class ModulMess(models.Model): module = models.ForeignKey(Module, on_delete=models.CASCADE, related_name='messages', verbose_name="所属模块") date_ini = models.DateTimeField(default=timezone.now, verbose_name="消息时间") message = models.CharField(max_length=255, verbose_name="消息内容") def __str__(self): return f"{self.module.key} | {self.message}"
如果不想单独建Module模型,也可以简化为以下设计(直接用模块编号关联):
class Owner(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='owned_modules') modulkey = models.CharField(max_length=255, unique=True) # 模块编号唯一 date_in = models.DateTimeField(default=timezone.now) class ModulMess(models.Model): modulkey = models.CharField(max_length=255) date_ini = models.DateTimeField(default=timezone.now) message = models.CharField(max_length=255)
三、序列化器实现
基于规范模型设计的序列化器示例:
from rest_framework import serializers from .models import ModulMess, Module, Owner # 模块消息序列化器 class ModulMessSerializer(serializers.ModelSerializer): module_key = serializers.CharField(source='module.key') # 显示模块编号,而非模块ID class Meta: model = ModulMess fields = ['id', 'module_key', 'date_ini', 'message'] # 带消息列表的模块序列化器 class ModuleWithMessagesSerializer(serializers.ModelSerializer): messages = ModulMessSerializer(many=True, read_only=True) class Meta: model = Module fields = ['key', 'messages'] # 用户拥有的模块序列化器(返回模块及对应消息) class UserOwnedModulesSerializer(serializers.ModelSerializer): module = ModuleWithMessagesSerializer(read_only=True) class Meta: model = Owner fields = ['module']
四、视图实现
方式1:直接返回用户有权查看的所有消息列表
from rest_framework import generics from rest_framework.permissions import IsAuthenticated from .models import ModulMess, Owner from .serializers import ModulMessSerializer class UserModuleMessagesView(generics.ListAPIView): serializer_class = ModulMessSerializer permission_classes = [IsAuthenticated] # 仅登录用户可访问 def get_queryset(self): # 第一步:获取当前用户拥有的所有模块ID user_module_ids = Owner.objects.filter(user=self.request.user).values_list('module_id', flat=True) # 第二步:过滤出这些模块的所有消息 return ModulMess.objects.filter(module_id__in=user_module_ids)
方式2:返回用户的模块列表,每个模块包含对应的消息
class UserOwnedModulesView(generics.ListAPIView): serializer_class = UserOwnedModulesSerializer permission_classes = [IsAuthenticated] def get_queryset(self): # 预加载关联数据,减少数据库查询次数 return Owner.objects.filter(user=self.request.user).select_related('module').prefetch_related('module__messages')
如果用简化版模型(无单独Module表),视图的get_queryset改为:
def get_queryset(self): user_modulkeys = Owner.objects.filter(user=self.request.user).values_list('modulkey', flat=True) return ModulMess.objects.filter(modulkey__in=user_modulkeys)
内容的提问来源于stack exchange,提问作者Jon Mikel Etxebarria
相关产品推荐
相关产品推荐

