概述
图书管理项目
版权声明:本博客来自路飞学城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属性
作用: 在表单提交时不对表单中输入的值进行验证;
语法:
特点:
- HTML5之后的新属性;
- 使用于表单以及标签的以下类型:
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">×</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,找路飞!更多精彩等着您!
最后
以上就是明理花卷为你收集整理的1. 图书管理项目图书管理项目的全部内容,希望文章能够帮你解决1. 图书管理项目图书管理项目所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复