开发一个简易的图书增删改查页面


需求

"""
1. 实现用户登录、注册
2. 列出图书列表、出版社列表、作者列表
3. 点击作者,会在新的页面列出该作者出版的图书列表
4. 点击出版社,会列出该出版社旗下图书列表
5. 可以创建、修改、删除  图书、作者、出版社

A. 点击修改书籍按钮,弹出模态框,模态框中展示该书的信息且信息可以修改,
B. 书名不可重复,不可修改
C. 修改图书信息时,使用ajax请求发送信息
"""

页面展示

 项目目录结构(前端用到了jQuery/bootstrap/sweetalert)

 app01文件夹

models.py

from django.db import models

# Create your models here.


"""
Book              与Publish多对一;与Author多对多
Author
Publish
Book2Author
"""


class Publish(models.Model):
    """出版社名称和出版社邮箱两个字段是联合索引"""
    pid = models.AutoField(
        primary_key=True,
        verbose_name="主键",
    )
    pub_name = models.CharField(
        max_length=32,
        verbose_name="出版社名称",
    )
    pub_addr = models.CharField(
        max_length=64,
        verbose_name="出版社地址",
    )
    pub_email = models.EmailField(
        verbose_name="出版社邮箱",
    )

    class Meda:
        unique_together = (("pub_name", "pub_email"),)
        ordering = ['-id']


class Author(models.Model):
    """作者名字字段是唯一索引"""
    aid = models.AutoField(
        primary_key=True,
        verbose_name="主键",
    )
    author_name = models.CharField(
        max_length=32,
        unique=True,
        verbose_name="作者名字",
    )
    author_gender_choices = (
        (1, ""),
        (2, ""),
        (3, "其他"),
    )
    author_gender = models.IntegerField(
        choices=author_gender_choices,
        default=1,
        verbose_name="作者性别",
    )


class Book(models.Model):
    """书籍名称这个字段是唯一索引"""
    bid = models.AutoField(
        primary_key=True,
        verbose_name="主键",
    )
    book_name = models.CharField(
        max_length=32,
        verbose_name="书籍名称",
        unique=True,
    )
    book_price = models.DecimalField(
        max_digits=8,
        decimal_places=2,
        verbose_name="书籍价格",
    )
    book_date = models.DateField(
        verbose_name="书籍出版日期",
    )
    publish = models.ForeignKey(to="Publish", on_delete=models.CASCADE)
    authors = models.ManyToManyField(to="Author")

myforms.py

from django import forms
from app01.models import *


class Mylogin(forms.Form):
    """登录校验"""
    username = forms.CharField(
        label="账号",
        min_length=3,
        max_length=9,
        error_messages={
            "required": "账号不能为空",
            "min_length": "账号不能小于3位",
            "max_length": "账号不能大于9位",
        },
        widget=forms.widgets.TextInput(attrs={"class": "form-control"}),
    )
    password = forms.CharField(
        label="密码",
        min_length=3,
        max_length=9,
        error_messages={
            "required": "密码不能为空",
            "min_length": "密码不能小于3位",
            "max_length": "密码不能大于9位",
        },
        widget=forms.widgets.PasswordInput(attrs={"class": "form-control"}),
    )


class Myreg(forms.Form):
    """注册校验"""
    username = forms.CharField(
        label="账号",
        min_length=3,
        max_length=9,
        error_messages={
            "required": "账号不能为空",
            "min_length": "账号不能小于3位",
            "max_length": "账号不能大于9位",
        },
        widget=forms.widgets.TextInput(attrs={"class": "form-control"}),
    )
    password = forms.CharField(
        label="密码",
        min_length=3,
        max_length=9,
        error_messages={
            "required": "密码不能为空",
            "min_length": "密码不能小于3位",
            "max_length": "密码不能大于9位",
        },
        widget=forms.widgets.PasswordInput(attrs={"class": "form-control"}),
    )
    confirm_password = forms.CharField(
        label="确认密码",
        min_length=3,
        max_length=9,
        error_messages={
            "required": "确认密码不能为空",
            "min_length": "密码不能小于3位",
            "max_length": "密码不能大于9位",
        },
        widget=forms.widgets.PasswordInput(attrs={"class": "form-control"}),
    )
    user_email = forms.EmailField(
        label="邮箱",
        error_messages={
            "invalid": "邮箱格式不正确",
            "required": "邮箱不能为空",
        },
        widget=forms.widgets.EmailInput(attrs={"class": "form-control"}),
    )

    def clean_user(self):
        """校验用户名是否包含特定字符"""
        username = self.cleaned_data.get("username")
        if "666" in username:
            self.add_error("username", "要自己666才行呀!!!")
        return username

    def clean(self):
        """校验密码和确认密码是否一致"""
        password = self.cleaned_data.get("password")
        confirm_password = self.cleaned_data.get("confirm_password")
        if not password == confirm_password:
            self.add_error("confirm_password", "两次密码不一致!!!")
        return self.cleaned_data


class AddBook(forms.Form):
    """添加书籍校验"""
    book_name = forms.CharField(
        max_length=16,
        error_messages={
            "required": "书名不能为空",
            "max_length": "书名不能大于16位",
        },
    )
    book_price = forms.FloatField(
        min_value=0.01,
        max_value=999999.99,
        error_messages={
            "min_value": "最小价格不能小于0.01",
            "max_value": "最大价格不能大于999999.99",
            "required": "价格不能为空",
        },
    )

    def clean_book_name(self):
        """校验书名是否重复"""
        book_name = self.cleaned_data.get("book_name")
        book_obj = Book.objects.filter(
            book_name=book_name
        ).first()
        if book_obj:
            self.add_error("book_name", "书籍已存在,请重新输入...")
        return book_name


class SetPassword(forms.Form):
    old_password = forms.CharField(
        label="原密码",
        min_length=3,
        max_length=9,
        error_messages={
            "required": "原密码不能为空",
            "min_length": "密码不能小于3位",
            "max_length": "密码不能大于9位",
        },
        widget=forms.widgets.PasswordInput(attrs={"class": "form-control"}),
    )
    new_password = forms.CharField(
        label="新密码",
        min_length=3,
        max_length=9,
        error_messages={
            "required": "新密码不能为空",
            "min_length": "新密码不能小于3位",
            "max_length": "新密码不能大于9位",
        },
        widget=forms.widgets.PasswordInput(attrs={"class": "form-control"}),
    )
    confirm_password = forms.CharField(
        label="确认密码",
        min_length=3,
        max_length=9,
        error_messages={
            "required": "确认密码不能为空",
            "min_length": "确认密码不能小于3位",
            "max_length": "确认密码不能大于9位",
        },
        widget=forms.widgets.PasswordInput(attrs={"class": "form-control"}),
    )

    def clean(self):
        new_password = self.cleaned_data.get("new_password")
        confirm_password = self.cleaned_data.get("confirm_password")
        if not new_password == confirm_password:
            self.add_error("confirm_password", "两次密码不一致,请重新输入...")
        return self.cleaned_data

test.py

from django.test import TestCase

# Create your tests here.

import os

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "six_module_one_work.settings")
    import django
    django.setup()
    from app01.models import *
    # 创建初始系统账户admin
    # from django.contrib.auth.models import User
    # user = User.objects.create_superuser(username='admin', password='asd123123', email='123@qq.com')

    # 创建初始作者表
    # author_list = []
    # for i in range(1, 6):
    #     author_obj = Author(
    #         author_name='jack%s' % i,
    #         author_gender=1,
    #     )
    #     author_list.append(author_obj)
    # Author.objects.bulk_create(author_list)

    # 创建初始出版社表
    # publish_list = []
    # for i in range(1, 6):
    #     publish_obj = Publish(
    #         pub_name="北京出版社%s" % i,
    #         pub_addr="北京市%s街道%s巷" % (i, i),
    #         pub_email="%s%s%s@163.com" % (i, i, i),
    #     )
    #     publish_list.append(publish_obj)
    # Publish.objects.bulk_create(publish_list)

    # 创建初始书籍表
    # book_list = []
    # for i in range(1, 251):
    #     book_obj = Book(
    #         book_name="python%s" % i,
    #         book_price=i,
    #         book_date="2021-02-02",
    #         publish_id=1,
    #     )
    #     book_list.append(book_obj)
    # Book.objects.bulk_create(book_list)

    # 创建作者书籍关系表
    # for i in range(1, 126):
    #     book_obj = Book.objects.filter(bid=i).first()
    #     book_obj.authors.add(1, 3, 5)
    # for i in range(126, 251):
    #     book_obj = Book.objects.filter(bid=i).first()
    #     book_obj.authors.add(2, 4)

views.py

from django.shortcuts import render, HttpResponse, redirect, reverse
from django.contrib import auth
from app01.myforms import *
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from app01.models import *
from django.core.paginator import Paginator, EmptyPage
# Create your views here.

FLAG = False


def register(request):
    form_obj = Myreg()
    if request.method == "POST":
        form_obj = Myreg(request.POST)
        if form_obj.is_valid():
            if not User.objects.filter(username=request.POST.get("username")).first():
                User.objects.create_user(
                    username=request.POST.get("username"),
                    email=request.POST.get("user_email"),
                    password=request.POST.get("password"),
                )
                global FLAG
                FLAG = True
                return redirect("/login/")
            else:
                title = "swal('注册用户已存在...')"
    return render(request, "reg.html", locals())


def login(request):
    form_obj = Mylogin()
    global FLAG
    if FLAG:
        title = "swal('注册成功...')"
    else:
        title = ""
    target_url = request.GET.get("next", "/index/")
    if request.method == "POST":
        form_obj = Mylogin(request.POST)
        if form_obj.is_valid():
            username = request.POST.get("username")
            password = request.POST.get("password")
            user = auth.authenticate(
                request,
                username=username,
                password=password,
            )
            if user is not None:
                auth.login(request, user)
                return redirect(target_url)
    FLAG = False
    return render(request, "login.html", locals())


@login_required
def index(request):
    authors = Author.objects.all().order_by("aid")
    publishs = Publish.objects.all().order_by("pid")
    book_obj = Book.objects.all().order_by("bid")
    paginator_obj = Paginator(book_obj, 10)
    page_num = int(request.GET.get("page", 1))
    try:
        current_page = paginator_obj.page(page_num)
    except EmptyPage as e:
        current_page = paginator_obj.page(1)
    if paginator_obj.num_pages > 11:
        if page_num-5 < 1:
            page_range = range(1, 12)
        elif page_num+5 > paginator_obj.num_pages:
            page_range = range(paginator_obj.num_pages-10, paginator_obj.num_pages+1)
        else:
            page_range = range(page_num-5, page_num+6)
    else:
        page_range = range(1, paginator_obj.num_pages+1)
    return render(request, "index.html", locals())


@login_required
def add_book(request):
    authors = Author.objects.all().order_by("aid")
    publishs = Publish.objects.all().order_by("pid")
    form_obj = AddBook()
    if request.method == "POST":
        form_obj = AddBook(request.POST)
        if form_obj.is_valid():
            book_obj = Book.objects.create(
                book_name=request.POST.get("book_name"),
                book_price=request.POST.get("book_price"),
                book_date=request.POST.get("book_date"),
                publish_id=request.POST.get("publish_id"),
            )
            author_list = request.POST.getlist("author_id")
            book_obj.authors.add(*author_list)
            return redirect("/index/")
    return render(request, "add_book.html", locals())


@login_required
def delete_book(request, number, page):
    Book.objects.filter(bid=number).delete()
    book_obj = Book.objects.all().order_by("bid")
    paginator_obj = Paginator(book_obj, 10)
    if page not in paginator_obj.page_range:
        page -= 1
    url = "/index/?page=%s" % page
    return HttpResponse(url)


@login_required
def edit_book(request, number, page):
    print(request.path_info)
    book_obj = Book.objects.filter(bid=number).first()
    if request.is_ajax():
        Book.objects.filter(bid=number).update(
            book_price=request.POST.get("book_price"),
            book_date=request.POST.get("book_date"),
            publish_id=int(request.POST.get("publish_id")),
        )
        authors_id = request.POST.getlist("authors_id[]")
        book_obj.authors.set(authors_id)
        url = "/index/?page=%s" % page
        return HttpResponse(url)


@login_required
def show_publish(request, number):
    publish_obj = Publish.objects.filter(pid=number).first()
    books_obj = Publish.objects.filter(pid=number).values_list(
        "book__book_name",
        "book__book_price",
        "book__book_date",
    ).order_by('pid')
    paginator_obj = Paginator(books_obj, 10)
    page_num = int(request.GET.get("page", 1))
    try:
        current_page = paginator_obj.page(page_num)
    except EmptyPage as e:
        current_page = paginator_obj.page(1)
    if paginator_obj.num_pages > 11:
        if page_num-5 < 1:
            page_range = range(1, 12)
        elif page_num+5 > paginator_obj.num_pages:
            page_range = range(paginator_obj.num_pages-10, paginator_obj.num_pages+1)
        else:
            page_range = range(page_num-5, page_num+6)
    else:
        page_range = range(1, paginator_obj.num_pages+1)
    if request.method == "POST":
        Publish.objects.filter(pid=number).update(
            pub_name=request.POST.get("pub_name"),
            pub_addr=request.POST.get("pub_addr"),
            pub_email=request.POST.get("pub_email"),
        )
        url = reverse("app01_show_publish", args=(number,))
        return redirect(url)
    return render(request, "show_publish.html", locals())


@login_required
def add_publish(request):
    if request.method == "POST":
        if request.POST.get("pub_name") and request.POST.get("pub_addr") and request.POST.get("pub_email"):
            publish_obj = Publish.objects.create(
                pub_name=request.POST.get("pub_name"),
                pub_addr=request.POST.get("pub_addr"),
                pub_email=request.POST.get("pub_email"),
            )
            if publish_obj:
                return redirect("/index/")
    return render(request, "add_publish.html", locals())


@login_required
def del_publish(request, name):
    Publish.objects.filter(pub_name=name).delete()
    return redirect("/index/")


@login_required
def show_author(request, number):
    author_obj = Author.objects.filter(aid=number).first()
    book_obj = Author.objects.filter(aid=number).values_list(
        "book__book_name",
        "book__book_price",
        "book__book_date",
    ).order_by('aid')
    paginator_obj = Paginator(book_obj, 10)
    page_num = int(request.GET.get("page", 1))
    try:
        current_page = paginator_obj.page(page_num)
    except EmptyPage as e:
        current_page = paginator_obj.page(1)
    if paginator_obj.num_pages > 11:
        if page_num-5 < 1:
            page_range = range(1, 12)
        elif page_num+5 > paginator_obj.num_pages:
            page_range = range(paginator_obj.num_pages-10, paginator_obj.num_pages+1)
        else:
            page_range = range(page_num-5, page_num+6)
    else:
        page_range = range(1, paginator_obj.num_pages+1)
    if request.method == "POST":
        Author.objects.filter(aid=number).update(
            author_name=request.POST.get("author_name"),
        )
        url = reverse("app01_show_author", args=(number,))
        return redirect(url)
    return render(request, "show_author.html", locals())


@login_required
def add_author(request):
    if request.method == "POST":
        if request.POST.get("author_name"):
            try:
                author_obj = Author.objects.create(
                    author_name=request.POST.get("author_name"),
                )
                if author_obj:
                    return redirect("/index/")
            except Exception as e:
                title = "swal('该作者已存在...')"
    return render(request, "add_author.html", locals())


@login_required
def del_author(request, name):
    Author.objects.filter(author_name=name).delete()
    return redirect("/index/")


@login_required
def logout(request):
    auth.logout(request)
    return redirect("/login/")


@login_required
def set_password(request):
    form_obj = SetPassword()
    user = request.user
    if request.method == "POST":
        form_obj = SetPassword(request.POST)
        if form_obj.is_valid():
            old_password = request.POST.get("old_password")
            new_password = request.POST.get("new_password")
            confirm_password = request.POST.get("confirm_password")
            if user.check_password(old_password):
                user.set_password(new_password)
                user.save()
                return redirect("/login/")
            else:
                title = "swal('原密码输入错误...')"
    return render(request, "set_password.html", locals())

six_module_one_work文件夹

 __init__.py

import pymysql

pymysql.install_as_MySQLdb()

settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': '2021122101',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'USER': 'root',
        'PASSWORD': '123456',
    }
}

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]

LOGIN_URL = "/login/"

urls.py

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path("login/", views.login, name="app01_login"),
    path("reg/", views.register, name="app01_reg"),
    path("index/", views.index, name="app01_index"),
    path("add_book/", views.add_book, name="app01_add_book"),
    path("delete_book///", views.delete_book, name="app01_delete_book"),
    path("edit_book///", views.edit_book, name="app01_edit_book"),
    path("show_publish//", views.show_publish, name="app01_show_publish"),
    path("add_publish/", views.add_publish, name="app01_add_publish"),
    path("del_publish//", views.del_publish, name="app01_del_publish"),
    path("show_author//", views.show_author, name="app01_show_author"),
    path("add_author/", views.add_author, name="app01_add_author"),
    path("del_author//", views.del_author, name="app01_del_author"),
    path("logout/", views.logout, name="app01_logout"),
    path("set_password/", views.set_password, name="app01_set_password"),
    path("", views.login),
]

templates文件夹

add_author.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    {% load static %}
    <script src="{% static 'jQuery3.6.js' %}">script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}">script>
    <link rel="stylesheet" href="{% static 'sweetalert/sweetalert.css' %}">
    <script src="{% static 'sweetalert/sweetalert.min.js' %}">script>
head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h3>添加作者h3>
            <a href="{% url 'app01_index' %}" class="pull-right"><button class="btn btn-primary">首页button>a>
            <br><br>
            <form action="" method="post">
                {% csrf_token %}
                <p>
                    作者姓名:
                    <input type="text" name="author_name" class="form-control">
                p>
                <p>
                    <input type="submit" class="btn btn-block btn-info">
                p>
            form>
        div>
    div>
div>
<script>
    {{ title|safe }}
script>
body>
html>

add_book.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    {% load static %}
    <script src="{% static 'jQuery3.6.js' %}">script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}">script>
head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h3 class="text-center">添加书籍h3>
            <a href="{% url 'app01_index' %}" class="pull-right"><button class="btn btn-primary">首页button>a>
            <br><br>
            <form action="" method="post" novalidate>
                {% csrf_token %}
                <p>
                    书籍名称:
                    <input type="text" name="book_name" class="form-control">
                    <span style="color: red">{{ form_obj.book_name.errors.0 }}span>
                p>
                <p>
                    书籍价格:
                    <input type="text" name="book_price" class="form-control">
                    <span style="color: red">{{ form_obj.book_price.errors.0 }}span>
                p>
                <p>
                    出版日期:
                    <input type="date" name="book_date" class="form-control">
                p>
                <p>
                    出版社:
                    <select name="publish_id" id="" class="form-control">
                        {% for publish in publishs %}
                            <option value="{{ publish.pk }}">{{ publish.pub_name }}option>
                        {% endfor %}
                    select>
                p>
                <p>
                    作者:
                    <select name="author_id" id="" class="form-control" multiple>
                        {% for author in authors %}
                            <option value="{{ author.pk }}">{{ author.author_name }}option>
                        {% endfor %}
                    select>
                p>
                <input type="submit" value="添加" class="btn btn-success btn-block">
            form>
        div>
    div>
div>
body>
html>

add_publish.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    {% load static %}
    <script src="{% static 'jQuery3.6.js' %}">script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}">script>
head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h3>添加出版社h3>
            <a href="{% url 'app01_index' %}" class="pull-right"><button class="btn btn-primary">首页button>a>
            <br><br>
            <form action="" method="post">
                {% csrf_token %}
                <p>
                    出版社名称:
                    <input type="text" name="pub_name" class="form-control">
                p>
                <p>
                    出版社地址:
                    <input type="text" name="pub_addr" class="form-control">
                p>
                <p>
                    出版社邮箱:
                    <input type="text" name="pub_email" class="form-control">
                p>
                <p>
                    <input type="submit" value="添加" class="btn btn-info btn-block">
                p>
            form>
        div>
    div>
div>
body>
html>

index.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    {% load static %}
    <script src="{% static 'jQuery3.6.js' %}">script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}">script>
    <link rel="stylesheet" href="{% static 'sweetalert/sweetalert.css' %}">
    <script src="{% static 'sweetalert/sweetalert.min.js' %}">script>
head>
<body>
<div class="container">
    <div class="row">
        <nav class="navbar navbar-inverse">
            <div class="container-fluid">
                
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                            data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                        <span class="sr-only">Toggle navigationspan>
                        <span class="icon-bar">span>
                        <span class="icon-bar">span>
                        <span class="icon-bar">span>
                    button>
                    <a class="navbar-brand" href="#">Branda>
                div>

                
                <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                    <ul class="nav navbar-nav">
                        <li class="active"><a href="#">Link <span class="sr-only">(current)span>a>li>
                        <li><a href="#">Linka>li>
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
                               aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret">span>a>
                            <ul class="dropdown-menu">
                                <li><a href="#">Actiona>li>
                                <li><a href="#">Another actiona>li>
                                <li><a href="#">Something else herea>li>
                                <li role="separator" class="divider">li>
                                <li><a href="#">Separated linka>li>
                                <li role="separator" class="divider">li>
                                <li><a href="#">One more separated linka>li>
                            ul>
                        li>
                    ul>
                    <form class="navbar-form navbar-left">
                        <div class="form-group">
                            <input type="text" class="form-control" placeholder="Search">
                        div>
                        <button type="submit" class="btn btn-default">Submitbutton>
                    form>
                    <ul class="nav navbar-nav navbar-right">
                        <li><a href="#">Linka>li>
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
                               aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret">span>a>
                            <ul class="dropdown-menu">
                                <li><a href="#">Actiona>li>
                                <li><a href="#">Another actiona>li>
                                <li><a href="#">Something else herea>li>
                                <li role="separator" class="divider">li>
                                <li><a href="#">Separated linka>li>
                            ul>
                        li>
                    ul>
                div>
            div>
        nav>
        <div class="row">
                <a href="{% url 'app01_logout' %}">
                    <button class="btn btn-danger pull-left">退出button>
                a>
                 
                <a href="{% url 'app01_set_password' %}">
                    <button class="btn btn-success">修改密码button>
                a>
                <a href="{% url 'app01_add_book' %}">
                    <button class="btn btn-primary pull-right">添加书籍button>
                a>
                <br><br><br>
                <table class="table table-bordered table-striped table-condensed">
                    <thead>
                    <tr>
                        <th class="text-center">序号th>
                        <th class="text-center">书籍名称th>
                        <th class="text-center">书籍价格th>
                        <th class="text-center">出版日期th>
                        <th class="text-center">出版社th>
                        <th class="text-center">作者th>
                        <th class="text-center">操作th>
                    tr>
                    thead>
                    <tbody>
                    {% for book in current_page %}
                        <tr class="text-center">
                            <td>
                                {% if forloop.first %}
                                    {{ current_page.start_index }}
                                {% else %}
                                    {{ current_page.start_index|add:forloop.counter0 }}
                                {% endif %}
                            td>
                            <td>{{ book.book_name }}td>
                            <td>{{ book.book_price }}td>
                            <td>{{ book.book_date|date:'Y-m-d' }}td>
                            <td><a href="{% url 'app01_show_publish' book.publish.pid %}">{{ book.publish.pub_name }}a>td>
                            <td>
                                {% for author in book.authors.all %}
                                    {% if forloop.last %}
                                        <a href="{% url 'app01_show_author' author.aid %}" target="_blank">{{ author.author_name }}a>
                                    {% else %}
                                        <a href="{% url 'app01_show_author' author.aid %}" target="_blank">{{ author.author_name }},a>
                                    {% endif %}
                                {% endfor %}
                            td>
                            <td>
                                <button class="btn btn-danger btn-xs b2" number_01="{{ book.pk }}" >编辑button>
                                <button class="btn btn-warning btn-xs b1" number={{ book.pk }}>删除button>
                            td>
                        tr>
                    {% endfor %}
                    <select name="" id="i6" style="display: none">
                        {% for publish in publishs %}
                            <option value={{ publish.pid }}>{{ publish.pub_name }}option>
                        {% endfor %}
                    select>
                    <select name="" id="i7" style="display: none" multiple>
                        {% for author in authors %}
                            <option value={{ author.pk }}>{{ author.author_name }}option>
                        {% endfor %}
                    select>
                    tbody>
                table>
                <nav aria-label="Page navigation" class="text-center">
                    <ul class="pagination">
                        <li>
                            <a href="?page=1" aria-label="Previous">
                                <span aria-hidden="true">首页span>
                            a>
                        li>
                        {% if current_page.has_previous %}
                            <li>
                                <a href="?page={{ current_page.previous_page_number }}" aria-label="Previous">
                                    <span aria-hidden="true">«span>
                                a>
                            li>
                        {% else %}
                            <li class="disabled">
                                <span aria-hidden="true">«span>
                            li>
                        {% endif %}
                        {% for page in page_range %}
                            {% if page == page_num %}
                                <li class="active"><a href="?page={{ page }}">{{ page }}a>li>
                            {% else %}
                                <li><a href="?page={{ page }}">{{ page }}a>li>
                            {% endif %}
                        {% endfor %}
                        {% if current_page.has_next %}
                            <li>
                                <a href="?page={{ current_page.next_page_number }}" aria-label="Next">
                                    <span aria-hidden="true">»span>
                                a>
                            li>
                        {% else %}
                            <li class="disabled">
                                <span aria-hidden="true">»span>
                            li>
                        {% endif %}
                        <li>
                            <a href="?page={{ paginator_obj.num_pages }}" aria-label="Previous">
                                <span aria-hidden="true">尾页span>
                            a>
                        li>
                    ul>
                nav>

                
                <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
                    <div class="modal-dialog" role="document">
                        <div class="modal-content">
                            <div class="modal-header">
                                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
                                        aria-hidden="true">×span>button>
                                <h4 class="modal-title" id="myModalLabel">编辑书籍h4>
                            div>
                            <div class="modal-body">
                                <p>
                                    书籍名称:
                                    <input type="text" name="book_name" class="form-control" id="i1" disabled>
                                p>
                                <p>
                                    书籍价格:
                                    <input type="text" name="book_price" class="form-control" id="i2">
                                p>
                                <p>
                                    出版日期:
                                    <input type="date" name="book_date" class="form-control" id="i3">
                                p>
                                <p>
                                    出版社:
                                    <select name="publish_id" id="i4" class="form-control">
                                    select>
                                p>
                                <p>
                                    作者:
                                    <select name="authors_id" id="i5" class="form-control" multiple>
                                    select>
                                p>
                            div>
                            <div class="modal-footer">
                                <button type="button" class="btn btn-default" data-dismiss="modal">取消button>
                                <button type="button" class="btn btn-primary b3">保存button>
                            div>
                        div>
                    div>
                div>
        div>
    div>
div>

<script>
    $(".b1").click(function () {
        let number = $(this).attr("number");
        swal({
                title: "确定要删除吗?",
                text: "删除后书籍就没有了!",
                type: "warning",
                showCancelButton: true,
                confirmButtonClass: "btn-danger",
                confirmButtonText: "确定!",
                cancelButtonText: "取消!",
                closeOnConfirm: false,
                closeOnCancel: false
            },
            function (isConfirm) {
                if (isConfirm) {
                    $.ajax({
                        url: '/delete_book/' + number + '/{{ page_num }}/',
                        type: "post",
                        data: {'csrfmiddlewaretoken': '{{ csrf_token }}'},
                        success: function (data) {
                            setTimeout(function () {
                                location.href = data;
                            }, 3000);
                        }
                    });
                    swal("Deleted!", "您已成功删除该书籍", "success");
                } else {
                    swal("Cancelled", "您已取消删除该书籍", "error");
                }
            })
    })
    $(".b2").click(function () {
        $('#myModal').modal('show');

        let value1 = $(this).parentsUntil("tbody").children('td').eq(1).html();
        let value2 = $(this).parentsUntil("tbody").children('td').eq(2).html();
        let value3 = $(this).parentsUntil("tbody").children('td').eq(3).html();
        let value_10 = $(this).parentsUntil("tbody").children("td").eq(4).children('a').html();
        let value_11 = $(this).parentsUntil("tbody").children("td").eq(5).children("a");

        $("#i1").val(value1);
        $("#i2").val(value2);
        $("#i3").val(value3);

        let value4 = $("#i6").children();
        $(value4).each(function () {
            $("#i4").append($(this));
        })
        $("#i4").children().each(function () {
            if ($(this).html() === value_10) {
                $(this).prop("selected", true);
            }
        })

        let value5 = $("#i7").children();
        $(value5).each(function () {
            $("#i5").append($(this));
        })
        let array = new Array();
        $(value_11).each(function () {
            let ss = $(this).html().split(',')[0];
            array.push(ss);
        })
        $.each(array, function (index, value) {
            $("#i5").children("option").each(function () {
                if ($(this).html() === value) {
                    $(this).prop("selected", true);
                }
            })
        })

        let number = $(this).attr("number_01");
        $(".b3").attr("number_02", number);
    })
    $(".b3").click(function () {
        let book_name = $("#i1").val();
        let book_price = $("#i2").val();
        let book_date = $("#i3").val();
        let publish_id = $("#i4").val();
        let authors_id = $("#i5").val();
        let number = $(this).attr("number_02");
        $.ajax({
            url: '/edit_book/' + number + '/{{ page_num }}/',
            type: "post",
            data: {
                'csrfmiddlewaretoken': '{{ csrf_token }}',
                'book_name': book_name,
                'book_price': book_price,
                'book_date': book_date,
                'publish_id': publish_id,
                'authors_id': authors_id,
            },
            success: function (data) {
                setTimeout(function () {
                    {#location.href = data;#}
                    window.location.reload();
                }, 2000);
            }
        })
        swal("modify successfully!", "您已成功修改该书籍", "success");
        $('#myModal').modal('hide');
    })
script>
body>
html>

login.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    {% load static %}
    <script src="{% static 'jQuery3.6.js' %}">script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}">script>
    <link rel="stylesheet" href="{% static 'sweetalert/sweetalert.css' %}">
    <script src="{% static 'sweetalert/sweetalert.min.js' %}">script>
head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h3 class="text-center text-danger">登录h3>
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for form in form_obj %}
                <p>
                {{ form.label }}: {{ form }}
                <span style="color: red">{{ form.errors.0 }}span>
                p>
                {% endfor %}
                <input type="submit" value="登录" class="btn btn-block btn-primary">
            form>
            <p class="pull-right">
                <span class="text-danger">没有账号?span>
                <a href="{% url 'app01_reg' %}">点击注册a>
            p>
        div>
    div>
div>
<script>{{ title|safe }}script>
body>
html>

reg.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    {% load static %}
    <script src="{% static 'jQuery3.6.js' %}">script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}">script>
    <link rel="stylesheet" href="{% static 'sweetalert/sweetalert.css' %}">
    <script src="{% static 'sweetalert/sweetalert.min.js' %}">script>
head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h3 class="text-center text-warning">注册h3>
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for form in form_obj %}
                <p>
                {{ form.label }}: {{ form }}
                <span style="color: red">{{ form.errors.0 }}span>
                p>
                {% endfor %}
                <input type="submit" value="注册" class="btn btn-primary btn-block">
            form>
            <p class="pull-right">
                <span class="text-danger">已有账号?span>
                <a href="{% url 'app01_login' %}">点击登录a>
            p>
        div>
    div>
div>
<script>{{ title|safe }}script>
body>
html>

set_password.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    {% load static %}
    <script src="{% static 'jQuery3.6.js' %}">script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}">script>
    <link rel="stylesheet" href="{% static 'sweetalert/sweetalert.css' %}">
    <script src="{% static 'sweetalert/sweetalert.min.js' %}">script>
head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h3 class="text-danger text-center">修改密码h3>
            <a href="{% url 'app01_index' %}">
                <button class="btn btn-primary pull-right">首页button>
            a>
            <br><br>
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for form in form_obj %}
                <p>
                    {{ form.label }}: {{ form }}
                    <span style="color: red">{{ form.errors.0 }}span>
                p>
                {% endfor %}
                <p>
                    <input type="submit" class="btn btn-success btn-block">
                p>
            form>
        div>
    div>
div>
<script>
    {{ title|safe }}
script>
body>
html>

show_author.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    {% load static %}
    <script src="{% static 'jQuery3.6.js' %}">script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}">script>
head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h3 class="text-center">作者信息及所出版书籍h3>
            <a href="{% url 'app01_index' %}" class="pull-right"><button class="btn btn-primary">首页button>a>
            <a href="{% url 'app01_add_author' %}" class="pull-left"><button class="btn btn-danger">添加作者button>a>
             
            <a href="{% url 'app01_del_author' author_obj.author_name %}"><button class="btn btn-warning">删除作者button>a>
            <form action="" method="post">
                {% csrf_token %}
                <br><br>
                <p>
                    作者姓名:
                    <input type="text" name="author_name" class="form-control" value="{{ author_obj.author_name }}">
                p>
                <p>
                    <input type="submit" value="更改" class="btn btn-block btn-info">
                p>
            form>
            <table class="table table-bordered table-hover table-condensed">
                <thead>
                <tr>
                    <th class="text-center">书籍名称th>
                    <th class="text-center">书籍价格th>
                    <th class="text-center">出版日期th>
                tr>
                thead>
                <tbody>
                {% for book in current_page %}
                <tr class="text-center">
                <td>{{ book.0 }}td>
                <td>{{ book.1 }}td>
                <td>{{ book.2|date:'Y-m-d' }}td>
                tr>
                {% endfor %}
                tbody>
            table>
            <nav aria-label="Page navigation" class="text-center">
                <ul class="pagination">
                    {% if current_page.has_previous %}
                        <li>
                            <a href="?page={{ current_page.previous_page_number }}" aria-label="Previous">
                                <span aria-hidden="true">«span>
                            a>
                        li>
                    {% else %}
                        <li class="disabled">
                            <span aria-hidden="true">«span>
                        li>
                    {% endif %}

                    {% for page in page_range %}
                        {% if page == page_num %}
                        <li class="active"><a href="?page={{ page }}">{{ page }}a>li>
                            {% else %}
                            <li><a href="?page={{ page }}">{{ page }}a>li>
                        {% endif %}
                    {% endfor %}

                    {% if current_page.has_next %}
                        <li>
                            <a href="?page={{ current_page.next_page_number }}" aria-label="Next">
                                <span aria-hidden="true">»span>
                            a>
                        li>
                    {% else %}
                        <li class="disabled">
                                <span aria-hidden="true">»span>
                        li>
                    {% endif %}
                ul>
            nav>
        div>
    div>
div>
body>
html>

show_publish.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    {% load static %}
    <script src="{% static 'jQuery3.6.js' %}">script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}">script>
head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
                <h3 class="text-center">出版社信息及所出版书籍h3>
                <a href="{% url 'app01_index' %}" class="pull-right"><button class="btn btn-primary">首页button>a>
                <a href="{% url 'app01_add_publish' %}" class="pull-left"><button class="btn btn-danger">添加出版社button>a>
                 
                <a href="{% url 'app01_del_publish' publish_obj.pub_name %}"><button class="btn btn-warning">删除出版社button>a>
            <form action="" method="post">
                {% csrf_token %}
                <br><br>
                <p>
                    出版社名称:
                    <input type="text" name="pub_name" value="{{ publish_obj.pub_name }}" class="form-control">
                p>
                <p>
                    出版社地址:
                    <input type="text" name="pub_addr" value="{{ publish_obj.pub_addr }}" class="form-control">
                p>
                <p>
                    出版社邮箱:
                    <input type="text" name="pub_email" value="{{ publish_obj.pub_email }}" class="form-control">
                p>
                <p>
                    <input type="submit" value="更改" class="btn btn-block btn-info">
                p>
            form>
            <table class="table table-bordered table-hover table-condensed">
                <thead>
                <tr>
                    <th class="text-center">书籍名称th>
                    <th class="text-center">书籍价格th>
                    <th class="text-center">出版日期th>
                tr>
                thead>
                <tbody>
                {% for book in current_page %}
                    <tr class="text-center">
                        <td>{{ book.0 }}td>
                        <td>{{ book.1 }}td>
                        <td>{{ book.2|date:'Y-m-d' }}td>
                    tr>
                {% endfor %}
                tbody>
            table>
            <nav aria-label="Page navigation" class="text-center">
                <ul class="pagination">
                    {% if current_page.has_previous %}
                        <li>
                            <a href="?page={{ current_page.previous_page_number }}" aria-label="Previous">
                                <span aria-hidden="true">«span>
                            a>
                        li>
                    {% else %}
                        <li class="disabled">
                            <span aria-hidden="true">«span>
                        li>
                    {% endif %}

                    {% for page in page_range %}
                        {% if page == page_num %}
                        <li class="active"><a href="?page={{ page }}">{{ page }}a>li>
                            {% else %}
                            <li><a href="?page={{ page }}">{{ page }}a>li>
                        {% endif %}
                    {% endfor %}

                    {% if current_page.has_next %}
                        <li>
                            <a href="?page={{ current_page.next_page_number }}" aria-label="Next">
                                <span aria-hidden="true">»span>
                            a>
                        li>
                    {% else %}
                        <li class="disabled">
                                <span aria-hidden="true">»span>
                        li>
                    {% endif %}
                ul>
            nav>
        div>
    div>
div>
body>
html>

README.txt

"""
环境配置:
        Django版本:2.0.1
        MySQL版本:5.7.35
        MySQL准备数据库:'2021122101'
        MySQL链接用户名及密码:'root'/'123456'

系统初始化数据:
        运行app01.tests.py中注释的代码即可
        初始化数据有系统初始登录账号及密码,作者/出版社/书籍/作者书籍关系这几张表数据

功能:
        项目启动后,首先进入登录页面,登录页面与注册页面各有a标签链接进行交互切换,注册成功后自动跳转到登陆页面;
        首页展示了书籍列表,可以对书籍进行添加、编辑、删除操作;出版社及作者均可进行点击查看,跳转到对应页面查看信息;
        点击出版社,页面会展示该出版社所出版的所有书籍,还可对出版社进行添加、编辑、删除操作;
        点击作者,弹出新页面,该页面展示该作者所关联的书籍信息,还可对作者进行添加、编辑、删除操作;
        首页点击退出,即退出登录回到登录页面;

        修改书籍信息,弹出模态框,模态框回显该书籍信息,且书籍名称不可编辑,点击模态框保存按钮发送Ajax请求;
        添加书籍信息,书籍名称不能重复;
        删除书籍,均有二次确认操作;
"""

如何一次性将项目所依赖的三方库版本信息导入及导出?

"""
pycharm中的命令行输入如下命令即可:

生成requirements.txt文件:pip freeze > requirements.txt

安装requirements.txt依赖:pip install -r requirements.txt
"""


""" requirements.txt导出信息展示 certifi==2021.5.30 charset-normalizer==2.0.4 click==8.0.1 colorama==0.4.4 DBUtils==2.0.2 decorator==4.4.2 Django==2.0.1 et-xmlfile==1.1.0 Flask==2.0.1 idna==3.2 imageio==2.9.0 imageio-ffmpeg==0.4.5 itsdangerous==2.0.1 Jinja2==3.0.1 MarkupSafe==2.0.1 moviepy==1.0.3 numpy==1.21.2 openpyxl==3.0.7 Pillow==8.3.2 prettytable==2.2.0 proglog==0.1.9 PyMySQL==1.0.2 pytz==2021.3 requests==2.26.0 tqdm==4.62.2 urllib3==1.26.6 wcwidth==0.2.5 Werkzeug==2.0.1 """

该程序已发现的小BUG

"""
编辑书籍信息的模态框,选择作者是多选的下拉框,没有给模态框的取消按钮绑定点击事件,所以当重新选择了作者后点击取消按钮,此时再次点击书籍编辑按钮弹出模态框时,作者的默认选中标签信息与书籍的作者信息不相符。

解决方案:给模态框的取消按钮及右上角关闭按钮都绑定同一个事件,即把作者的多选下拉框的子标签option标签的默认选中属性都取消?
"""