1. 图书管理项目

图书管理项目

版权声明:本博客来自路飞学城Python全栈开发培训课件,仅用于学习之用,严禁用于商业用途。
欢迎访问路飞学城官网:https://www.luffycity.com/

1. 需求分析

本项目是基于Django实现图书增删改查功能,是对Django基础内容的综合应用,因本人初学,水平有限,代码仅供参考。

项目需求如下:

  • 扩展Auth模块自定义用户表实现用户登录、注册
  • 列出图书列表、出版社列表、作者列表
  • 点击作者,会在新的页面列出该作者出版的图书列表
  • 点击出版社,会列出该出版社旗下图书列表
  • 可以创建、修改、删除 图书、作者、出版社
  • 点击修改按钮,弹出模块框,模态框中展示该书的信息且信息可以修改
  • 书名不可重复,不可修改
  • 修改图书信息时,使用ajax请求发送信息

2. 设计及生成表结构

表结构设计

  • 用户表UserInfo:nid、tel

  • 书籍表 Book:title 、 price 、 pub_date 、 publish(外键,多对一) 、 authors(多对多)

  • 出版社表 Publish:name 、 city 、 email

  • 作者表 Author:name 、 age 、gender

创建数据库并配置

创建一个名为 book数据库,编码指定为 utf8:

create database book default charset=utf8;   

在Django项目的settings.py 文件中找到 DATABASES 配置项,配置数据库连接信息:

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

在Django项目的__init__.py文件中写如下代码,告诉 Django 使用 pymysql 模块连接 mysql 数据库:

# 在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置
import pymysql
pymysql.install_as_MySQLdb()

创建模型

from django.contrib.auth.models import AbstractUser
from django.db import models
# Create your models here.


# 用户表
class UserInfo(AbstractUser):
    """
    用户信息表
    """
    nid = models.AutoField(primary_key=True)
    tel = models.CharField(max_length=11, null=True, unique=True)


# 出版社表
class Publish(models.Model):
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=64)
    email = models.EmailField()


# 作者表
class Author(models.Model):
    gender_choices = (
        ('0', '女'),
        ('1', '男'),
        ('2', '保密'),
    )
    name = models.CharField(max_length=32)
    age = models.SmallIntegerField()
    gender = models.SmallIntegerField(choices=gender_choices)


# 书籍表
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    pub_date = models.DateField()
    # 一对多关系,一个出版社可以有多本图书
    publish = models.ForeignKey("Publish", on_delete=models.CASCADE)
    # 多对多关系,一个作者可以有多本图书,一本图书也可以有多个作者
    authors = models.ManyToManyField("Author")

创建表结构

通过两条数据库迁移命令即可在指定的数据库中创建表 ,在命令行中运行:

$ python3 manage.py makemigrations  # 让 Django 知道我们在我们的模型有一些变更
$ python3 manage.py migrate   # 创建表结构

3. 用户注册及登录功能

3.1 用户注册

创建From模型

# myForms.py文件
import re
from django import forms
from django.core.exceptions import ValidationError
from app01 import models
from django.forms import widgets


# 自定义验证规则
def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value):
        raise ValidationError('手机号码格式错误')


class UserForm(forms.Form):
    username = forms.CharField(max_length=32,
                               min_length=5,
                               label="用户名",
                               error_messages={"required": "该字段不能为空",
                                               'min_length': '用户名最少为5个字符',
                                               'max_length': '用户名最多为32个字符'},
                               widget=widgets.TextInput(attrs={"class": "form-control",
                                                               'placeholder': '用户名'}, ))
    pwd = forms.CharField(max_length=32,
                          error_messages={"required": "该字段不能为空"},
                          label="密码",
                          widget=widgets.PasswordInput(attrs={"class": "form-control",
                                                              'placeholder': '密码'}, ))
    re_pwd = forms.CharField(max_length=32,
                             error_messages={"required": "该字段不能为空"},
                             label="确认密码",
                             widget=widgets.PasswordInput(attrs={"class": "form-control",
                                                                 'placeholder': '确认密码'}, ))
    email = forms.EmailField(max_length=32,
                             error_messages={"required": "该字段不能为空",
                                             'invalid': u'邮箱格式错误'},
                             label="邮箱",
                             widget=widgets.EmailInput(attrs={"class": "form-control",
                                                              'placeholder': u'邮箱'}, ))
    tel = forms.CharField(validators=[mobile_validate, ],
                          error_messages={"required": "该字段不能为空"},
                          label="手机号码",
                          widget=widgets.TextInput(attrs={"class": "form-control",
                                                          'placeholder': u'手机号码'}, ))

    def clean_user(self):
        val = self.cleaned_data.get("user")

        user = models.UserInfo.objects.filter(username=val).first()
        if not user:
            return val
        else:
            raise ValidationError("该用户已注册!")

    def clean(self):
        pwd = self.cleaned_data.get("pwd")
        re_pwd = self.cleaned_data.get("re_pwd")

        if pwd and re_pwd:
            if pwd == re_pwd:
                return self.cleaned_data
            else:
                raise ValidationError("两次密码不一致!")
        else:
            return self.cleaned_data

配置路由

# url.py
urlpatterns = [
    path('admin/', admin.site.urls),
    path('register/', views.register),
    path('login/', views.login),
    path('logout/', views.logout),

创建视图函数

# views.py文件
from django.shortcuts import render, redirect, HttpResponse
from app01 import models
from app01 import myForms
from django.contrib.auth.models import User
from django.contrib import auth
from django.contrib.auth.decorators import login_required


def register(request):
    if request.method == "POST":
        form = myForms.UserForm(request.POST)
        print(request.POST)
        if form.is_valid():
            data = form.cleaned_data
            print(data)
            # 使用django认证组件
            username = data.get('username')
            pwd = data.get('pwd')
            email = data.get('email')
            tel = data.get('tel')
            models.UserInfo.objects.create_user(username=username,
                                                password=pwd,
                                                email=email,
                                                tel=tel)
            return redirect('/login/')
        else:
            errors = form.errors.get("__all__")
            return render(request, "register.html", {"form": form, "errors": errors})
    form = myForms.UserForm()
    return render(request, "register.html", {"form": form})

创建模板

register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>用户注册</title>
    <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <style>
        .error{
            color: red;
        }
    </style>
</head>
<body>
	<nav class="navbar navbar-inverse" role="navigation">
		<div class="container-fluid">
			<div class="navbar-header">
				<a class="navbar-brand" href="#">网站名</a>
			</div>
			<div>
                <ul class="nav navbar-nav navbar-right">
                    <li><a href="/register/"><span class="glyphicon glyphicon-user"></span> 注册</a></li>
                    <li><a href="/login/"><span class="glyphicon glyphicon-log-in"></span> 登录</a></li>
                </ul>
			</div>
		</div>
	</nav>
	<div class="container">
		<div class="row">
            <div class="col-sm-4 col-sm-offset-4">
                <form action="" method="post" novalidate >
                    {% csrf_token %}
                    <fieldset >
                        <div class="form-group">
                            <label for="id_{{ form.username.name }}">{{ form.username.label }}</label>
                            {{ form.username }} 
                            <span class="pull-right error">{{ form.username.errors.0 }}</span>

                        </div>
                        <div class="form-group">
                            <label for="id_{{ form.pwd.name }}">{{ form.pwd.label }}</label>
                            {{ form.pwd }} <span class="pull-right error">{{ form.pwd.errors.0 }}</span>

                        </div>
                        <div class="form-group">
                            <label for="id_{{ form.re_pwd.name }}">{{ form.re_pwd.label }}</label>
                            {{ form.re_pwd }}
                            <span class="pull-right error">{{ form.re_pwd.errors.0 }}</span>
                            <span class="pull-right error">{{ errors.0 }}</span>

                        </div>
                        <div class="form-group">
                            <label for="id_{{ form.email.name }}"> {{ form.email.label }}</label>
                            {{ form.email }} <span class="pull-right error">{{ form.email.errors.0 }}</span>
                        </div>
                        <div class="form-group">
                            <label for="id_{{ form.tel.name }}"> {{ form.tel.label }}</label>
                            {{ form.tel }} <span class="pull-right error">{{ form.tel.errors.0 }}</span>
                        </div>

                        <button type="submit" id="btn" class="btn btn-primary pull-right">注册</button>
                    </fieldset>
                </form>
            </div>
        </div>
    </div>
</body>
</html>

form表单的novalidate属性

作用: 在表单提交时不对表单中输入的值进行验证;

语法: 

特点:

  1. HTML5之后的新属性;
  2. 使用于表单以及标签的以下类型:
    text,search,url,telephone,email,password,date pickers,range以及color。

效果展示

在这里插入图片描述

3.2 用户登录与注销

配置路由

同上

创建视图函数

# 登录
def login(request):
    if request.method == "GET":
        return render(request, "login.html")
    user = request.POST.get('username')
    pwd = request.POST.get('pwd')
    user = auth.authenticate(username=user, password=pwd)
    if not user:
        return redirect('/login/')
    else:
        auth.login(request, user)
        path = request.GET.get('next') or '/books/'
        return redirect(path)


# 注销
def logout(request):
    auth.logout(request)
    return redirect('/login/')

创建模板

login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>用户登录</title>
    <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
	<nav class="navbar navbar-inverse" role="navigation">
		<div class="container-fluid">
			<div class="navbar-header">
				<a class="navbar-brand" href="#">网站名</a>
			</div>
			<div>
                <ul class="nav navbar-nav navbar-right">
                    <li><a href="/register/"><span class="glyphicon glyphicon-user"></span> 注册</a></li>
                    <li><a href="/login/"><span class="glyphicon glyphicon-log-in"></span> 登录</a></li>
                </ul>
			</div>
		</div>
	</nav>
	<div class="container">
		<div class="row">
            <div class="col-sm-4 col-sm-offset-4">
                <form action="" method="post" novalidate >
                    {% csrf_token %}
                    <fieldset >
                        <div class="form-group">
                            <label for="username">用户名</label>
                            <input type="text" class="form-control" name="username" id="username"
                                   placeholder="请输入用户名">
                        </div>
                        <div class="form-group">
                            <label for="pwd">密码</label>
                            <input type="password" class="form-control" name="pwd" id="pwd" 
                                   placeholder="请输入密码">
                        </div>
                        <button type="submit" id="btn" class="btn btn-primary pull-right">登录</button>
                    </fieldset>
                </form>
            </div>
        </div>
    </div>
</body>
</html>

4. 图书信息的增删改查

图书列表分全部图书、按作者id查询图书、按出版社id查询图书三个页面。

创建From模型

# myForm.py文件

class BookForm(forms.Form):
    title = forms.CharField(min_length=2,
                            max_length=32,
                            label="书籍名称",
                            error_messages={"required": "该字段不能为空",
                                            'min_length': '最少为2个字符',
                                            'max_length': '最多为32个字符'},
                            widget=widgets.TextInput(attrs={"class": "form-control"}))
    price = forms.DecimalField(label="价格", error_messages={"required": "该字段不能为空!"},
                               widget=widgets.NumberInput(attrs={"class": "form-control"}))
    pub_date = forms.DateField(label="出版日期", 
                               error_messages={"required": "该字段不能为空!"}, 
                               widget=widgets.DateInput(attrs={"class": "form-control",
                                                               "type": "Date"}))
    publish = forms.ChoiceField(label="出版社", error_messages={"required": "该字段不能为空"},
                                widget=widgets.Select(attrs={"class": "form-control"}))
    authors = forms.MultipleChoiceField(label="作者", error_messages={"required": "该字段不能为空"},
                                        widget=widgets.SelectMultiple(attrs={"class": "form-control"}))

    # 选项来源于数据库,在初始化中提取最新数据
    def __init__(self, *args, **kwargs):
        super(BookForm, self).__init__(*args, **kwargs)
        self.fields['publish'].choices = models.Publish.objects.all().values_list('pk', 'name')
        self.fields['authors'].choices = models.Author.objects.all().values_list('pk', 'name')

    def clean_title(self):
        val = self.cleaned_data.get("title")
        ret = models.Book.objects.filter(title=val)
        if not ret:
            return val
        else:
            raise ValidationError("该书籍信息已录入!")

配置路由

# url.py
urlpatterns = [
    ...    
    path('books/', views.select_books),
    path('books/author/<int:id>', views.select_books_by_author),
    path('books/publish/<int:id>', views.select_books_by_publish),
    path('addbook/', views.add_book),
    path('delbook/<int:id>', views.del_book),
    path('editbook/', views.update_book),
    ...

创建视图函数

@login_required
def add_book(request):
    if request.method == "GET":
        form = myForms.BookForm()
        return render(request, "addbook.html", {"form": form})

    form = myForms.BookForm(request.POST)
    if form.is_valid():  # 进行数据校验
        # 校验成功
        data = form.cleaned_data  # 校验成功的值,会放在cleaned_data里。
        book = models.Book(
            title=data.get('title'),
            price=data.get('price'),
            pub_date=data.get('pub_date'),
            publish_id=data.get('publish'),
        )
        book.save()
        author_id_list = data.get('authors')
        book.authors.set(author_id_list)
        return redirect('/books/')
    else:
        return render(request, "addbook.html", {"form": form})


@login_required
def select_books(request):
    book_list = models.Book.objects.all()
    return render(request, 'books.html', {'book_list': book_list})


@login_required
def del_book(request, id):
    models.Book.objects.filter(pk=id).delete()
    return redirect('/books/')


@login_required
def select_books_by_author(request, id):
    author = models.Author.objects.filter(nid=id).first()
    book_list = author.book_set.all()
    return render(request, 'books.html', {'book_list': book_list})


@login_required
def select_books_by_publish(request, id):
    publish = models.Publish.objects.filter(nid=id).first()
    book_list = publish.book_set.all()
    return render(request, 'books.html', {'book_list': book_list})


@login_required
def update_book(request):
    if request.method == 'POST':
        nid = request.POST.get('nid')
        book = models.Book.objects.filter(pk=nid).first()
        book.title = request.POST.get('title')
        book.price = request.POST.get('price')
        book.pub_date = request.POST.get('pub_date')
        book.publish_id = request.POST.get('publish_id')
        book.save()
        author_id_list = json.loads(request.POST.get('author_id_list'))
        book.authors.set(author_id_list)
        return redirect('/books/')
    else:
        nid = request.GET.get('nid')
        res = {}
        book_obj = models.Book.objects.filter(pk=nid).first()
        if book_obj:
            author_id_list = []
            authors = book_obj.authors.all()
            for author in authors:
                author_id_list.append(author.pk)
            book_info = {
                "pk": book_obj.pk,
                "title": book_obj.title,
                "price": book_obj.price,
                "pub_date": book_obj.pub_date,
                "publish_id": book_obj.publish.pk,
                "author_id_list": author_id_list,
            }
            publish_list = list(models.Publish.objects.values_list("pk", "name"))
            author_list = list(models.Author.objects.values_list("pk", "name"))
            res["book_info"] = book_info
            res["publish_list"] = publish_list
            res["author_list"] = author_list
            return JsonResponse({"res": res})

创建基础模板

base.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>图书管理</title>
    <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
	<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <style>
        .error{
            color: red;
        }
    </style>
</head>
<body>
	<nav class="navbar navbar-inverse" role="navigation">
		<div class="container-fluid">
			<div class="navbar-header">
		        <button type="button" class="navbar-toggle" data-toggle="collapse"
		                data-target="#example-navbar-collapse">
		            <span class="sr-only">切换导航</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 class="collapse navbar-collapse" id="example-navbar-collapse">
				<ul class="nav navbar-nav">
					<li><a href="/books/">书籍</a></li>
					<li><a href="/authors/">作者</a></li>
					<li><a href="/publishs/">出版社</a></li>
				</ul>
                <ul class="nav navbar-nav navbar-right">
                    <li ><a href=""><span>您好:</span>{{ request.user }}</a> </li>
                    <li><a href="/logout/"><span class="glyphicon glyphicon-log-out"></span> 注销</a></li>
                </ul>
			</div>
		</div>
	</nav>
	<div class="container">
		<div class="row">
			<div class="col-sm-2">
				<h3>菜单</h3>
				<ul class="nav nav-pills nav-stacked">
					<li><a href="/books/">书籍</a></li>
					<li><a href="/authors/">作者</a></li>
					<li><a href="/publishs/">出版社</a></li>
				</ul>
				<hr class="hidden-sm hidden-md hidden-lg">
			</div>
            <div class="col-sm-8">
                {% block content %}
                {% endblock content %}
            </div>
        </div>
    </div>
    <script>
        $(function () {
            $('ul.nav').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>
</body>
</html>

注:基础模板下面的js是为了实现页面左侧菜单显示激活状态跟随页面路径进行切换。

创建图书列表子模板

books.html:

{% extends 'base.html' %}

{% block content %}
    <h3>查看书籍</h3>
    <a class="btn btn-primary" href="/addbook/" style="margin-bottom: 10px;">添加书籍</a>
    <table class="table" >
        <thead>
            <tr>
                <th>序号</th>
                <th>书名</th>
                <th>价格</th>
                <th>出版日期</th>
                <th>出版社</th>
                <th>作者</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            {% for book in book_list %}
                <tr>
                    <td>{{ forloop.counter }}</td>
                    <td>{{ book.title }}</td>
                    <td>{{ book.price }}</td>
                    <td>{{ book.pub_date|date:'Y-m-d' }}</td>
                    <td>{{ book.publish.name }}</td>
                    <td>
                        {% for author in book.authors.all %}
                            {% if forloop.last %}
                                {{ author.name }}
                            {% else %}
                                {{ author.name }}、
                            {% endif %}
                        {% endfor %}
                    </td>
                    <td>
                        <a class="btn btn-info editBook" data-toggle="modal" 
                           data-target="#editBookModal" index={{ book.pk }}>编辑</a>
                        <a href="/delbook/{{ book.pk }}" class="btn btn-danger" >删除</a>
                    </td>
                   </tr>
            {% endfor %}
        </tbody>
    </table>
    <!-- 图书编辑模态框(Modal) -->
    <div class="modal fade" id="editBookModal" tabindex="-1" 
         role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" 
                            aria-hidden="true">&times;</button>
                    <h4 class="modal-title" id="myModalLabel">编辑书籍信息</h4>
                </div>
                <div class="modal-body">
                    <form novalidate>
                        {% csrf_token %}
                        <fieldset >
                            <input type="hidden" id="nid" name="nid" value="">
                            <div class="form-group">
                                <label for="title">书籍名称</label>
                                <input type="text" id="title" name="title" 
                                       class="form-control" value='' disabled>
                            </div>
                            <div class="form-group">
                                <label for="price">价格</label>
                                <input type="text" id="price" name="price" class="form-control" value="">
                            </div>
                            <div class="form-group">
                                <label for="pub_date">出版日期</label>
                                <input type='date' class="form-control" 
                                       id="pub_date" name="pub_date" value="">
                            </div>
                            <div class="form-group">
                                <label for="publish_id">出版社</label>
                                <select class="form-control" id="publish_id" name="publish_id">
                                </select>
                            </div>
                            <div class="form-group">
                                <label for="author_id_list">作者</label>
                                <select multiple="multiple"  class="form-control" 
                                        id="author_id_list" name="author_id_list">
                                </select>
                            </div>
                        </fieldset>
                    </form>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
                    <button type="button" class="btn btn-primary" id="updBtn">提交更改</button>
                </div>
            </div>
        </div>
    </div>
    <script>
        $(function () {
           $('.editBook').click(function () {
               var nid = '';
               nid = $(this).attr('index');
               $.ajax({
                   url: "/editbook/",
                   type: "get",
                   data: {"nid": nid},
                   success: function (data) {
                       var book = data.res.book_info;
                       var author_list = data.res.author_list;
                       var publish_list = data.res.publish_list;
                       $('#nid').attr('value', book.pk);
                       $('#title').attr('value', book.title);
                       $('#price').attr('value', book.price);
                       $('#pub_date').attr('value', book.pub_date);
                       $('#publish_id').empty();
                       for (var i = 0; i < publish_list.length; i++) {
                           if (publish_list[i][0] == book.publish_id) {
                               $('#publish_id').append(
                                   `<option value=${publish_list[i][0]} selected>
										${publish_list[i][1]}
        							</opton>`)
                           } else {
                               $('#publish_id').append(
                                   `<option value=${publish_list[i][0]} >
										${publish_list[i][1]}
        							</opton>`)
                           }
                       }
                       $('#author_id_list').empty();
                       for (var i = 0; i < author_list.length; i++) {
                           if ($.inArray(author_list[i][0], book.author_id_list) >= 0) {
                               $('#author_id_list').append(
                                   `<option value=${author_list[i][0]} selected>
										${author_list[i][1]}
        							</option>`
                               )
                           } else {
                               $('#author_id_list').append(
                                   `<option value=${author_list[i][0]} >
										${author_list[i][1]}
        							</option>`
                               )
                           }
                       }
                   }
               })
           })
           $('#updBtn').click(function () {
               var CsrfToken = $("[name='csrfmiddlewaretoken']").val();
               var options = $('#author_id_list option:selected');
               var author_id_list = new Array();
               $('#author_id_list option:selected').each(function () {
                    author_id_list.push($(this).val());
               });
               $.ajax({
                   url: "/editbook/",
                   type: "post",
                   data: {
                       "nid": $('#nid').val(),
                       "title": $('#title').val(),
                       "price": $('#price').val(),
                       "pub_date": $('#pub_date').val(),
                       "publish_id": $('#publish_id').val(),
                       "author_id_list":JSON.stringify(author_id_list) ,
                       'csrfmiddlewaretoken':CsrfToken,
                   },
                   success: function (data) {
                       $('#editBookModal').modal('hide');
                       location.reload();
                   }
               })
           })
        })
    </script>
{% endblock content %}

addbook.html:

{% extends 'base.html' %}

{% block content %}
    <form action="" method="post" novalidate>
        {% csrf_token %}
        <fieldset >
            <div class="form-group">
                <label for="id_{{ form.title.name }}">{{ form.title.label }}</label>
                {{ form.title }} <span class="pull-right error">{{ form.title.errors.0 }}</span>
            </div>
            <div class="form-group">
                <label for="id_{{ form.price.name }}">{{ form.price.label }}</label>
                {{ form.price }} <span class="pull-right error">{{ form.price.errors.0 }}</span>
            </div>
            <div class="form-group">
                <label for="id_{{ form.pub_date.name }}">{{ form.pub_date.label }}</label>                
                {{ form.pub_date }}<span class="pull-right error">{{ form.pub_date.errors.0 }}</span>
            </div>
            <div class="form-group">
                <label for="id_{{ form.publish.name }}">{{ form.publish.label }}</label>
                {{ form.publish }}
                <span class="pull-right error">{{ form.publish.errors.0 }}</span>
            </div>
            <div class="form-group">
                <label for="id_{{ form.authors.name }}">{{ form.authors.label }}</label>
                {{ form.authors }}
                <span class="pull-right error">{{ form.authors.errors.0 }}</span>
            </div>

        <button type="submit" id="btn" class="btn btn-primary pull-right">提交</button>
        </fieldset>
    </form>
{% endblock content %}

效果展示

图书列表:

在这里插入图片描述

添加书籍:

在这里插入图片描述

修改书籍:

在这里插入图片描述

5. 作者和出版社信息修改

创建From模型

...
class AuthorForm(forms.Form):
    gender_choices = (
        (0, '女'),
        (1, '男'),
        (2, '保密'),
    )
    flag = forms.CharField(label="操作标识")
    name = forms.CharField(min_length=2, label="姓名", 
                           error_messages={"min_length": "最少2位", "required": "该字段不能为空!"},
                           widget=widgets.TextInput(attrs={"class": "form-control"}))
    age = forms.IntegerField(label="年龄", error_messages={"required": "该字段不能为空"},
                             widget=widgets.TextInput(attrs={"class": "form-control"}))
    gender = forms.ChoiceField(label="性别", choices=gender_choices, 
                               error_messages={"required": "该字段不能为空"},
                               widget=forms.widgets.Select(attrs={"class": "form-control"}))

    def clean_name(self):
        val = self.cleaned_data.get("name")
        flag = self.cleaned_data.get("flag")
        ret = models.Author.objects.filter(name=val)
        if (flag == "update") or not ret:
            return val
        else:
            raise ValidationError("该用户已录入!")

    def clean_age(self):
        val = self.cleaned_data.get("age")
        if 0 < val < 200:
            return val
        else:
            raise ValidationError("年龄有误!")


class PublishForm(forms.Form):
    flag = forms.CharField(label="操作标识")
    name = forms.CharField(min_length=4, label="出版社名称", 
                           error_messages={"min_length": "最少4位", "required": "该字段不能为空!"},
                           widget=widgets.TextInput(attrs={"class": "form-control"}))
    city = forms.CharField(label="所在城市", error_messages={"required": "该字段不能为空!"},
                           widget=widgets.TextInput(attrs={"class": "form-control"}))
    email = forms.EmailField(label="出版社邮箱", error_messages={"required": "该字段不能为空!"},
                             widget=widgets.TextInput(attrs={"class": "form-control"}))

    def clean_name(self):
        val = self.cleaned_data.get("name")
        flag = self.cleaned_data.get("flag")
        ret = models.Publish.objects.filter(name=val)
        if flag == "update" or not ret:
            return val
        else:
            raise ValidationError("该出版社信息已录入!")

...

flag字段的作用:

在新加记录时需要校验数据库是不是已经存在相同名称的记录,如果有则不让添加;

但是在更新记录时,我们设置名称不能修改,那么校验时一定会存在相同的名称,这时候需要校验通过。

所以设置一个flag字段用于区分两种提交的类型。

配置路由

urlpatterns = [
    ...
    path('authors/', views.select_authors),
    path('addauthor/', views.add_author),
    path('delauthor/', views.del_author),
    path('editauthor/<int:id>', views.update_author),
    path('publishs/', views.select_publishs),
    path('addpublish/', views.add_publish),
    path('delpublish/', views.del_publish),
    path('editpublish/<int:id>', views.update_publish),
]

创建视图函数

@login_required
def add_author(request):
    if request.method == "GET":
        form = myForms.AuthorForm()
        return render(request, "addauthor.html", {"form": form})
    else:
        form = myForms.AuthorForm(request.POST)
        if form.is_valid():
            # 校验成功
            data = form.cleaned_data
            data.pop('flag')
            models.Author.objects.create(**data)
            return redirect('/authors/')
        else:
            return render(request, "addauthor.html", {"form": form})


@login_required
def select_authors(request):
    author_list = models.Author.objects.all()
    return render(request, 'authors.html', {'author_list': author_list})


@login_required
def update_author(request, id):
    author = models.Author.objects.filter(pk=id).first()
    if request.method == 'GET':
        form = myForms.AuthorForm(
            initial={
                'name': author.name,
                'age': author.age,
                'gender': author.gender,
            },
        )
        return render(request, 'updateauthor.html', {"form": form, "author": author})
    else:
        form = myForms.AuthorForm(request.POST)
        if form.is_valid():  # 进行数据校验
            # 校验成功
            data = form.cleaned_data  # 校验成功的值,会放在cleaned_data里。
            author.name = data.get('name')
            author.age = data.get('age')
            author.gender = data.get('gender')
            author.save()
            return redirect('/authors/')
        else:
            print(form.errors)  # 打印错误信息
            clean_errors = form.errors.get("__all__")
        return render(request, "updateauthor.html", {"form": form, "clean_errors": clean_errors, "author": author})


@login_required
def del_author(request):
    nid = request.GET.get('nid')
    author = models.Author.objects.filter(pk=nid).first()
    print(author)
    books = author.book_set.all()
    info = {
        "flag": None,
        "msg": None
    }
    if books:
        info['flag'] = False
        info['msg'] = "该作者有关联书籍,不能删除!"
    else:
        info['flag'] = True
        info['msg'] = "删除成功!"
        models.Author.objects.filter(pk=nid).delete()
    return JsonResponse(info)


@login_required
def select_publishs(request):
    publish_list = models.Publish.objects.all()
    return render(request, 'publishs.html', {'publish_list': publish_list})


@login_required
def add_publish(request):
    if request.method == "GET":
        form = myForms.PublishForm()
        return render(request, "addpublish.html", {"form": form})
    else:
        form = myForms.PublishForm(request.POST)
        if form.is_valid():  # 进行数据校验
            # 校验成功
            data = form.cleaned_data  # 校验成功的值,会放在cleaned_data里。
            data.pop('flag')
            models.Publish.objects.create(**data)
            return redirect('/publishs/')
        else:
            return render(request, "addpublish.html", {"form": form})


@login_required
def del_publish(request):
    nid = request.GET.get('nid')
    publish = models.Publish.objects.filter(pk=nid).first()
    books = publish.book_set.all()
    info = {
        "flag": None,
        "msg": None
    }
    if books:
        info['flag'] = False
        info['msg'] = "该出版社有关联书籍,不能删除!"
    else:
        info['flag'] = True
        info['msg'] = "删除成功!"
        models.Publish.objects.filter(pk=nid).delete()
    return JsonResponse(info)


@login_required
def update_publish(request, id):
    publish = models.Publish.objects.filter(pk=id).first()
    if request.method == 'GET':
        form = myForms.PublishForm(
            initial={
                'name': publish.name,
                'city': publish.city,
                'email': publish.email,
            },
        )
        return render(request, 'updatepublish.html', {"form": form})
    else:
        form = myForms.PublishForm(request.POST)
        if form.is_valid():  # 进行数据校验
            # 校验成功
            data = form.cleaned_data  # 校验成功的值,会放在cleaned_data里。
            publish.name = data.get('name')
            publish.city = data.get('city')
            publish.email = data.get('email')
            publish.save()
            return redirect('/publishs/')
        else:
            return render(request, "updatepublish.html", {"form": form, "publish": publish})

创建模板

addauthor.html:

{% extends 'base.html' %}

{% block content %}
    <h3>添加作者</h3>
    <form action="" method="post" novalidate>
        {% csrf_token %}
        <fieldset >
            <input type="text" name="{{ form.flag.name }}" 
                   id="id_{{ form.flag.name }}" value="add" hidden>
            <div class="form-group">
                <label for="id_{{ form.name.name }}">{{ form.name.label }}</label>
                {{ form.name }} <span class="pull-right error">{{ form.name.errors.0 }}</span>
            </div>
            <div class="form-group">
                <label for="id_{{ form.age.name }}">{{ form.age.label }}</label>
                {{ form.age }} <span class="pull-right error">{{ form.age.errors.0 }}</span>
            </div>
            <div class="form-group">
                <label for="id_{{ form.gender.name }}">{{ form.gender.label }}</label>
                {{ form.gender }}
                <span class="pull-right error">{{ form.gender.errors.0 }}</span>
            </div>

        <button type="submit" id="btn" class="btn btn-primary pull-right">提交</button>
        </fieldset>
    </form>
{% endblock content %}

authors.html:

{% extends 'base.html' %}

{% block content %}
    <h3>查看作者</h3>
    <a class="btn btn-primary" href="/addauthor/" style="margin-bottom: 10px;">添加作者</a>

    <table class="table" >
        <thead>
            <tr>
                <th>序号</th>
                <th>姓名</th>
                <th>年龄</th>
                <th>性别</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            {% for author in author_list %}
                <tr>
                    <td>{{ forloop.counter }}</td>
                    <td><a href="/books/author/{{ author.pk }}">{{ author.name }}</a></td>
                    <td>{{ author.age }}</td>
                    <td>{{ author.get_gender_display}}</td>
                    <td>
                        <a href="/editauthor/{{ author.pk }}" class="btn btn-info" >编辑</a>
                        <a class="btn btn-danger delBtn" index="{{ author.pk }}" >删除</a>
                    </td>
                   </tr>
            {% endfor %}
        </tbody>
    </table>
        <script>
        $('.delBtn').click(function () {
           nid = $(this).attr('index');
           $.ajax({
               url: "/delauthor/",
               type: "get",
               data: {nid: nid},
               success: function (data) {
                   if(data.flag){
                       location.reload();
                   }else{
                       alert(data.msg);
                   }
               }
           })
        })
    </script>
{% endblock content %}

get_gender_display():用来获取获取性别的名称而不是序号。

有时候我们创建的midels的表如上,存入数据库的是数字,需要展示的是元祖后面的信息,我们从数据库拿到的又是前面的信息,此时就需要用到 get_对应表格_display(),这样就可以得到元祖后面的数据了。

updateauthor.html:

{% extends 'base.html' %}

{% block content %}
    <h3>编辑作者</h3>
    <form action="" method="post">
        {% csrf_token %}

        <fieldset >
            <input type="text" name="{{ form.flag.name }}" value="update"  hidden>
            <div class="form-group">
                <label for="id_{{ form.name.name }}">{{ form.name.label }}</label>
                <input type="text" class="form-control" 
                       name="{{ form.name.name }}" id="id_{{ form.name.name }}" 
                       value="{{ form.name.value }}" required readonly>
                <span class="pull-right error">{{ form.name.errors.0 }}</span>
            </div>
            <div class="form-group">
                <label for="id_{{ form.age.name }}">{{ form.age.label }}</label>
                {{ form.age }} <span class="pull-right error">{{ form.age.errors.0 }}</span>
            </div>
            <div class="form-group">
                <label for="id_{{ form.gender.name }}">{{ form.gender.label }}</label>
                {{ form.gender }}
                <span class="pull-right error">{{ form.gender.errors.0 }}</span>
            </div>
            <button type="submit" class="btn btn-primary pull-right">提交</button>
        </fieldset>
    </form>
{% endblock content %}

addpublish.html:

{% extends 'base.html' %}

{% block content %}
    <h3>添加出版社</h3>
    <form action="" method="post" novalidate>
        {% csrf_token %}
        <fieldset >
            <input type="text" name="{{ form.flag.name }}" 
                   id="id_{{ form.flag.name }}" value="add" hidden>
            <div class="form-group">
                <label for="id_{{ form.name.name }}">{{ form.name.label }}</label>
                {{ form.name }} <span class="pull-right error">{{ form.name.errors.0 }}</span>
            </div>
            <div class="form-group">
                <label for="id_{{ form.city.name }}">{{ form.city.label }}</label>
                {{ form.city }} <span class="pull-right error">{{ form.city.errors.0 }}</span>
            </div>
            <div class="form-group">
                <label for="id_{{ form.email.name }}">{{ form.email.label }}</label>
                {{ form.email }} <span class="pull-right error">{{ form.email.errors.0 }}</span>
            </div>

        <button type="submit" id="btn" class="btn btn-primary pull-right">提交</button>
        </fieldset>
    </form>
{% endblock content %}

publishs.html:

{% extends 'base.html' %}

{% block content %}
    <h3>查看出版社</h3>
    <a class="btn btn-primary" href="/addpublish/" style="margin-bottom: 10px;">添加出版社</a>

    <table class="table" >
        <thead>
            <tr>
                <th>序号</th>
                <th>出版社名称</th>
                <th>所在城市</th>
                <th>出版社邮箱</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            {% for publish in publish_list %}
                <tr>
                    <td>{{ forloop.counter }}</td>
                    <td><a href="/books/publish/{{ publish.pk }}">{{ publish.name }}</a></td>
                    <td>{{ publish.city }}</td>
                    <td>{{ publish.email}}</td>
                    <td>
                        <a href="/editpublish/{{publish.pk }}" class="btn btn-info" >编辑</a>
                        <a class="btn btn-danger delBtn" index="{{ publish.pk }}" >删除</a>
                    </td>
                   </tr>
            {% endfor %}
        </tbody>
    </table>
        <script>
        $('.delBtn').click(function () {
           nid = $(this).attr('index');
           $.ajax({
               url: "/delpublish/",
               type: "get",
               data: {nid: nid},
               success: function (data) {
                   if(data.flag){
                       location.reload();
                   }else{
                       alert(data.msg);
                   }
               }
           })
        })
    </script>
{% endblock content %}

updatepublish.html:

{% extends 'base.html' %}

{% block content %}
    <h3>编辑出版社</h3>
    <form action="" method="post">
        {% csrf_token %}

        <fieldset >
            <input type="text" name="{{ form.flag.name }}" 
                   id="id_{{ form.flag.name }}" value="update"  hidden>
            <div class="form-group">
                <label for="id_{{ form.name.name }}">{{ form.name.label }}</label>
                <input type="text" class="form-control" 
                       name="{{ form.name.name }}" id="id_{{ form.name.name }}" 
                       value="{{ form.name.value }}" required readonly>
                <span class="pull-right error">{{ form.name.errors.0 }}</span>
            </div>
            <div class="form-group">
                <label for="id_{{ form.city.name }}">{{ form.city.label }}</label>
                {{ form.city }} <span class="pull-right error">{{ form.city.errors.0 }}</span>
            </div>
            <div class="form-group">
                <label for="id_{{ form.email.name }}">{{ form.email.label }}</label>
                {{ form.email }} <span class="pull-right error">{{ form.email.errors.0 }}</span>
            </div>
            <button type="submit" class="btn btn-primary pull-right">提交</button>
        </fieldset>
    </form>
{% endblock content %}

效果展示

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

以上代码是路飞学城模块作业,还有需要完善的地方,敬请批评指正。学python,找路飞!更多精彩等着您!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个比较大的项目,需要设计数据库、界面和逻辑等多个方面。以下是一个简单的图书馆管理系统的示例代码,供参考: ```c #include <stdio.h> #include <string.h> // 图书信息结构体 typedef struct { char id[10]; // 图书编号 char name[50]; // 图书名称 char author[50]; // 作者 int count; // 数量 } Book; // 学生信息结构体 typedef struct { char id[10]; // 学号 char name[50]; // 姓名 int borrow_count; // 借阅数量 Book books[5]; // 借阅的图书 } Student; // 学生列表 Student students[100]; int student_num = 0; // 图书列表 Book books[100]; int book_num = 0; // 初始化图书列表 void init_books() { books[0] = (Book){"001", "C语言程序设计", "谭浩强", 10}; books[1] = (Book){"002", "Java编程思想", "Bruce Eckel", 5}; book_num = 2; } // 初始化学生列表 void init_students() { student_num = 0; } // 根据图书编号查找图书 Book* find_book(char* id) { for (int i = 0; i < book_num; i++) { if (strcmp(books[i].id, id) == 0) { return &books[i]; } } return NULL; } // 根据学生学号查找学生 Student* find_student(char* id) { for (int i = 0; i < student_num; i++) { if (strcmp(students[i].id, id) == 0) { return &students[i]; } } return NULL; } // 借书 void borrow_book(char* student_id, char* book_id) { Student* student = find_student(student_id); if (student == NULL) { printf("学生不存在!\n"); return; } Book* book = find_book(book_id); if (book == NULL) { printf("图书不存在!\n"); return; } if (book->count <= 0) { printf("图书已被借完!\n"); return; } if (student->borrow_count >= 5) { printf("借阅数量已达上限!\n"); return; } student->books[student->borrow_count] = *book; student->borrow_count++; book->count--; printf("借书成功!\n"); } // 还书 void return_book(char* student_id, char* book_id) { Student* student = find_student(student_id); if (student == NULL) { printf("学生不存在!\n"); return; } Book* book = find_book(book_id); if (book == NULL) { printf("图书不存在!\n"); return; } int index = -1; for (int i = 0; i < student->borrow_count; i++) { if (strcmp(student->books[i].id, book_id) == 0) { index = i; break; } } if (index == -1) { printf("学生未借阅该图书!\n"); return; } for (int i = index; i < student->borrow_count - 1; i++) { student->books[i] = student->books[i + 1]; } student->borrow_count--; book->count++; printf("还书成功!\n"); } // 显示学生信息 void show_student(char* student_id) { Student* student = find_student(student_id); if (student == NULL) { printf("学生不存在!\n"); return; } printf("学号:%s\n", student->id); printf("姓名:%s\n", student->name); printf("借阅数量:%d\n", student->borrow_count); printf("借阅的图书:\n"); for (int i = 0; i < student->borrow_count; i++) { printf(" %s 《%s》 %s\n", student->books[i].id, student->books[i].name, student->books[i].author); } } // 显示图书信息 void show_book(char* book_id) { Book* book = find_book(book_id); if (book == NULL) { printf("图书不存在!\n"); return; } printf("编号:%s\n", book->id); printf("名称:%s\n", book->name); printf("作者:%s\n", book->author); printf("数量:%d\n", book->count); } // 显示所有图书 void show_all_books() { printf("所有图书:\n"); for (int i = 0; i < book_num; i++) { printf(" %s 《%s》 %s 数量:%d\n", books[i].id, books[i].name, books[i].author, books[i].count); } } // 显示所有学生 void show_all_students() { printf("所有学生:\n"); for (int i = 0; i < student_num; i++) { printf(" %s %s 借阅数量:%d\n", students[i].id, students[i].name, students[i].borrow_count); } } int main() { init_books(); init_students(); while (1) { printf("\n请选择操作:\n"); printf("1. 借书\n"); printf("2. 还书\n"); printf("3. 显示学生信息\n"); printf("4. 显示图书信息\n"); printf("5. 显示所有图书\n"); printf("6. 显示所有学生\n"); printf("7. 退出\n"); int choice = 0; scanf("%d", &choice); if (choice == 1) { printf("请输入学生学号和图书编号:\n"); char student_id[10], book_id[10]; scanf("%s %s", student_id, book_id); borrow_book(student_id, book_id); } else if (choice == 2) { printf("请输入学生学号和图书编号:\n"); char student_id[10], book_id[10]; scanf("%s %s", student_id, book_id); return_book(student_id, book_id); } else if (choice == 3) { printf("请输入学生学号:\n"); char student_id[10]; scanf("%s", student_id); show_student(student_id); } else if (choice == 4) { printf("请输入图书编号:\n"); char book_id[10]; scanf("%s", book_id); show_book(book_id); } else if (choice == 5) { show_all_books(); } else if (choice == 6) { show_all_students(); } else if (choice == 7) { break; } else { printf("无效的操作!\n"); } } return 0; } ``` 该示例代码包含了借书、还书、显示学生信息、显示图书信息、显示所有图书和显示所有学生等功能。需要注意的是,该代码仅为示例,实际的图书馆管理系统需要根据具体需求进行设计和开发。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值