基于Django的图书推荐管理系统(附源码)

2,786 阅读19分钟

demo体验传送门 vx 1257309054

一、搭建项目

python解释器版本使用3.7.8。

1、创建虚拟环境

在d盘下创建一个文件夹my_work,然后在里面创建两个文件夹:provenv

win+R输入cmd进入文件夹venv,然后执行以下命令创建虚拟环境:

python -m venv book_manager

激活虚拟环境:

cd book_manager
cd Scripts
activate

导入django:

pip install Django==3.0.7 -i https://pypi.mirrors.ustc.edu.cn/simple/
pip install PyMySQL==0.9.2 -i https://pypi.mirrors.ustc.edu.cn/simple/
pip install xadmin-django==3.0.2 -i https://pypi.mirrors.ustc.edu.cn/simple/
pip install mysqlclient==2.0.1 -i https://pypi.mirrors.ustc.edu.cn/simple/

然后进入pro目录

cd ..
cd ..
cd ..
cd pro

2、创建项目

执行命令:

django-admin startproject book_manager

3、创建子应用

切换到项目根目录:

cd book_manager

创建子应用

python manage.py startapp book

自此项目创建完成。

二、settings.py配置

1、创建数据库

使用MySQL可视化工具创建一个数据库book_manager

在这里插入图片描述

2、PyCharm打开项目

使用PyCharm打开项目:file->open.

在项目根目录下创建以下文件夹:

static、media、imgs、log、template。

其中media中再创建一个文件夹book_cover存放书籍封面。

template,选中template->右键->Make Directory as- >Template Folder.

在这里插入图片描述

3、配置项目虚拟环境

在这里插入图片描述

4、允许所有网站访问

book_manager\settings.py中做修改:

ALLOWED_HOSTS = ['*']

5、添加子应用

book_manager\settings.py中的INSTALLED_APPS加入子应用book,如下:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'xadmin',
    'crispy_forms',
    'book'
]

6、添加template目录

book_manager\settings.py中的TEMPLATES中DIRS改为:

'DIRS': [os.path.join(BASE_DIR, 'template')],

7、使用mysql数据库

把在book_manager\settings.py中的DATABASES注释掉,改为:

ip = '127.0.0.1'
DATABASE_NAME = 'book_manager'  # mysql数据库名称
DATABASE_USER = 'root'  # mysql数据库用户名
DATABASE_PASS = 'xxx'  # mysql数据库密码
DATABASE_HOST = ip  # mysql数据库IP
DATABASE_PORT = 3306  # mysql数据库端口

# 配置数据库
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  # 修改数据库为MySQL,并进行配置
        'NAME': DATABASE_NAME,  #
        'USER': DATABASE_USER,  # 用户名
        'PASSWORD': DATABASE_PASS,  # 密码
        'HOST': DATABASE_HOST,
        'PORT': DATABASE_PORT,
        'OPTIONS': {'charset': 'utf8mb4', }
    }
}

8、使用中文

book_manager\settings.py的LANGUAGE_CODE、TIME_ZONE和USE_TZ改为:

LANGUAGE_CODE = 'zh-hans'  # 使用中国时区

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False

9、配置静态文件路由

book_manager\settings.py的STATIC_URL改为:

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]
# STATIC_ROOT = os.path.join(BASE_DIR, 'static')  # 收集静态文件时打开,然后关闭STATICFILES_DIRS

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_USER_ICON = os.path.join(BASE_DIR, 'media/user_icon')

三、models.py数据表

book\models.py中创建用户、标签表、书籍表、购书清单车、评分表、收藏表、点赞表、评论表:

from django.db import models


# Create your models here.
class User(models.Model):
    username = models.CharField(max_length=32, unique=True, verbose_name="账号")
    password = models.CharField(max_length=32, verbose_name="密码")
    phone = models.CharField(max_length=32, verbose_name="手机号码")
    name = models.CharField(max_length=32, verbose_name="名字", unique=True)
    address = models.CharField(max_length=32, verbose_name="地址")
    email = models.EmailField(verbose_name="邮箱")

    class Meta:
        db_table = 'user'
        verbose_name_plural = "用户"
        verbose_name = "用户"

    def __str__(self):
        return self.name


class Tags(models.Model):
    name = models.CharField(max_length=32, verbose_name="标签")
    intro = models.TextField(blank=True, null=True, verbose_name='简介')

    class Meta:
        db_table = 'tags'
        verbose_name = "标签"
        verbose_name_plural = "标签"

    def __str__(self):
        return self.name


class Book(models.Model):
    tags = models.ForeignKey(
        Tags,
        on_delete=models.CASCADE,
        verbose_name="标签",
        related_name="tags",
        blank=True,
        null=True,
    )

    title = models.CharField(verbose_name="书名", max_length=32)
    author = models.CharField(verbose_name="作者", max_length=32)
    published_time = models.DateField(blank=True, null=True, verbose_name='出版时间')
    intro = models.TextField(verbose_name="描述")
    pic = models.FileField(verbose_name="封面图片", max_length=64, upload_to='book_cover')
    collect_num = models.IntegerField(verbose_name="收藏人数", default=0)
    rate_num = models.IntegerField(verbose_name="评分人数", default=0)
    like_num = models.IntegerField(verbose_name="点赞人数", default=0)
    look_num = models.IntegerField(verbose_name="浏览量", default=0)
    is_show = models.BooleanField(default=True, verbose_name="是否显示")
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    is_purchase = models.BooleanField(default=False, verbose_name='是否可购买')
    price = models.FloatField(default=66.66, verbose_name="单价")
    inventory = models.IntegerField(verbose_name="库存", default=9999, blank=True, null=True)
    purchase_num = models.IntegerField(verbose_name="销售数量", default=0, blank=True, null=True)

    class Meta:
        db_table = 'book'
        verbose_name = "图书"
        verbose_name_plural = "图书"

    def __str__(self):
        return self.title


class PurchaseList(models.Model):
    # 购书清单
    book = models.ForeignKey(Book, related_name='purchase_book', on_delete=models.CASCADE, blank=True, null=True,
                             verbose_name="图书id")
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, verbose_name="用户id")
    order_code = models.CharField(max_length=20, unique=True, verbose_name='订单号')
    create_time = models.DateTimeField(verbose_name="下单时间", auto_now_add=True)
    price = models.FloatField(default=66.66, verbose_name="单价")
    purchase_num = models.IntegerField(verbose_name="购买数量", default=1)
    all_price = models.FloatField(default=66.66, verbose_name="总价")
    phone = models.CharField(max_length=64, blank=True, null=True, verbose_name="手机号")
    address = models.TextField(blank=True, null=True, verbose_name="地址")
    order_status = models.CharField(max_length=10, default='0',
                                    choices=(
                                        ('0', '待支付'),
                                        ('1', '已支付'),
                                        ('2', '待收货'),
                                        ('3', '已收货'),
                                    ), verbose_name='订单状态')
    pay_time = models.DateTimeField(blank=True, null=True, verbose_name="支付时间")
    received_time = models.DateTimeField(blank=True, null=True, verbose_name="收货时间")

    class Meta:
        db_table = 'purchase_list'
        verbose_name = "购书清单"
        verbose_name_plural = verbose_name


class RateBook(models.Model):
    book = models.ForeignKey(Book, related_name='rate_book', on_delete=models.CASCADE, blank=True, null=True,
                             verbose_name="图书id")
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, verbose_name="用户id")
    mark = models.FloatField(verbose_name="评分")
    create_time = models.DateTimeField(verbose_name="添加时间", auto_now_add=True)

    class Meta:
        db_table = 'rate_book'
        verbose_name = "评分信息"
        verbose_name_plural = verbose_name

class CollectBook(models.Model):
    book = models.ForeignKey(
        Book, on_delete=models.CASCADE, related_name='collect_book', blank=True, null=True, verbose_name="图书id"
    )
    user = models.ForeignKey(
        User, on_delete=models.CASCADE, blank=True, null=True, verbose_name="用户id",
    )
    is_delete = models.BooleanField(default=False, verbose_name='是否取消')
    create_time = models.DateTimeField(verbose_name="收藏时间", auto_now_add=True)

    class Meta:
        db_table = 'collect_book'
        verbose_name = "图书收藏"
        verbose_name_plural = verbose_name


class LikeBook(models.Model):
    book = models.ForeignKey(
        Book, on_delete=models.CASCADE, related_name='like_book', blank=True, null=True, verbose_name="图书id")
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, verbose_name="用户id", )
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')
    create_time = models.DateTimeField(verbose_name="点赞时间", auto_now_add=True)

    class Meta:
        db_table = 'like_book'
        verbose_name = "图书点赞"
        verbose_name_plural = verbose_name


class CommentBook(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, verbose_name="用户")
    book = models.ForeignKey(Book, on_delete=models.CASCADE, blank=True, null=True, verbose_name="书籍")
    content = models.TextField(verbose_name="内容")
    create_time = models.DateTimeField(verbose_name='评论时间', auto_now_add=True)
    like_num = models.IntegerField(verbose_name="点赞数", default=0)
    like_users = models.TextField(null=True,blank=True, default=None,verbose_name="点赞用户")
    is_show = models.BooleanField(default=True, verbose_name='是否显示')

    class Meta:
        db_table = 'comment_book'
        verbose_name = "图书评论"
        verbose_name_plural = verbose_name

四、urls.py路由配置

1、修改book_manager\urls.py

book下创建一个urls.py,并让book_manager\urls.py分配路由,

其中book_manager\urls.py改为:

import xadmin
from django.urls import path, re_path, include
from django.views.generic import RedirectView
from django.views.static import serve
from django.conf import settings

urlpatterns = [
    path('xadmin/', xadmin.site.urls),
    path("", include("book.urls")),
    # favicon.cio
    re_path(r'^favicon\.ico$', RedirectView.as_view(url=r'media/favicon.ico')),
    re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
    # re_path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATICFILES_DIRS}), # 收集静态文件时关闭
    path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATIC_ROOT}), # 收集静态文件时打开,然后关闭STATICFILES_DIRS
]

2、修改book\urls.py

先改为:


from django.urls import path, re_path

from book import views

urlpatterns = [

]

后期会添加各种路由。

3、数据迁移

在pycharm左下角的Terminal里执行数据迁移命令

python manage.py makemigrations
python manage.py migrate

4、创建缓存表

python manage.py createcachetable

5、收集静态文件

先把book_manager\settings.py中的静态文件路由改为:

STATIC_URL = '/static/'
# STATICFILES_DIRS = [
#     os.path.join(BASE_DIR, 'static'),
# ]
STATIC_ROOT = os.path.join(BASE_DIR, 'static')  # 收集静态文件时打开,然后关闭STATICFILES_DIRS

然后执行:

python manage.py collectstatic

执行成功后,把book_manager\settings.py中的静态文件路由改为:

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]
# STATIC_ROOT = os.path.join(BASE_DIR, 'static')  # 收集静态文件时打开,然后关闭STATICFILES_DIRS

book_manager\urls.py改为:

import xadmin
from django.urls import path, re_path, include
from django.views.generic import RedirectView
from django.views.static import serve
from django.conf import settings

urlpatterns = [
    path('xadmin/', xadmin.site.urls),
    path("", include("book.urls")),
    # favicon.cio
    re_path(r'^favicon\.ico$', RedirectView.as_view(url=r'media/favicon.ico')),
    re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
    re_path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATICFILES_DIRS}), # 收集静态文件时关闭
    # path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATIC_ROOT}), # 收集静态文件时打开,然后关闭STATICFILES_DIRS
]

6、创建后台管理员

python manage.py createsuperuser
设置账号为 root
邮箱为 1@qq.com
密码为 book-root

五、导入基础数据

把根目录下的book.sql在mysql可视化工具中执行即可。

六、核心代码

1、static创建文件夹

static目录下创建三个文件夹imagecssjsfonts用来存放前端需要使用到的文件。

2、base.html前端框架

在目录templates下创建前端页面框架base.html,代码如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="/media/books.png">
    <title>图书推荐系统</title>
    {% block style %}
    {% endblock %}
    <!-- Bootstrap core CSS -->
    <link href="/static/css/bootstrap.min.css" rel="stylesheet">
    <!-- Custom styles for this template -->
    <link href="/static/css/dashboard.css" rel="stylesheet">
    <link href="/static/css/custom.css" rel="stylesheet">
    {% block extrastyle %}
    {% endblock %}
    <!-- Just for debugging purposes. Don't actually copy these 2 lines! -->
    <!--[if lt IE 9]>
    <script src="../../assets/js/ie8-responsive-file-warning.js"></script><![endif]-->
    <script src="/static/js/ie-emulation-modes-warning.js"></script>
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
    <script src="/static/js/html5shiv.min.js"></script>
    <script src="/static/js/respond.min.js"></script>
    <![endif]-->

</head>

<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container-fluid">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
                    aria-expanded="false" aria-controls="navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/">图书推荐系统</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav navbar-right">
                {% if request.session.login_in == True %}
                    <li><a href="{% url 'personal' %}">{{ request.session.name }}</a></li>
                    <li><a href="{% url 'logout' %}">退出</a></li>
                {% else %}
                    <li><a href="{% url 'login' %}">登录</a></li>
                    <li><a href="{% url 'register' %}">注册</a></li>
                {% endif %}
            </ul>
            <form class="navbar-form navbar-right" action="{% url 'search' %}" method='post'>
                {% csrf_token %}
                <label for="search"></label>
                <input id="search" type="text" class="form-control" name="search" placeholder="输入关键字"/>
                <button class="btn btn-default" type="submit">提交</button>
            </form>
        </div>
    </div>
</nav>
{% block content-nav %}{% endblock %}
<div class="container-fluid">
    <div class="row" >
        <div class="col-sm-3 col-md-2 sidebar">
            <ul class="nav nav-sidebar">
                <li class="active"><a href="{% url 'all_book' %}">全部书籍<span class="sr-only">(current)</span></a></li>
                <li><a href="{% url 'new_book' %}">新书速递</a></li>
                <li><a href="{% url 'hot_book' %}">热门书籍</a></li>
                <li><a href="{% url 'sort_book' %}">图书分类</a></li>
                <li><a href="{% url 'recommend_book' %}">猜你喜欢</a></li>
                <li><a href="{% url 'purchase_index' %}">购买书籍</a></li>
                <li><a href="{% url 'shop_list' %}">购物车</a></li>
                <li><a href="{% url 'personal' %}">个人中心</a></li>
            </ul>
        </div>
        <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main" style="margin-right:0;padding-right:0;">
            {% block right-panel-content %}
            {% endblock %}
        </div>
    </div>
</div>


<script src="/static/js/jquery-2.1.1.min.js"></script>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script src="/static/js/ie10-viewport-bug-workaround.js"></script>
<script src="/static/js/custom.js"></script>
<script src="/static/js/plugins/highstock/js/highstock.js"></script>
<script src="/static/js/plugins/highstock/js/modules/exporting.js"></script>
<script type="text/javascript">
    window.__user_media_prefix__ = "/media/";
    window.__user_path_prefix__ = "";
    window.__user_language_code__ = "";
    $(function ($) {
        {#    导航栏按钮渲染#}
        $(".sidebar").find("li").each(function () {
            var a = $(this).find("a:first")[0];
            if ($(a).attr("href") === location.pathname) {
                $(this).addClass("active");
            } else {
                $(this).removeClass("active");
            }
        });
    });
</script>
{% block bottom-js %}
{% endblock %}
</body>
</html>

3、all_book.html全部书籍

template下创建all_book.html页面:

{% extends 'base.html' %}
{% block right-panel-content %}
    <h3 class="text-center">{{ title }}</h3>
    {% if  not books %}
        <h3 class="text-center">对不起没有书籍</h3>
    {% endif %}

    {% for book in books %}

        <div class="container-fluid">
            <div class="row clearfix">
                <div class="col-md-2 column">
                    <img class="img-thumbnail book-image" alt="140x140" src="/media/{{ book.pic }}" width=140px
                         height=140px/>
                </div>
                <div class="col-md-7 column">
                    <h3>
                        <a href="{% url 'book' book.id %}">    {{ book.title }}</a>
                    </h3>
                    <p>{{ book.intro | slice:":100" }}......</p>
                    <p>
                        <strong>
                            <span style="margin-right: 2px">浏览量: </span>
                            {{ book.look_num  }}
                        </strong>
                        <strong>
                            <span style="margin-right: 2px">点赞量: </span>
                            {{ book.like_num  }}
                        </strong><strong>
                            <span style="margin-right: 2px">收藏量: </span>
                            {{ book.collect_num  }}
                        </strong>
                    </p>

                </div>
            </div>
        </div>
    {% endfor %}
    <div class="container-fluid">
        <ul class="pagination" id="pager">
            {#上一页按钮开始#}
            {# 如果当前页有上一页#}
            {% if books.has_previous %}
                {#  当前页的上一页按钮正常使用#}
                <li class="previous"><a href="{{ path }}?page={{ books.previous_page_number }}">上一页</a></li>
            {% else %}
                {# 当前页的不存在上一页时,上一页的按钮不可用#}
                <li class="previous disabled"><a href="#">上一页</a></li>
            {% endif %}
            {#上一页按钮结束#}
            {# 页码开始#}
            <li class="item active"><a href="{{ path }}?page={{ books.number }}">{{ books.number }}</a></li>
            {#            <li class="item"><a href="{{ path }}?page={{ book.number|add:1 }}">{{ book.number|add:1 }}</a></li>#}
            {#            <li class="item"><a href="{{ path }}?page={{ book.number|add:2 }}">{{ book.number|add:2 }}</a></li>#}
            {#页码结束#}
            {# 下一页按钮开始#}
            {% if books.has_next %}
                <li class="next"><a href="{{ path }}?page={{ books.next_page_number }}">下一页</a></li>
            {% else %}
                <li class="next disabled"><a href="#">下一页</a></li>
            {% endif %}
            <li class="item"><a href="#">
                {{ books.number }}/{{ books.paginator.num_pages }}</a>
            </li>

        </ul>
    </div>
{% endblock %}

3、book/urls.py创建基础路由

book/urls.py创建搜索、全部书籍、新书速递、热门书籍、图书分类、猜你喜欢、购买书籍、购物车、个人中心等路由,代码如下:

# !/usr/bin/python
# -*- coding: utf-8 -*-

from django.urls import path

from book import views

urlpatterns = [
    path("", views.index, name="index"),  # 首页
    path("login/", views.login, name="login"),  # 登录
    path("register/", views.register, name="register"),  # 注册
    path("logout/", views.logout, name="logout"),  # 退出
    path("modify_pwd/", views.modify_pwd, name="modify_pwd"),  # 修改密码
    path("search/", views.search, name="search"),  # 搜索
    path("all_book/", views.all_book, name="all_book"),  # 所有书籍
    path("book/<int:book_id>/", views.book, name="book"),  # 具体的书籍
    path("score/<int:book_id>/", views.score, name="score"),  # 评分
    path("comment/<int:book_id>/", views.comment, name="comment"),  # 评论
    path("comment_like/<int:comment_id>/", views.comment_like, name="comment_like"),  # 给评论点赞
    path("collect/<int:book_id>/", views.collect, name="collect"),  # 收藏
    path("like/<int:book_id>/", views.like, name="like"),  # 点赞
    path("new_book/", views.new_book, name="new_book"),  # 新书速递
    path("hot_book/", views.hot_book, name="hot_book"),  # 热门书籍
    path("sort_book/", views.sort_book, name="sort_book"),  # 图书分类
    path("recommend_book/", views.recommend_book, name="recommend_book"),  # 猜你喜欢
    path("purchase_index/", views.purchase_index, name="purchase_index"),  # 购买书籍首页
    path("purchase_check/<int:book_id>/", views.purchase_check, name="purchase_check"),  # 查看购买书籍详情
    path("purchase/", views.purchase, name="purchase"),  # 立即购买书籍
    path("purchase_pay/", views.purchase_pay, name="purchase_pay"),  # 支付
    path("add_shop_list/", views.add_shop_list, name="add_shop_list"),  # 添加到购物车
    path("sub_shop_list/", views.sub_shop_list, name="sub_shop_list"),  # 移除购物车书籍
    path("shop_list_pay/", views.shop_list_pay, name="shop_list_pay"),  # 购物车结算
    path("shop_list/", views.shop_list, name="shop_list"),  # 购物车
    path("personal/", views.personal, name="personal"),  # 个人中心
    path("my_like/", views.my_like, name="my_like"),  # 获取我的点赞
    path("my_collect/", views.my_collect, name="my_collect"),  # 获取我的收藏
    path("my_rate/", views.my_rate, name="my_rate"),  # 我打分过的书籍
    path("my_comments/", views.my_comments, name="my_comments"),  # 我的评论
    path("delete_rate/<int:rate_id>", views.delete_rate, name="delete_rate"),  # 取消评分
    path("delete_comment/<int:comment_id>", views.delete_comment, name="delete_comment"),  # 取消评论

]

在book\views.py中为每个路由创建响应。

4、登录

def login(request):
    if request.method == "POST":
        form = Login(request.POST)
        if form.is_valid():
            username = form.cleaned_data["username"]
            password = form.cleaned_data["password"]
            result = User.objects.filter(username=username)
            if result:
                user = User.objects.get(username=username)
                if user.password == password:
                    request.session["login_in"] = True
                    request.session["user_id"] = user.id
                    request.session["name"] = user.name
                    return redirect(reverse("all_book"))
                else:
                    return render(
                        request, "login.html", {"form": form, "error": "账号或密码错误"}
                    )
            else:
                return render(
                    request, "login.html", {"form": form, "error": "账号不存在"}
                )
    else:
        form = Login()
        return render(request, "login.html", {"form": form})

在这里插入图片描述

5、注册

def register(request):
    if request.method == "POST":
        form = RegisterForm(request.POST)
        error = None
        if form.is_valid():
            username = form.cleaned_data["username"]
            password = form.cleaned_data["password2"]
            email = form.cleaned_data["email"]
            name = form.cleaned_data["name"]
            phone = form.cleaned_data["phone"]
            address = form.cleaned_data["address"]
            User.objects.create(
                username=username,
                password=password,
                email=email,
                name=name,
                phone=phone,
                address=address,
            )
            # 根据表单数据创建一个新的用户
            return redirect(reverse("login"))  # 跳转到登录界面
        else:
            return render(
                request, "register.html", {"form": form, "error": error}
            )  # 表单验证失败返回一个空表单到注册页面
    form = RegisterForm()
    return render(request, "register.html", {"form": form})

在这里插入图片描述

6、登出

def logout(request):
    if not request.session.get("login_in", None):  # 不在登录状态跳转回首页
        return redirect(reverse("index"))
    request.session.flush()  # 清除session信息
    return redirect(reverse("index"))

7、修改密码

@login_in
def modify_pwd(request):
    # 获取我的信息
    user = User.objects.get(id=request.session.get("user_id"))
    if request.method != "POST":
        return render(request, '404.html')
    form = Edit(instance=user, data=request.POST)
    if form.is_valid():
        form.save()
        # return redirect(reverse("personal"))
        return render(request, "personal.html", {"inform_message": "修改成功", "inform_type": "success", "form": form})
    else:
        return render(request, "personal.html", {"inform_message": "修改失败", "inform_type": "danger", "form": form})

在这里插入图片描述

8、搜索

def search(request):  # 搜索
    if request.method == "POST":  # 如果搜索界面
        key = request.POST["search"]
        request.session["search"] = key  # 记录搜索关键词解决跳页问题
    else:
        key = request.session.get("search")  # 得到关键词
    # 进行内容的模糊搜索
    books = Book.objects.filter(Q(title__icontains=key) | Q(intro__icontains=key) | Q(author__icontains=key))
    page_num = request.GET.get("page", 1)
    books = books_paginator(books, page_num)
    return render(request, "all_book.html", {"books": books})

9、所有书籍

def all_book(request):
    # 按收藏数进行排序
    books = Book.objects.annotate(user_collector=Count('collect_num')).order_by('-user_collector')
    paginator = Paginator(books, 9)
    current_page = request.GET.get("page", 1)
    books = paginator.page(current_page)
    return render(request, "all_book.html", {"books": books, "title": "所有书籍"})

在这里插入图片描述

10、具体的书籍

def book(request, book_id):
    # 获取具体的书籍
    user_id = request.session.get("user_id")
    book = Book.objects.get(pk=book_id)
    book.look_num += 1
    book.save()
    comments = book.commentbook_set.order_by("-create_time")
    rate = RateBook.objects.filter(book=book).aggregate(Avg("mark")).get("mark__avg", 0)
    rate = rate if rate else 0
    book_rate = round(rate, 2)

    if user_id:
        user = User.objects.get(pk=user_id)
        is_collect = True if book.collect_book.filter(user_id=user_id) else False
        is_like = True if book.like_book.filter(user_id=user_id) else False
        is_rate = RateBook.objects.filter(book=book, user=user).first()
    rate_num = book.rate_num
    collect_num = book.collect_num
    return render(request, "book.html", locals())

在这里插入图片描述

11、评分

@login_in
def score(request, book_id):
    user = User.objects.get(id=request.session.get("user_id"))
    book = Book.objects.get(id=book_id)
    score = float(request.POST.get("score", 0))
    is_rate = RateBook.objects.filter(book=book, user=user).first()
    if not is_rate:
        book.rate_num += 1
        book.save()
        RateBook.objects.get_or_create(user=user, book=book, defaults={"mark": score})
        is_rate = {'mark': score}
    comments = book.commentbook_set.order_by("-create_time")
    user_id = request.session.get("user_id")
    rate = RateBook.objects.filter(book=book).aggregate(Avg("mark")).get("mark__avg", 0)
    rate = rate if rate else 0
    book_rate = round(rate, 2)
    is_collect = True if book.collect_book.filter(user_id=user_id) else False
    is_like = True if book.like_book.filter(user_id=user_id) else False
    rate_num = book.rate_num
    collect_num = book.collect_num
    return render(request, "book.html", locals())

12、评论

@login_in
def comment(request, book_id):
    # 评论
    user = User.objects.get(id=request.session.get("user_id"))
    book = Book.objects.get(id=book_id)
    comment = request.POST.get("comment", "")
    CommentBook.objects.create(user=user, book=book, content=comment)
    comments = book.commentbook_set.order_by("-create_time")
    user_id = request.session.get("user_id")
    rate = RateBook.objects.filter(book=book).aggregate(Avg("mark")).get("mark__avg", 0)
    rate = rate if rate else 0
    book_rate = round(rate, 2)
    is_collect = True if book.collect_book.filter(user_id=user_id) else False
    is_like = True if book.like_book.filter(user_id=user_id) else False
    is_rate = RateBook.objects.filter(book=book, user=user).first()
    rate_num = book.rate_num
    collect_num = book.collect_num
    return render(request, "book.html", locals())

在这里插入图片描述

13、给评论点赞

@login_in
def comment_like(request, comment_id):
    user_id = request.session.get("user_id")
    user = User.objects.get(id=user_id)

    comment = CommentBook.objects.get(id=comment_id)
    if not comment.like_users:
        comment.like_users = '{},'.format(user_id)
        comment.like_num += 1
        is_comment_like = False
    elif str(user_id) not in comment.like_users.split(','):
        comment.like_users += '{},'.format(user_id)
        comment.like_num += 1
        is_comment_like = False
    else:
        is_comment_like = True

    comment.save()
    book = comment.book
    comments = book.commentbook_set.order_by("-create_time")
    user_id = request.session.get("user_id")
    rate = RateBook.objects.filter(book=book).aggregate(Avg("mark")).get("mark__avg", 0)
    rate = rate if rate else 0
    book_rate = round(rate, 2)
    is_collect = True if book.collect_book.filter(user_id=user_id) else False
    is_like = True if book.like_book.filter(user_id=user_id) else False
    is_rate = RateBook.objects.filter(book=book, user=user).first()
    rate_num = book.rate_num
    collect_num = book.collect_num
    return render(request, "book.html", locals())

14、收藏

@login_in
def collect(request, book_id):
    user_id = request.session.get("user_id")
    user = User.objects.get(id=user_id)
    book = Book.objects.get(id=book_id)
    collects = book.collect_book.filter(user_id=user_id)
    if collects:
        collect_ = collects.first()
        if collect_.is_delete:
            # 已取消收藏,再次点击改为已收藏
            is_collect = True
            collect_.is_delete = False
            collect_num_ = 1
            code = 1
        else:
            # 已收藏,再次点击改为取消收藏
            is_collect = False
            collect_.is_delete = True
            collect_num_ = -1
            code = 2
        collect_.save()
    else:
        # 未存在收藏书籍,创建收藏记录
        CollectBook.objects.create(
            book=book,
            user=user,
            is_delete=False
        )
        is_collect = True
        collect_num_ = 1
        code = 1

    book.collect_num += collect_num_  # 收藏人数加1
    book.save()
    comments = book.commentbook_set.order_by("-create_time")
    rate = RateBook.objects.filter(book=book).aggregate(Avg("mark")).get("mark__avg", 0)
    rate = rate if rate else 0
    book_rate = round(rate, 2)
    is_like = True if book.like_book.filter(user_id=user_id) else False
    is_rate = RateBook.objects.filter(book=book, user=user).first()
    rate_num = book.rate_num
    collect_num = book.collect_num
    if request.POST.get('from', '') == 'shop':
        return JsonResponse(data={'code': code, 'msg': 'success'}, status=200)
    return render(request, "book.html", locals())

15、点赞

@login_in
def like(request, book_id):
    user_id = request.session.get("user_id")
    user = User.objects.get(id=user_id)
    book = Book.objects.get(id=book_id)
    likes = book.like_book.filter(user_id=user_id)
    if likes:
        like_ = likes.first()
        if like_.is_delete:
            # 已取消点赞,再次点击改为已点赞
            is_like = True
            like_.is_delete = False
            like_num_ = 1
        else:
            # 已点赞,再次点击改为取消点赞
            is_like = False
            like_.is_delete = True
            like_num_ = -1
        like_.save()
    else:
        # 未存在点赞书籍,创建点赞记录
        LikeBook.objects.create(
            book=book,
            user=user,
            is_delete=False
        )
        is_like = True
        like_num_ = 1

    book.like_num += like_num_  # 收藏人数加1
    book.save()
    comments = book.commentbook_set.order_by("-create_time")
    rate = RateBook.objects.filter(book=book).aggregate(Avg("mark")).get("mark__avg", 0)
    rate = rate if rate else 0
    book_rate = round(rate, 2)
    is_collect = True if book.collect_book.filter(user_id=user_id) else False
    is_rate = RateBook.objects.filter(book=book, user=user).first()
    rate_num = book.rate_num
    collect_num = book.collect_num
    like_num = book.like_num
    return render(request, "book.html", locals())

16、新书速递

def new_book(request):
    page_number = request.GET.get("page", 1)
    # 按出版时间排序
    books = books_paginator(Book.objects.all().order_by("-published_time")[:10], page_number)
    return render(request, "all_book.html", {"books": books, "title": "新书速递"})

17、热门书籍

def hot_book(request):
    page_number = request.GET.get("page", 1)
    # 按收藏量排序
    books = Book.objects.all().order_by('-collect_num')[:10]
    books = books_paginator(books, page_number)
    return render(request, "all_book.html", {"books": books, "title": "热门书籍"})

18、图书分类

def sort_book(request):
    # 按收藏数进行排序
    sort_id = request.GET.get('sort_id', '1')

    tracks = []
    title = None
    for tag in Tags.objects.all():
        if int(sort_id) == tag.id:
            title = tag.name
            class_name = 'btn_select_track'
        else:
            class_name = 'btn_grey_track'
        tracks.append({
            'name': tag.name,
            'href': '/sort_book/?sort_id={}'.format(tag.id),
            'class_name': class_name
        })

    books = Book.objects.filter(tags_id=sort_id).annotate(user_collector=Count('collect_num')).order_by(
        '-user_collector')
    paginator = Paginator(books, 9)
    current_page = request.GET.get("page", 1)
    books = paginator.page(current_page)
    return render(request, "book_sort.html", {"books": books, "title": "{}类书籍".format(title), "tracks": tracks})

在这里插入图片描述

19、猜你喜欢

# 猜你喜欢
@login_in
def recommend_book(request):
    page = request.GET.get("page", 1)
    books = books_paginator(recommend_by_user_id(request.session.get("user_id")), page)
    path = request.path
    title = "猜你喜欢"
    return render(request, "all_book.html", {"books": books, "path": path, "title": title})

在这里插入图片描述

20、推荐算法

在项目根目录下的recommend_books.py文件中:

# -*-coding:utf-8-*-
import os
import django
import operator
from book.models import *
from math import sqrt, pow

os.environ["DJANGO_SETTINGS_MODULE"] = "book.settings"
django.setup()


class UserCf:
    # 基于用户协同算法来获取推荐列表
    """
    利用用户的群体行为来计算用户的相关性。
    计算用户相关性的时候我们就是通过对比他们对相同物品打分的相关度来计算的

    举例:

    --------+--------+--------+--------+--------+
            |   X    |    Y   |    Z   |    R   |
    --------+--------+--------+--------+--------+
        a   |   5    |    4   |    1   |    5   |
    --------+--------+--------+--------+--------+
        b   |   4    |    3   |    1   |    ?   |
    --------+--------+--------+--------+--------+
        c   |   2    |    2   |    5   |    1   |
    --------+--------+--------+--------+--------+

    a用户给X物品打了5分,给Y打了4分,给Z打了1分
    b用户给X物品打了4分,给Y打了3分,给Z打了1分
    c用户给X物品打了2分,给Y打了2分,给Z打了5分

    那么很容易看到a用户和b用户非常相似,但是b用户没有看过R物品,
    那么我们就可以把和b用户很相似的a用户打分很高的R物品推荐给b用户,
    这就是基于用户的协同过滤。
    """

    # 获得初始化数据
    def __init__(self, data):
        self.data = data

    # 通过用户名获得书籍列表,仅调试使用
    def getItems(self, username1, username2):
        return self.data[username1], self.data[username2]

    # 计算两个用户的皮尔逊相关系数
    def pearson(self, user1, user2):  # 数据格式为:书籍id,浏览次数
        print("user message", user1)
        sumXY = 0.0
        n = 0
        sumX = 0.0
        sumY = 0.0
        sumX2 = 0.0
        sumY2 = 0.0
        for movie1, score1 in user1.items():
            if movie1 in user2.keys():  # 计算公共的书籍浏览次数
                n += 1
                sumXY += score1 * user2[movie1]
                sumX += score1
                sumY += user2[movie1]
                sumX2 += pow(score1, 2)
                sumY2 += pow(user2[movie1], 2)
        if n == 0:
            print("p氏距离为0")
            return 0
        molecule = sumXY - (sumX * sumY) / n
        denominator = sqrt((sumX2 - pow(sumX, 2) / n) * (sumY2 - pow(sumY, 2) / n))
        if denominator == 0:
            print("共同特征为0")
            return 0
        r = molecule / denominator
        print("p氏距离:", r)
        return r

    # 计算与当前用户的距离,获得最临近的用户
    def nearest_user(self, username, n=1):
        distances = {}
        # 用户,相似度
        # 遍历整个数据集
        for user, rate_set in self.data.items():
            # 非当前的用户
            if user != username:
                distance = self.pearson(self.data[username], self.data[user])
                # 计算两个用户的相似度
                distances[user] = distance
        closest_distance = sorted(
            distances.items(), key=operator.itemgetter(1), reverse=True
        )
        # 最相似的N个用户
        print("closest user:", closest_distance[:n])
        return closest_distance[:n]

    # 给用户推荐书籍
    def recommend(self, username, n=1):
        recommend = {}
        nearest_user = self.nearest_user(username, n)
        for user, score in dict(nearest_user).items():  # 最相近的n个用户
            for book_id, scores in self.data[user].items():  # 推荐的用户的书籍列表
                if book_id not in self.data[username].keys():  # 当前username没有看过
                    rate = RateBook.objects.filter(book_id=book_id, user__username=user)
                    # 如果用户评分低于3分,则表明用户不喜欢此书籍,则不推荐给别的用户
                    if rate and rate.first().mark < 3:
                        continue
                    if book_id not in recommend.keys():  # 添加到推荐列表中
                        recommend[book_id] = scores
        # 对推荐的结果按照书籍浏览次数排序
        return sorted(recommend.items(), key=operator.itemgetter(1), reverse=True)


def recommend_by_user_id(user_id, book_id=None):
    # 通过用户协同算法来进行推荐
    current_user = User.objects.get(id=user_id)
    # 如果当前用户没有打分 则按照热度顺序返回
    if current_user.ratebook_set.count() == 0:
        if book_id:
            book_list = Book.objects.exclude(pk=book_id).order_by("-like_num")[:15]
        else:
            book_list = Book.objects.all().order_by("-like_num")[:15]
        return book_list
    users = User.objects.all()
    all_user = {}
    for user in users:
        rates = user.ratebook_set.all()
        rate = {}
        # 用户有给图书打分
        if rates:
            for i in rates:
                rate.setdefault(str(i.book.id), i.mark)
            all_user.setdefault(user.username, rate)
        else:
            # 用户没有为书籍打过分,设为0
            all_user.setdefault(user.username, {})

    print("this is all user:", all_user)
    user_cf = UserCf(data=all_user)
    recommend_list = user_cf.recommend(current_user.username, 15)
    good_list = [each[0] for each in recommend_list]
    print('this is the good list', good_list)
    if not good_list:
        # 如果没有找到相似用户喜欢的书则按照热度顺序返回
        if book_id:
            book_list = Book.objects.exclude(pk=book_id).order_by("-like_num")[:15]
        else:
            book_list = Book.objects.all().order_by("-like_num")[:15]
        return book_list
    if book_id and book_id in good_list:
        good_list.pop(good_list.index(book_id)) # 不推荐书籍book_id
    book_list = Book.objects.filter(id__in=good_list).order_by("-collect_num")[:15]
    return book_list

21、购买书籍首页

def purchase_index(request):
    books = Book.objects.filter(is_purchase=True)
    paginator = Paginator(books, 6)
    current_page = request.GET.get("page", 1)
    books = paginator.page(current_page)
    purchase_msg = None if books else "还没有书籍可供购买哦,敬请期待!"
    return render(request, "purchase_book.html", {"books": books, "title": "购买书籍", "purchase_msg": purchase_msg})

在这里插入图片描述

22、查看购买书籍详情

def purchase_check(request, book_id):
    # 查看购买书籍详情
    book = Book.objects.get(pk=book_id, is_show=True)
    user_id = request.session.get('user_id')
    if user_id:
        is_collect = True if book.collect_book.filter(user_id=user_id) else False
    else:
        is_collect = False
    return render(request, "purchase_check.html", {"book": book, "is_collect": is_collect})

在这里插入图片描述

23、立即购买书籍

@login_in
def purchase(request):
    if request.method == 'GET':
        return render(request, '404.html')

    user_id = request.session.get('user_id')
    book_id = request.POST.get('book_id')
    purchase_num = request.POST.get('purchase_num')

    book = Book.objects.get(id=book_id)
    is_shop_list = 0
    shop_id = book.id
    purchase_num = purchase_num
    order_code = datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')
    total_price = round(book.price * int(purchase_num), 2)
    return render(request, 'purchase.html', locals())

在这里插入图片描述

24、支付

@login_in
def purchase_pay(request):
    if request.method == 'GET':
        return render(request, '404.html')

    user_id = request.session.get('user_id')
    user = User.objects.get(pk=user_id)
    shop_id = request.POST.get('shop_id')
    is_shop_list = request.POST.get('is_shop_list')
    purchase_num = int(request.POST.get('purchase_num'))
    total_price = request.POST.get('total_price')
    address = request.POST.get('address')
    phone = request.POST.get('phone')
    order_code = request.POST.get('order_code')  # 订单号

    if is_shop_list == '0':
        # 立即购买页面的支付
        # 查看是否已存在相同的订单号
        purchase_lists = PurchaseList.objects.filter(order_code=order_code)
        if purchase_lists:
            purchase_list = purchase_lists.first()
            if purchase_list.order_status == '0':
                purchase_list.order_status = '1'
                purchase_list.pay_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                purchase_list.save()
            page = request.GET.get("page", 1)
            books = books_paginator(recommend_by_user_id(request.session.get("user_id"), shop_id), page)
            inform_message = '此订单已经支付成功,为你推荐了可能感兴趣的书籍!'
            inform_type = 'success'
            return render(request, 'pay_success.html', locals())
        try:
            book = Book.objects.get(pk=shop_id)
        except:
            return render(request, '404.html')
        # 创建支付订单
        PurchaseList.objects.create(
            book=book,
            user=user,
            order_code=order_code,
            price=book.price,
            purchase_num=purchase_num,
            all_price=total_price,
            phone=phone,
            address=address,
            order_status='1',
            pay_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        )
        book.purchase_num += 1
        book.inventory -= purchase_num
        book.save()
    else:
        # 购物车支付
        for p_id, value in json.loads(shop_id).items():
            purchase_lists = PurchaseList.objects.filter(id=p_id, order_status='0')
            if not purchase_lists:
                continue
            purchase_list = purchase_lists.first()
            purchase_list.order_status = '1'
            purchase_list.order_code = value[1]
            purchase_list.purchase_num = value[0]
            purchase_list.all_price = round(purchase_list.price * value[0], 2)
            purchase_list.phone = phone
            purchase_list.address = address
            purchase_list.pay_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            purchase_list.save()
    return redirect('{}{}'.format(request.build_absolute_uri('/'), 'shop_list/?shoplist=payed'))

25、添加到购物车

# 添加到购物车
@login_in
def add_shop_list(request):
    # 添加到购物车
    user_id = request.session.get('user_id')

    if request.method == 'GET':
        # 添加购物车成功
        book_id = request.GET.get('book_id')
        book = Book.objects.get(id=book_id)
        page = request.GET.get("page", 1)
        books = books_paginator(recommend_by_user_id(request.session.get("user_id"), book_id), page)
        inform_message = '《{}》添加到购物车成功,为你推荐了可能感兴趣的书籍!可在菜单栏【我的】->【购物清单】'.format(book.title)
        inform_type = 'success'
        is_shop_list = 1
        return render(request, 'pay_success.html', locals())

    # POST请求添加到购物车
    user = User.objects.get(pk=user_id)
    book_id = request.POST.get('book_id')
    book = Book.objects.get(pk=book_id)
    purchase_num = int(request.POST.get('purchase_num'))
    order_code = datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')  # 订单号
    # 创建购物车记录
    PurchaseList.objects.create(
        book=book,
        user=user,
        order_code=order_code,
        price=book.price,
        purchase_num=purchase_num,
        all_price=round(book.price * purchase_num, 2),
        order_status='0'
    )
    return JsonResponse(data={'code': 1, 'msg': 'success', 'book_title': book.title, 'book_id': book_id}, status=200)

26、从购物车移除商品

@login_in
def sub_shop_list(request, *args, **kwargs):
    shop_id = request.POST.get('shop_id')
    PurchaseList.objects.filter(pk=shop_id).delete()
    return JsonResponse(data={'code': 1, 'msg': 'success'}, status=200)

27、购物车批量结算

@login_in
def shop_list_pay(request, *args, **kwargs):
    user_id = request.session.get('user_id')
    data = json.loads(request.GET.get('data'))

    shop_lists = []
    all_price = 0
    all_num = 0
    shop_id = {}
    for p_id, purchase_num in data.items():
        order_code = datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')
        pl = PurchaseList.objects.get(id=p_id)
        purchase_num = int(purchase_num)
        total_price = round(pl.book.price * purchase_num, 2)
        all_num += purchase_num
        all_price += total_price
        d = {
            'shop_id': p_id,
            'title': pl.book.title,
            'pic': pl.book.pic,
            'price': pl.book.price,
            'purchase_num': purchase_num,
            'total_price': total_price,
            'order_code': order_code,
        }
        shop_id[p_id] = [purchase_num, order_code]
        shop_lists.append(d)
    is_shop_list = 1
    shop_id = json.dumps(shop_id)
    shop_lists = shop_lists
    total_price = round(all_price, 2)
    purchase_num = all_num
    return render(request, 'purchase.html', locals())

28、购物车

@login_in
def shop_list(request):
    # 购书清单
    user_id = request.session.get('user_id')
    select_shop = request.GET.get('shoplist', 'shopcart')
    page = request.GET.get("page", 1)
    # 创建一个有序字典
    shop_names = collections.OrderedDict(
        {'shopcart': '购物车', 'payed': '已支付', 'notdelivery': '待收货', 'delivery': '已收货'})

    tracks = []
    for key, value in shop_names.items():
        tracks.append({
            'name': value,
            'href': '/shop_list/?shoplist={}'.format(key),
            'class_name': 'btn_select_track' if select_shop == key else 'btn_grey_track'
        })
    tracks = tracks
    select_type = shop_names[select_shop]
    is_delivery = False
    if select_type == '购物车':
        purchase_lists = PurchaseList.objects.filter(user_id=user_id, order_status='0').order_by('-create_time')
    elif select_type == '已支付':
        purchase_lists = PurchaseList.objects.filter(user_id=user_id, order_status='1').order_by('-create_time')
    elif select_type == '待收货':
        purchase_lists = PurchaseList.objects.filter(user_id=user_id, order_status='2').order_by('-create_time')
    else:
        is_delivery = True
        purchase_lists = PurchaseList.objects.filter(user_id=user_id, order_status='3').order_by('-create_time')

    if not purchase_lists:
        # 没有查找到订单数据
        books = books_paginator(recommend_by_user_id(request.session.get("user_id")), page)
        inform_message = '{}列表空空如也,为你推荐了可能感兴趣的书籍!'.format(shop_names[select_shop])
        inform_type = 'warning'
        return render(request, 'shop_list_recommend.html', locals())
    if select_shop == 'shopcart':
        return render(request, 'shop_list.html', locals())
    return render(request, 'pay_list.html', locals())

在这里插入图片描述

29、个人中心

@login_in
def personal(request):
    user = User.objects.get(id=request.session.get("user_id"))
    form = Edit(instance=user)
    return render(request, "personal.html", {"form": form})

在这里插入图片描述

30、我的点赞

@login_in
def my_like(request):
    like_books = LikeBook.objects.filter(user_id=request.session.get("user_id"), is_delete=False)
    return render(request, "my_like.html", {"like_books": like_books})

在这里插入图片描述

31、我的收藏

@login_in
def my_collect(request):
    collect_books = CollectBook.objects.filter(user_id=request.session.get("user_id"), is_delete=False)
    return render(request, "my_collect.html", {"collect_books": collect_books})

32、我的评论

@login_in
def my_comments(request):
    comment_books = CommentBook.objects.filter(user_id=request.session.get("user_id"), is_show=True)
    return render(request, "my_comment.html", {"comment_books": comment_books})

在这里插入图片描述

33、删除我的评论

@login_in
def delete_comment(request, comment_id):
    # 删除评论
    comment = CommentBook.objects.get(pk=comment_id)
    comment.is_show = False
    comment.save()
    comment_books = CommentBook.objects.filter(user_id=request.session.get("user_id"))
    return render(request, "my_comment.html", {"comment_books": comment_books})

34、我的评分

@login_in
def my_rate(request):
    rate_books = RateBook.objects.filter(user_id=request.session.get("user_id"))
    return render(request, "my_rate.html", {"rate_books": rate_books})

35、删除我的评分

@login_in
def delete_rate(request, rate_id):
    rate = RateBook.objects.filter(pk=rate_id)
    if not rate:
        return render(request, "404.html")
    rate = rate.first()
    rate.book.rate_num -= 1
    rate.book.save()
    rate.save()
    rate.delete()
    rate_books = RateBook.objects.filter(user_id=request.session.get("user_id"))
    return render(request, "my_rate.html", {"rate_books": rate_books})

七、后台管理

1、创建adminx.py文件

在子应用book下创建一个adminx.py文件:

在这里插入图片描述

里面代码为:

# !/usr/bin/python
# -*- coding: utf-8 -*-
import xadmin
from django.utils.safestring import mark_safe
from xadmin import views
from django.conf import settings
from .models import *


# https://fontawesome.dashgame.com/  图标字体网站
# 基础设置
class BaseSetting(object):
    enable_themes = True  # 使用主题
    use_bootswatch = True


# 全局设置
class GlobalSettings(object):
    site_title = '图书管理系统'  # 标题
    site_footer = mark_safe(settings.SITE_FOOTER)  # 页尾
    site_url = '/'
    menu_style = 'accordion'  # 设置左侧菜单  折叠样式


# 用户管理
class UserAdmin(object):
    search_fields = ['username', 'phone', 'name']  # 检索字段
    list_display = ['id', 'username', 'phone', 'name', 'address', 'email']  # 要显示的字段
    ordering = ('id',)  # 设置默认排序字段,负号表示降序排序
    list_per_page = 30  # 默认每页显示多少条记录,默认是100条
    model_icon = 'fa fa-users'  # 左侧小图标
    list_editable = ['name', 'address']  # 可编辑字段


# 标签管理
class TagsAdmin(object):
    search_fields = ['name']  # 检索字段
    list_display = ['id', 'name', 'show_intro']
    list_filter = ['name']
    ordering = ('id',)
    model_icon = 'fa fa-tags'  # 左侧小图标

    def show_intro(self, obj):
        # 显示简介
        if not obj.intro:
            return mark_safe('')
        if len(obj.intro) < 20:
            return mark_safe(obj.intro)

        short_id = f'{obj._meta.db_table}_short_text_{obj.id}'
        short_text_len = len(obj.intro) // 4
        short_text = obj.intro[:short_text_len] + '......'
        detail_id = f'{obj._meta.db_table}_detail_text_{obj.id}'
        detail_text = obj.intro

        text = """<style type="text/css">
                        #%s,%s {padding:10px;border:1px solid green;} 
                  </style>
                    <script type="text/javascript">

                    function openShutManager(oSourceObj,oTargetObj,shutAble,oOpenTip,oShutTip,oShortObj){
                        var sourceObj = typeof oSourceObj == "string" ? document.getElementById(oSourceObj) : oSourceObj;
                        var targetObj = typeof oTargetObj == "string" ? document.getElementById(oTargetObj) : oTargetObj;
                        var shortObj = typeof oShortObj == "string" ? document.getElementById(oShortObj) : oShortObj;
                        var openTip = oOpenTip || "";
                        var shutTip = oShutTip || "";
                        if(targetObj.style.display!="none"){
                           if(shutAble) return;
                           targetObj.style.display="none";
                           shortObj.style.display="block";
                           if(openTip  &&  shutTip){
                            sourceObj.innerHTML = shutTip; 
                           }
                        } else {
                           targetObj.style.display="block";
                           shortObj.style.display="none";
                           if(openTip  &&  shutTip){
                            sourceObj.innerHTML = openTip; 
                           }
                        }
                        }
                    </script>
                    <p id="%s">%s</p>
                    <p><a href="###" onclick="openShutManager(this,'%s',false,'点击关闭','点击展开','%s')">点击展开</a></p>

                    <p id="%s" style="display:none">
                       %s
                    </p>
                    """ % (short_id, detail_id, short_id, short_text, detail_id, short_id, detail_id, detail_text)
        return mark_safe(text)

    show_intro.short_description = '描述'


# 书籍管理
class BookAdmin(object):
    search_fields = ['title', 'author', 'intro']  # 检索字段
    list_display = ['id', 'show_pic', 'title', 'author', 'published_time', 'show_intro',
                    'tags', 'look_num', 'like_num', 'collect_num', 'rate_num', 'show_avg_mark',
                    'is_show', 'is_purchase', 'price', 'inventory', 'purchase_num']  # 要显示的字段
    list_filter = ['create_time', 'published_time', 'tags', 'is_show']  # 分组过滤的字段
    ordering = ('id',)  # 设置默认排序字段,负号表示降序排序
    list_per_page = 30  # 默认每页显示多少条记录,默认是100条
    model_icon = 'fa fa-book'  # 左侧小图标
    list_editable = ['title', 'author', 'intro', 'published_time', 'is_show', 'is_purchase']  # 可编辑字段
    style_fields = {'tags': 'm2m_transfer', 'prizes': 'm2m_transfer'}  # 控制字段的显示样式
    filter_horizontal = ('tags', 'prizes')  # 水平选择编辑多对多字段

    def show_pic(self, obj):
        # 显示书籍封面
        if obj.pic.name:
            text = """
                <style type="text/css">

                    #div1 img{
                      cursor: pointer;
                      transition: all 0.6s;
                    }
                    #div1 img:hover{
                      transform: scale(2);
                    }
                </style>
                <div id="div1">
                    <img src="%s" style="width:50px;"/>
                </div>
                """ % (self.request.build_absolute_uri('/') + 'media/' + obj.pic.name)

            return mark_safe(text)
        return ''

    def show_intro(self, obj):
        # 显示简介
        if not obj.intro:
            return mark_safe('')
        if len(obj.intro) < 20:
            return mark_safe(obj.intro)
        short_id = f'{obj._meta.db_table}_short_text_{obj.id}'
        short_text = obj.intro[:len(obj.intro) // 4] + '......'
        detail_id = f'{obj._meta.db_table}_detail_text_{obj.id}'
        detail_text = obj.intro

        text = """<style type="text/css">
                        #%s,%s {padding:10px;border:1px solid green;} 
                  </style>
                    <script type="text/javascript">

                    function openShutManager(oSourceObj,oTargetObj,shutAble,oOpenTip,oShutTip,oShortObj){
                        var sourceObj = typeof oSourceObj == "string" ? document.getElementById(oSourceObj) : oSourceObj;
                        var targetObj = typeof oTargetObj == "string" ? document.getElementById(oTargetObj) : oTargetObj;
                        var shortObj = typeof oShortObj == "string" ? document.getElementById(oShortObj) : oShortObj;
                        var openTip = oOpenTip || "";
                        var shutTip = oShutTip || "";
                        if(targetObj.style.display!="none"){
                           if(shutAble) return;
                           targetObj.style.display="none";
                           shortObj.style.display="block";
                           if(openTip  &&  shutTip){
                            sourceObj.innerHTML = shutTip; 
                           }
                        } else {
                           targetObj.style.display="block";
                           shortObj.style.display="none";
                           if(openTip  &&  shutTip){
                            sourceObj.innerHTML = openTip; 
                           }
                        }
                        }
                    </script>
                    <p id="%s" title="%s">%s</p>
                    <p><a href="###" onclick="openShutManager(this,'%s',false,'点击关闭','点击展开','%s')">点击展开</a></p>

                    <p id="%s" style="display:none">
                       %s
                    </p>
                    """ % (
            short_id, detail_id, short_id, detail_text, short_text, detail_id, short_id, detail_id, detail_text)
        return mark_safe(text)

    def show_avg_mark(self, obj):
        return obj.avg_mark

    def save_models(self):
        flag = self.org_obj is None and 'create' or 'change'
        if flag == 'create':
            if self.new_obj.pic.name:
                self.new_obj.pic.name = f"{self.new_obj.title}.{self.new_obj.pic.name.split('.')[1]}"
        if flag == 'change' and 'pic' in self.change_message():
            if self.org_obj.pic.name:
                self.org_obj.pic.name = f"{self.org_obj.title}.{self.org_obj.pic.name.split('.')[1]}"

        super().save_models()

    show_pic.short_description = '封面'
    show_intro.short_description = '描述'
    show_avg_mark.short_description = '评分'


# 书籍购买管理
class PurchaseListAdmin(object):
    search_fields = ['book__title', 'user__name']  # 要显示的字段
    list_display = ['book', 'user', 'purchase_num', 'all_price', 'phone', 'address', 'order_status', 'pay_time',
                    'received_time']  # 检索字段
    list_filter = ['order_status', 'create_time']  # 分组过滤的字段
    ordering = ('id',)  # 设置默认排序字段,负号表示降序排序
    list_per_page = 30  # 默认每页显示多少条记录,默认是100条
    list_editable = ['order_status', 'received_time']  # 可编辑字段
    fk_fields = ('book', 'user')  # 设置显示外键字段


# 书籍评分管理
class RateAdmin(object):
    search_fields = ['book__title', 'user__name', 'mark']  # 检索字段
    list_display = ['book', 'user', 'mark', 'create_time']  # 要显示的字段
    list_filter = ['mark', 'create_time']  # 分组过滤的字段
    ordering = ('id',)  # 设置默认排序字段,负号表示降序排序
    list_per_page = 30  # 默认每页显示多少条记录,默认是100条
    list_editable = []  # 可编辑字段
    fk_fields = ('book', 'user')  # 设置显示外键字段


# 书籍点赞管理
class LikeBookAdmin(object):
    search_fields = ['book__title', 'user__name']  # 检索字段
    list_display = ['book', 'user', 'is_delete', 'create_time']  # 要显示的字段
    list_filter = ['is_delete', 'create_time']  # 分组过滤的字段
    ordering = ('id',)  # 设置默认排序字段,负号表示降序排序
    list_per_page = 30  # 默认每页显示多少条记录,默认是100条
    list_editable = []  # 可编辑字段
    fk_fields = ('book', 'user')  # 设置显示外键字段


# 书籍收藏管理
class CollectBookAdmin(object):
    search_fields = ['book__title', 'user__name']  # 检索字段
    list_display = ['book', 'user', 'is_delete', 'create_time']  # 要显示的字段
    list_filter = ['is_delete', 'create_time']  # 分组过滤的字段
    ordering = ('id',)  # 设置默认排序字段,负号表示降序排序
    list_per_page = 30  # 默认每页显示多少条记录,默认是100条
    list_editable = []  # 可编辑字段
    fk_fields = ('book', 'user')  # 设置显示外键字段


# 书籍评论管理
class CommentAdmin(object):
    search_fields = ['book__title', 'user__name']  # 检索字段
    list_display = ['user', 'book', 'show_content', 'like_num', 'is_show', 'create_time']  # 要显示的字段
    list_filter = ['book', 'is_show', 'create_time']  # 分组过滤的字段
    ordering = ('id',)  # 设置默认排序字段,负号表示降序排序
    list_per_page = 30  # 默认每页显示多少条记录,默认是100条
    list_editable = []  # 可编辑字段
    fk_fields = ('book', 'user')  # 设置显示外键字段

    def show_content(self, obj):
        # 显示评论内容
        if not obj.content:
            return mark_safe('')
        if len(obj.content) < 20:
            return mark_safe(obj.content)
        short_id = f'{obj._meta.db_table}_short_text_{obj.id}'
        short_text = obj.content[:len(obj.content) // 4] + '......'
        detail_id = f'{obj._meta.db_table}_detail_text_{obj.id}'
        detail_text = obj.content

        text = """<style type="text/css">
                        #%s,%s {padding:10px;border:1px solid green;} 
                  </style>
                    <script type="text/javascript">

                    function openShutManager(oSourceObj,oTargetObj,shutAble,oOpenTip,oShutTip,oShortObj){
                        var sourceObj = typeof oSourceObj == "string" ? document.getElementById(oSourceObj) : oSourceObj;
                        var targetObj = typeof oTargetObj == "string" ? document.getElementById(oTargetObj) : oTargetObj;
                        var shortObj = typeof oShortObj == "string" ? document.getElementById(oShortObj) : oShortObj;
                        var openTip = oOpenTip || "";
                        var shutTip = oShutTip || "";
                        if(targetObj.style.display!="none"){
                           if(shutAble) return;
                           targetObj.style.display="none";
                           shortObj.style.display="block";
                           if(openTip  &&  shutTip){
                            sourceObj.innerHTML = shutTip; 
                           }
                        } else {
                           targetObj.style.display="block";
                           shortObj.style.display="none";
                           if(openTip  &&  shutTip){
                            sourceObj.innerHTML = openTip; 
                           }
                        }
                        }
                    </script>
                    <p id="%s">%s</p>
                    <p><a href="###" onclick="openShutManager(this,'%s',false,'点击关闭','点击展开','%s')">点击展开</a></p>

                    <p id="%s" style="display:none">
                       %s
                    </p>
                    """ % (short_id, detail_id, short_id, short_text, detail_id, short_id, detail_id, detail_text)
        return mark_safe(text)

    show_content.short_description = '评论内容'


xadmin.site.register(views.CommAdminView, GlobalSettings)
xadmin.site.register(views.BaseAdminView, BaseSetting)
xadmin.site.register(User, UserAdmin)
xadmin.site.register(Tags, TagsAdmin)
xadmin.site.register(Book, BookAdmin)
xadmin.site.register(PurchaseList, PurchaseListAdmin)
xadmin.site.register(RateBook, RateAdmin)
xadmin.site.register(LikeBook, CollectBookAdmin)
xadmin.site.register(CollectBook, CollectBookAdmin)
xadmin.site.register(CommentBook, CommentAdmin)

2、修改book\apps.py

代码改为如下:

from django.apps import AppConfig


class BookConfig(AppConfig):
    name = 'book'
    verbose_name = "图书推荐系统"

3、修改book\__init__.py

代码改为:

default_app_config = 'book.apps.BookConfig'

4、浏览器登录

浏览器访问http://127.0.0.1:8000/xadmin/

输入账号:root

输入密码:book-root

在这里插入图片描述

八、部署到服务器

1、前期工作

部署到ubuntu服务器,把项目放到home目录下,创建数据库book_manager

在服务器/home/venv/目录下创建一个虚拟环境book_manager

cd ..
cd home/venv/
python3 -m venv book_manager

激活虚拟环境:

source book_manager/bin/activate

更新pip:

pip install --upgrade pip

退回项目根目录:

cd ..
cd book_manager

批量安装第三方库:

pip install -r requirements.txt  -i https://pypi.tuna.tsinghua.edu.cn/simple

数据迁移:

python manage.py migrate

创建缓存表:

python manage.py createcachetable

创建后台管理员

python manage.py createsuperuser
设置账号为 root
邮箱为 1@qq.com
密码为 book-root

数据库图形化工具执行books.sql

2、配置uwsgi.ini

uwsgi.ini内容如下:

[uwsgi]
# 使用nginx连接时 使用
socket=0.0.0.0:8101
# 直接作为web服务器使用
#http=127.0.0.1:8101
# 配置工程目录
chdir=/home/book_manager
# 配置项目的wsgi目录。相对于工程目录
wsgi-file=book_manager/wsgi.py
virtualenv =/home/venv/book_manager
#配置进程,线程信息
listen=1024
processes=2
threads=4
enable-threads=True
master=True
pidfile=uwsgi.pid
daemonize=uwsgi.log
#django项目修改完文件后自动重启
py-autoreload=1

uwsgi.ini放到根目录下。

导入uwsgi:

pip install uwsgi

启动uwsgi:

uwsgi --ini uwsgi.ini

查看是否启动成功:

netstat -lnp|grep uwsgi

若出现类似:

tcp        0      0 0.0.0.0:8101            0.0.0.0:*               LISTEN      927/uwsgi  

则说明启动成功

2、配置nginx

创建一个book_manager_nginx文件,内容如下:

    #设定虚拟主机配置
        server {
            #侦听80端口
            listen 80;
            #listen 443 ssl;
            #定义使用 www.nginx.cn访问
    		#ssl on;
            server_name  xxx.xxx.com;
            #server_name  xxx.xxx.xxx.xxx;
            #定义服务器的默认网站根目录位置
            root /home/book_manager;
    		#ssl_session_timeout 5m; 
            #ssl_certificate   /etc/nginx/cert/xxx.pem;
            #ssl_certificate_key  /etc/nginx/cert/xxx.key;
            #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
            #ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
            #ssl_prefer_server_ciphers on;
            #设定本虚拟主机的访问日志
            #access_log  logs/nginx.access.log  main;
     
            #默认请求
            location / {
                #倒入了uwsgi的配置
                include uwsgi_params; 
    		    client_max_body_size	50m;
                #连接uwsgi的超时时间
               # uwsgi_connect_timeout 30; 
     	    #设定了uwsig服务器位置
     	    	uwsgi_pass 127.0.0.1:8101;
            }
            
            location /static{
          	alias /home/book_manager/static;
            }
    	location /media {
    	alias /home/book_manager/media;
    	}
    }

文件放到/etc/nginx/sites-available下面。

然后通过以下命令映射到/etc/nginx/sites-enabled

ln -s /etc/nginx/sites-available/book_manager_nginx /etc/nginx/sites-enabled/book_manager_nginx

nginx重启:

nginx -s reload

在浏览器中访问网站就可以看到书籍了。

在这里插入图片描述

[uwsgi]
# 使用nginx连接时 使用
socket=0.0.0.0:8101
# 直接作为web服务器使用
#http=127.0.0.1:8101
# 配置工程目录
chdir=/home/book_manager
# 配置项目的wsgi目录。相对于工程目录
wsgi-file=book_manager/wsgi.py
virtualenv =/home/venv/book_manager
#配置进程,线程信息
listen=1024
processes=2
threads=4
enable-threads=True
master=True
pidfile=uwsgi.pid
daemonize=uwsgi.log
#django项目修改完文件后自动重启
py-autoreload=1

uwsgi.ini放到根目录下。

导入uwsgi:

pip install uwsgi

启动uwsgi:

uwsgi --ini uwsgi.ini

查看是否启动成功:

netstat -lnp|grep uwsgi

若出现类似:

tcp        0      0 0.0.0.0:8101            0.0.0.0:*               LISTEN      927/uwsgi  

则说明启动成功

2、配置nginx

创建一个book_manager_nginx文件,内容如下:

    #设定虚拟主机配置
        server {
            #侦听80端口
            listen 80;
            #listen 443 ssl;
            #定义使用 www.nginx.cn访问
    		#ssl on;
            server_name  xxx.xxx.com;
            #server_name  xxx.xxx.xxx.xxx;
            #定义服务器的默认网站根目录位置
            root /home/book_manager;
    		#ssl_session_timeout 5m; 
            #ssl_certificate   /etc/nginx/cert/xxx.pem;
            #ssl_certificate_key  /etc/nginx/cert/xxx.key;
            #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
            #ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
            #ssl_prefer_server_ciphers on;
            #设定本虚拟主机的访问日志
            #access_log  logs/nginx.access.log  main;
     
            #默认请求
            location / {
                #倒入了uwsgi的配置
                include uwsgi_params; 
    		    client_max_body_size	50m;
                #连接uwsgi的超时时间
               # uwsgi_connect_timeout 30; 
     	    #设定了uwsig服务器位置
     	    	uwsgi_pass 127.0.0.1:8101;
            }
            
            location /static{
          	alias /home/book_manager/static;
            }
    	location /media {
    	alias /home/book_manager/media;
    	}
    }

文件放到/etc/nginx/sites-available下面。

然后通过以下命令映射到/etc/nginx/sites-enabled

ln -s /etc/nginx/sites-available/book_manager_nginx /etc/nginx/sites-enabled/book_manager_nginx

nginx重启:

nginx -s reload

在浏览器中访问网站就可以看到书籍了。 在这里插入图片描述

demo体验传送门