Django中匹配用户收藏多对多流派字段并展示对应内容
问题描述
我正在开发一个包含三类帖子的项目:
- 电影(模型名
List) - 音乐(模型名
MusicList) - 书籍(模型名
BookList)
每类对应独立模型,且各自拥有专属流派模型:Genre(电影)、MusicGenre(音乐)、BookGenre(书籍)。
系统中有Profile模型,包含fav_music_genre、fav_movie_genre、fav_Book_genre三个多对多字段,用于存储用户收藏的流派。
需要在视图的all(首页)函数中编写代码,对比当前登录用户的上述收藏流派字段,仅展示流派匹配的List、MusicList和BookList内容。例如:若用户收藏了Action与Comedy流派,则只显示包含这两个流派的列表内容。
我是Python/Django新手,为此困惑了4-6天,找不到解决思路。以下是我的models.py代码:
class MusicList(models.Model): title = models.CharField(max_length=120) genre = models.ManyToManyField('MusicGenre') creator = models.ForeignKey(User,on_delete=models.SET_NULL,blank=True, null=True) posted = models.DateTimeField(auto_now_add=True) content = RichTextField(null=True,default=' ') type = models.CharField(max_length=10,default="Music") slug = models.SlugField(max_length= 300,null=True, blank = True, unique=True) def __str__(self): return f'{self.title}|{self.creator}' def save(self, *args, **kwargs): self.slug = slugify(self.title + str(self.posted)) super(MusicList,self).save(*args, **kwargs) class MusicGenre(models.Model): name = models.CharField(max_length=150) def __str__(self): return self.name class BookList(models.Model): title = models.CharField(max_length=120) creator = models.ForeignKey(User,on_delete=models.SET_NULL,blank=True, null=True) genre = models.ManyToManyField('BookGenre') posted = models.DateTimeField(auto_now_add=True) spoiler_choices = [(False, 'No'),(True, 'Yes')] spoiler = models.BooleanField(default=False,null=True, choices = spoiler_choices) content = RichTextField(null=True,default=' ') type = models.CharField(max_length=10,default="book") slug = models.SlugField(max_length= 300,null=True, blank = True, unique=True) def __str__(self): return f'{self.title}|{self.creator}' def save(self, *args, **kwargs): self.slug = slugify(self.title + str(self.posted)) super(BookList,self).save(*args, **kwargs) class BookGenre(models.Model): name = models.CharField(max_length=150) def __str__(self): return self.name class List(models.Model): title = models.CharField(max_length=120) genre = models.ManyToManyField('Genre') creator = models.ForeignKey(User,on_delete=models.SET_NULL,blank=True, null=True) posted = models.DateTimeField(auto_now_add=True) content = RichTextField(null=True,default=' ') type = models.CharField(max_length=10,default="Movie") spoiler_choices = [(False, 'No'),(True, 'Yes')] spoiler = models.BooleanField(default=False,null=True, choices = spoiler_choices) slug = models.SlugField(max_length= 300,null=True, blank = True, unique=True) def __str__(self): return f'{self.title}|{self.creator}' def save(self, *args, **kwargs): self.slug = slugify(self.title + str(self.posted)) super(List,self).save(*args, **kwargs) class Genre(models.Model): name = models.CharField(max_length=150) def __str__(self): return self.name class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, blank=True) bio = models.TextField(max_length=500, blank=True) location = models.CharField(max_length=30, blank=True) birth_date = models.DateField(null=True, blank=True) profile_pic = models.ImageField(blank=True, default='', upload_to='profiles_pics') fav_music_genre = models.ManyToManyField('MusicGenre') fav_Book_genre = models.ManyToManyField('BookGenre') fav_movie_genre = models.ManyToManyField('Genre') def __str__(self): return self.user.username + " | bio : " + self.bio @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): if created: Profile.objects.create(user=instance) @receiver(post_save, sender=User) def save_user_profile(sender, instance, **kwargs): instance.profile.save()
解决方案
步骤1:导入必要模块
在视图文件中导入相关模型与用户验证模块:
from django.shortcuts import render from .models import List, MusicList, BookList, Profile
步骤2:编写all视图函数
def all(request): # 初始化查询集,默认显示全部内容 movie_lists = List.objects.all() music_lists = MusicList.objects.all() book_lists = BookList.objects.all() # 处理登录用户的流派过滤逻辑 if request.user.is_authenticated: try: profile = request.user.profile # 获取用户收藏的各流派ID列表 fav_movie_ids = profile.fav_movie_genre.values_list('id', flat=True) fav_music_ids = profile.fav_music_genre.values_list('id', flat=True) fav_book_ids = profile.fav_Book_genre.values_list('id', flat=True) # 过滤电影列表:保留包含至少一个收藏流派的帖子 if fav_movie_ids: movie_lists = List.objects.filter(genre__in=fav_movie_ids).distinct() # 过滤音乐列表 if fav_music_ids: music_lists = MusicList.objects.filter(genre__in=fav_music_ids).distinct() # 过滤书籍列表 if fav_book_ids: book_lists = BookList.objects.filter(genre__in=fav_book_ids).distinct() except Profile.DoesNotExist: # 信号已自动创建Profile,此分支为兜底处理 pass # 将结果传递给模板 context = { 'movie_lists': movie_lists, 'music_lists': music_lists, 'book_lists': book_lists, } return render(request, 'your_template.html', context)
核心逻辑说明
values_list('id', flat=True):提取收藏流派的ID列表,用于后续过滤查询。filter(genre__in=...):Django多对多字段过滤语法,筛选出流派匹配用户收藏的帖子。.distinct():避免同一帖子因匹配多个收藏流派而重复显示。- 默认逻辑:未登录/无收藏流派时显示全部内容,可根据需求修改(比如提示用户登录或选择流派)。
模板使用示例
在模板中遍历过滤后的结果:
<h2>推荐电影</h2> {% for movie in movie_lists %} <div>{{ movie.title }} - {{ movie.genre.all|join:", " }}</div> {% empty %} <p>暂无匹配的电影内容</p> {% endfor %} <h2>推荐音乐</h2> {% for music in music_lists %} <div>{{ music.title }} - {{ music.genre.all|join:", " }}</div> {% empty %} <p>暂无匹配的音乐内容</p> {% endfor %} <h2>推荐书籍</h2> {% for book in book_lists %} <div>{{ book.title }} - {{ book.genre.all|join:", " }}</div> {% empty %} <p>暂无匹配的书籍内容</p> {% endfor %}
内容的提问来源于stack exchange,提问作者Ayat Rahman
相关产品推荐
相关产品推荐

