Django条件筛选搜索

问题描述

COMP9900大作业做一个电影搜索平台,将搜索结果按照电影种类筛选查看是必不可少的功能。
任务目标: 以用户的评论过的所有电影包含的电影流派为推荐依据,转化为标签,让用户可以按照标签筛选电影库中匹配的结果
在这里插入图片描述

解决流程

1.数据库

login/models.py

from django.db import models
from movie.models import Movie
from datetime import datetime

# Create your models here.
# login/models.py

from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    '''User Info'''
    id = models.AutoField(primary_key=True)
    email = models.EmailField(unique=True)
    username = models.CharField(max_length=128, unique=True, verbose_name='User Name')
    info = models.TextField(verbose_name="Personal profile", default='Hi!')
    REQUIRED_FIELDS = ['email']
    USERNAME_FIELD = 'username'
    password = models.CharField(max_length=256)
    c_time = models.DateTimeField(auto_now_add=True, verbose_name='Create time')
    wishlist = models.ManyToManyField(Movie)
    # is_authenticated=True
    # is_anonymous=False
    is_active=True
    # is_staff=True
    # has_module_perms=True
    def __str__(self):
        return self.username

    class Meta:
        ordering = ['id']
        unique_together = (("username", "email"),)
        verbose_name = 'Users'
        verbose_name_plural = 'Users'


class Comment(models.Model):
    star = models.FloatField(verbose_name='star', default=0)
    content = models.TextField(verbose_name="review content", default='')
    movie = models.ForeignKey(Movie, default='', on_delete=models.CASCADE, verbose_name='movie')
    user = models.ForeignKey(User, default='',  on_delete=models.CASCADE, verbose_name='user')
    add_time = models.DateTimeField(default=datetime.now, verbose_name='add_time')


    class Meta:
        ordering = ['user']
        verbose_name = 'Comment'
        verbose_name_plural = verbose_name
    def __str__(self):
        return self.user.username

movie/models.py

from django.db import models
from urllib import request
import ssl

# Create your models here.

# Movie genres tag
class Tag(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=100, verbose_name='名称')

    class Meta:
        indexes = [
            models.Index(fields=['id', 'name']),
            models.Index(fields=['id'], name='genres_id_idx'),
        ]
        verbose_name = 'Genre'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

class Director(models.Model):

    id = models.TextField(verbose_name="director_id",primary_key=True)
    name = models.CharField(max_length=100, verbose_name='Name')

    class Meta:
        indexes = [
            models.Index(fields=['id', 'name']),
            models.Index(fields=['id'], name='id_idx'),
        ]
        verbose_name = 'director'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class Movie(models.Model):
    """
    movie info
    """
    id = models.TextField(verbose_name="IMDBID",primary_key=True)

    # id = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='Title', max_length=200,default='')
    director = models.TextField(verbose_name="导演", default='',blank=True,null=True)
    directors = models.ManyToManyField('Director',related_name='director_movie')

    # tag= models.ForeignKey(Tag, default='', on_delete=models.DO_NOTHING, verbose_name='类型标签')
    genres = models.ManyToManyField('Tag')
    genres_text = models.TextField(verbose_name="genres in text", default='',blank=True,null=True)
    info = models.TextField(verbose_name="电影简介", default='',blank=True,null=True)
    year = models.CharField('Year', max_length=10, blank=True, null=True)
    run_time = models.CharField('Run Time', max_length=10, blank=True, null=True)
    logo = models.ImageField(upload_to='banner/%Y/%m', default='image/default.png',blank=True,null=True, max_length=100, verbose_name='封面')
    star = models.FloatField(verbose_name='星级',default=0)
    comment_nums = models.IntegerField(verbose_name='评论数',default=0)
    img_path = models.TextField(verbose_name="image path", default='',blank=True)
    # add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')

    def image_data(self):
        head = {}

        context = ssl._create_unverified_context()

        head['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15'
        # 创建Request对象并添加heads
        req = request.Request('https://api.themoviedb.org/3/find/'
                              +self.id+'?api_key=1a0eeda873912b473a595640ef04b25e&language=en-US&external_source=imdb_id', headers=head)
        # 传入创建好的Request对象
        response = request.urlopen(req,context=context)
        # 读取响应信息并解码
        html = response.read().decode('utf-8')
        img_url = html.split('"poster_path":"')[1].split('"')[0]
        # 打印爬到的信息
        return 'https://image.tmdb.org/t/p/w500/' + img_url
    def image_url(self):

        # img_addr = format_html('https://api.themoviedb.org/3/find/{}?api_key=1a0eeda873912b473a595640ef04b25e&language=en-US&external_source=imdb_id',self.imdb)

        return 'https://image.tmdb.org/t/p/w500/' + '2pwwawlnti0BoluxFiksnVg5TZ7.jpg'
    def __str__(self):
        return self.title  # 主要__str__最好是个字符串,不然你会遇到很多的坑,还有我们返回的这两个字段填写数据的时候必须写上数据,必然相加会报错,null类型和str类型不能相加等错误信息。

    class Meta:
        verbose_name = 'Movie'
        verbose_name_plural = verbose_name

Tag类存的是电影种类,Movie和Tag是多对多(ManyToMany)的关系,一个电影可以是喜剧片也可以是恐怖片,同样一个种类也对应许多不同的电影。

2.url映射

第二步就要想好怎么去传递用户点击的是哪一个种类这个信息,这里我采用的方法是在url路径后加上"-xx"用数字来对应种类的id,在数据库中,Tag的形式是如下图的在这里插入图片描述
因此“-1”代表 Thriller,“-3”就代表Horror,因为一共有27个种类,我选择50作为全部种类(不筛选)的代表数字。
movie/urls.py

from django.urls import path
from django.conf import settings
from django.conf.urls.static import static

from django.conf.urls import url,include
from movie import views

urlpatterns = [

    path('', views.IndexView, name='index'),
    url(r'^search/(?P<keywords>.*)/$', views.SearchView.as_view(), name='search'),
    url(r'^searchByGenresOrDirector/(?P<keywords>.*)/$', views.SearchByGenresView.as_view(), name='search_gd'),

    path('logout/', views.Logout, name='logout'),
    url(r'^detail/(?P<movie_id>.*)-(?P<genre_id>\d+)/$', views.MovieDetailView.as_view(), name="detail"),
    url(r'^add_wishlist/(?P<movie_id>.*)/$', views.Add_wishlist.as_view(), name='add_wishlist'),
    url(r'^comment/$', views.CommentView.as_view(), name='comment'),
    ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

只需看detail那一列即可。

3. 后端逻辑

一般我习惯先写出后端代码再往前端填,也不知道是不是正确的顺序。
因为作业中要求拉黑用户不计入电影平均分,所以这部分还要加入计算电影评分的代码,有些冗长,几个部分已经注释标出,只需看Recommendation部分即可
用字典q来传递命令是个很出乎我意料的操作,我也是从网上看的,可能因为第一次用Django,觉得很神奇

class MovieDetailView(View):
    """
    Movie Details
    """
    def get(self, request, movie_id, genre_id):
        """
        主体部分
        """
        movie = Movie.objects.get(id=movie_id)
        username = request.COOKIES['username']
        user = User.objects.get(username=username)
        if_in_wishlist = movie in user.wishlist.all()
        if_commented = Comment.objects.filter(movie=movie_id,user=user)
        try:
            bannedlist = Bannedlist.objects.get(user=user).banned_user.all()
        except:
            bannedlist = ''
        comments = Comment.objects.filter(Q(movie=movie_id) & ~Q(user__in=bannedlist))
        if comments:
            star = sum([c.star for c in comments]) / len([c.star for c in comments])
        else:
            star = 0
        try:
            page = request.GET.get('page', 1)
        except PageNotAnInteger:
            page = 1
        p = Paginator(comments, 8, request=request)
        comments = p.page(page)
        """
        Recommendation部分
        """
        request_path = request.path
        q = {} #筛选条件的字典

        genre_list = Comment.objects.filter(Q(user=user)).values('movie').values('movie__genres','movie__genres__name')
        if genre_id == '50':
            # all the genres
            pass
        else:
            # select one genres
            genre = Tag.objects.get(id=int(genre_id))
            commented_movies_genres = Comment.objects.filter(Q(user=user)).values('movie').values('movie__genres__id') # 感兴趣的genreid
            q['genres__in'] = [genre_id,]
            # request_path = request_path.split('-')[0]+str(genre_id)

        recommendation_movies = Movie.objects.filter(**q).order_by('star','title')[:20]
        try:
            page = request.GET.get('page', 1)
        except PageNotAnInteger:
            page = 1
        p_2 = Paginator(recommendation_movies, 3, request=request)
        recommendation_movies = p_2.page(page)

        """
        movie rate
        """
        movies = Movie.objects.filter(**q).order_by('star','title')[:20]
        star_dic = dict()
        for m in movies:
            movie_id = m.id
            username = request.COOKIES['username']
            user = User.objects.get(username=username)
            try:
                bannedlist = Bannedlist.objects.get(user=user).banned_user.all()
            except:
                bannedlist = ''
            comment = Comment.objects.filter(Q(movie=movie_id) & ~Q(user__in=bannedlist))
            if comment:
                star = sum([c.star for c in comment]) / len([c.star for c in comment])
                star_dic[movie_id] = star
            else:
                star_dic[movie_id] = 0


        return render(request, 'movie/details.html', {"movie": movie, "comments": comments,
                                                      'if_in_wishlist': if_in_wishlist, "if_commented": if_commented,
                                                      "star":star, "recommendation_movies": recommendation_movies,
                                                      "current_url":request_path,"genre_list": genre_list,"star_dic":star_dic})
4. 前端代码

对我这个初学者来说也是难点之一,要自己定义模板标签simple_tag。
如果不知道这个是什么,参考一下我的上篇文章

movie/templatetags/movie_extras.py

from django.utils.safestring import mark_safe
from django import template

register = template.Library()
@register.simple_tag
def action_all(current_url): 
    """
    获取当前url,.../detail/tt000000-1/
    :param current_url:.../detail/tt000000-1/
    :return: all
    """
    url_part_list = current_url.split('-')

    if url_part_list[1].strip('/') == "50":
        temp = "<a href='%s' class='active'>ALL</a>"
    else:
        temp = "<a href='%s'>ALL</a>"

    url_part_list[1] = "50"

    href = '-'.join(url_part_list)

    temp = temp % (href,) #把链接填进去
    return mark_safe(temp)


@register.simple_tag
def action(current_url, item):
    # .../detail/tt000000-1/
    # item: genres_id genre_name
    url_part_list = current_url.split('-')

    if str(item['movie__genres']) == url_part_list[1].strip('/'):
        temp = "<a href='%s' class='active'>%s</a>"
    else:
        temp = "<a href='%s'>%s</a>"

    url_part_list[1] = str(item['movie__genres'])

    ur_str = '-'.join(url_part_list)  # 拼接整体url
    temp = temp % (ur_str, item['movie__genres__name'])  # 生成对应的a标签
    return mark_safe(temp)  # 返回安全的html

movie/templates/movie/details.py

<ul>
<li><span>{% action_all current_url%}</span></li>
{% for item in genre_list %}
<li><span>{% action current_url item %}</span></li>
{% endfor %}
</ul>

主要代码就是这些,参考学习了这篇大佬的文章真的很感谢,这篇文章里情况更加复杂,是多层筛选的,方法是在url后面多加几个"-",代码也会更加复杂。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值