图书管理系统
出版社的管理
源码位置:https://gitee.com/machangwei-8/learning_materials/tree/master/%E9%A1%B9%E7%9B%AE/bookmanager
1.设计URL
urlpatterns = [ url(r'^publisher_list/', views.publisher_list), ]
1.1环境准备
创建项目bookmanager。app名字修改成对应的。
settings
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #项目目录
DEBUG = True #debug模式,测试的时候使用true
创建数据库: create database bookmanager;
#settings.py设置数据库配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'bookmanager', 'HOST':'127.0.0.1', 'PORT':3306, 'USER':'root', 'PASSWORD':'123', } } #init中使用pymysql import pymysql pymysql.install_as_MySQLdb() #models.py中创建表 from django.db import models # Create your models here. class Publisher(models.Model): pid=models.AutoField(primary_key=True) #修改主键名字,使用自己创建的主键 name=models.CharField(max_length=32)
刚刚配置将USER配置成了USR,导致执行makegrations报错:
迁移文件生成:
迁移文件中生成表名,字段名等:
再添加一个字段。它提示添加个默认值或者退出:
退出后,添加默认值再操作,添加字段,出错
最终执行的迁移文件是这个内容,我再这里面添加一下这个字段:
然后再models里面添加这个,相当于一开始就在models里加了这个字段然后执行了makegragions。这样表里的内容和迁移文件对应上了。
似乎执行改变了一次就生成一个文件:
刚才出问题了,将这些删掉,再执行试试。
生成数据库表:
添加并上传:
展示出版社,设置url
from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^publisher_list/', views.publisher_list), ]
def publisher_list(request): #显示一个含出版社信息的页面 return render(request,'publisher_list.html')
<html lang="en"> <head> <meta charset="UTF-8"> <title>魔降风云变</title> </head> <body> <h1>出版社列表</h1> </body> </html>
这样就拿到页面了,但是还要展示出数据库中的数据。
渲染数据库的数据,将数据以表格形式显示出来:
from app01 import models #展示出版社 def publisher_list(request): # 从数据库中查询到出版社的信息 all_publishers = models.Publisher.objects.all() return render(request, 'publisher_list.html', {'all_publishers': all_publishers}) #publisher_list.html中使用的是字典中的键,键代替这个所有的对象
#render方法也可以传{键值对},在html模板文件中使用键对传入的值进行操作。
HTML页面我们可以写死了,但是我们需要的是根据数据库的内容展示出来,用模板渲染出来。
在函数中传进来的是对象列表。每一个都是对象,是表中一行数据:
每行数据库的内容显示在网页,使用pk,那么无论主键是啥名字,都可以用pk代替
给表格加个边框:
添加序号列:forloop.counter,从字典中每循环一次就计数一次,这样就让每行内容有个序号。而我们只需要将模板变量传进去,用固定语法操作。
2.写函数
from django.shortcuts import render
from app01 import models
# 展示出版社
def publisher_list(request):
# 从数据库中查询到出版社的信息
all_publishers = models.Publisher.objects.all()
# 返回一个包含出版社信息的页面
return render(request, 'publisher_list.html', {'all_publishers': all_publishers})
过程放在1里面了
3.写模板
<table border="1"> <thead> <tr> <th>序号</th> <th>ID</th> <th>名称</th> </tr> </thead> <tbody> {% for publisher in all_publishers %} <tr> <td>{{ forloop.counter }}</td> <td>{{ publisher.pk }}</td> <td>{{ publisher.name }}</td> </tr> {% endfor %} </tbody> </table>
过程放在1里面了
3.1、新增
obj = models.Publisher.objects.create(name=publisher_name)
新增功能的效果:出版社列表页面中一个按钮,点击跳转页面。这个页面中输入出版社的相关信息(出版社名称),有个按钮提交信息,后台要把输入的信息插入到数据库当中。插入之后浏览器需要再跳转到出版社列表页面。
这个按钮直接是个a标签就可以。也就是我在出版社列表中有个a标签,点击跳转页面add_publisher,里面要有form表单。
添加url,点击views跳转进入写函数:
写出添加数据库记录的页面:这样在框中输入数据,后台将数据获取到并写入数据库中
2处理的原理如下1处:
获取出版社的名称:是获取这个提交请求的name,获得post请求键值对中的值
# 使用ORM将数据插入到数据库中,将获取到的要添加的出版社名称创建一个字段是输入名称的数据表记录。pid不用写,因为是自增
1处的请求2处没有获取到,然后后面的name=None,这样前端3处跳转报错(1048, "Column 'name' cannot be null")
上面的报错的原因是1处get的名字与2处不一致,所有没有获取到post提交的值,那么1处的变量就是None。解决方法,将它们改为一致,我将模板文件的2处改为publisher_name。
下面我做的操作是在3处写入‘小马出版社’,点击提交之后,5处不变,重新进入模板文件的页面。可看函数,函数中无论GET或者POST请求都是返回的同样的页面。然后再看
4处数据库,函数拿到了我们提交的数据并在数据库中已经创建了这条记录。
网页提交‘小马过河2出版社’,创建一条数据,返回的是这个类的对象,就是那个数据表的一条记录。可以通过创建一行数据时的返回值取出这行数据的各个字段的内容。
我们需要的是当我们从添加出版社的页面提交添加之后,那么页面跳转查看所有出版社的页面,这样能看到新添加的出版社。那么使用重定向:
看下图,在add_publisher页面中添加进一行数据后,直接跳转到publisher_list页面并显示新添加的一条记录‘小熊出版社’。
添加数据页面可以通过添加后进入展示所有数据的页面,但是展示表中所有记录的页面还没有进入添加数据的页面,那么在展示表中所有记录的页面添加一个a标签让它跳转到新增数据的页面即可,如下图
此处的a标签地址应写成路径,在url路由中能找到,这里实现了点击新增进入添加数据的页面,添加数据又能返回这里的展示页面。新增a标签的跳转实际上是点击之后又发送了一次get请求。
我现在添加一个重复的出版社名称,它会添加记录到数据库中,
出版社名字不应该重复的,下面就把这个字段设置成唯一键,然后执行数据迁移的那两条命令结果报错,原因是数据库中已经有重复的数据了‘小熊出版社’。
django.db.utils.IntegrityError: (1062, "Duplicate entry '小熊出版社' for key 'app01_publisher_name_36f04192_uniq'")
pycharm中将这条数据删除掉并提交
然后执行那两条数据迁移的命令,成功修改这个字段为唯一键:
既然这个字段已经是唯一键了,那么我在页面添加数据库中已经有的这个字段内容,那么报错如下:
(1062, "Duplicate entry '小熊出版社' for key 'app01_publisher_name_36f04192_uniq'")
数据库有约束,我应该在数据库插入之前,就进行判断有没有这个数据,而不是直接就往数据里面插入数据
这里添加了判断条件之后还是报错页面,原因是,我们应该在数据插入之前就判断是否存在相同的字段内容
1处插入数据前判断是否有相同的,有就传进error信息到,然后在返回的原网页后面渲染上错误提示信息(2处)。这样同名的数据插入网页会提示已存在的提示
如果我不写内容直接点击提交
出现了一条空数据,这显然不是我们想要的,所有我们需要在这个的函数上做判断,如果获取的值为空,那么返回这个页面,并在输入框后面渲染不能输入为空的错误提示
这里显示为已存在,刚刚我在里面插入了一个空的数据,现在将它删除掉
现在显示的是不能为空了,一开始error不是这个信息,到了不能为空这里,又被覆盖了一次。
看下面的代码,有三个render
我想要将三个render简化成一个:
1)我可以最后返回一个render,其它的只是赋值error变量。然后render的页面里渲染不同的error值。如果没有错误即默认error是空字符串就可以。
2)然后判断error信息是否非假(即非空),非假才执行创建数据记录的操作并重定向页面到查看所有出版社数据信息的页面
当设置了一个error是空字符串的时候,正常返回的页面是没有显示内容的。这样虽然函数的结构修改了,但是功能没有改变
def add_publisher(request): error='' #返回一个包含from表单的出版社页面 if request.method=='POST': publish_name=request.POST.get('publisher_name') # 判断出版社名称是否有重复的 if models.Publisher.objects.filter(name=publish_name): error='出版社名称已存在' #判断输入值是否为空 if not publish_name: error="输入不能为空" if not error: obj=models.Publisher.objects.create(name=publish_name) #跳转到展示出版社的页面 return redirect('/publisher_list/') return render(request,'add_publisher.html',{'error':error})
但是还是有个问题,就是取出来的数据id是乱序的
这是因为publiser_list这一页的get请求出来的数据没有排序,将从数据库中取出来的所有数据给个排序就解决了
3.2、删除
obj_list = models.Publisher.objects.filter(pk=pk) obj_list.delete() obj = models.Publisher.objects.get(pk=pk) obj.delete()
想要删除某一条记录,应该做什么样的操作呢?那么在名称列后面有个按钮或者a标签,点击它就删除掉这条记录,这样是可以的吧。
那么在展示页中后面添加一列,使用a标签
现在应该是我点击一个删除,然后发送一个请求 ,并在后端数据库将这条数据删除,删除之后再跳转到这个页面
先设计删除url,创建删除函数,删除函数逻辑如下;
如果我的函数中有一处存在错误,可能所有函数对应的网页都不能访问:检查元素加载失败
给删除a标签设置一个地址,已经实现点击删除访问这个路径了
但是点击删除的是哪条记录,我需要获取到才能在后台将这条记录删除掉。于是将它的跳转路径拼接一个这条记录的主键id
删除代码:
点击删除:
这时候我是点击删除按钮删的,我也可能使用浏览器访问地址进行删除
id=15的已删除:
如果自己拼接在浏览器地址栏访问请求不存在的记录:报错:Publisher matching query does not exist.
那么我应该对这种情况做个判断,因为是get获取数据的,get不到就会显示错误,那么将get改为过滤
过滤出来可能多条记录,也可以对queryset 这个对象列表里的对象批量删除
def del_publisher(request): # 获取要删除的数据 pk = request.GET.get('id') obj_list = models.Publisher.objects.filter(pk=pk) if not obj_list: # 没有要删除的数据 return HttpResponse('要删除的数据不存在') # 删除该数据 # obj.delete() obj_list.delete() # 跳转到展示页面 return redirect('/publisher_list/')
3.3、编辑(修改数据)
obj.name = publisher_name
obj.save() # 保存数据到数据库中
想要进行编辑的话应该是什么样的过程。点击编辑a标签之后可以跳转到编辑页面,这个编辑页面包含了你要编辑的那条数据,直接在这上面进行修改,修改完了之后提交,后台修改了之后跳转回来到所有出版社展示页
创建url,创建函数,将编辑标签和删除标签写在同一列
点击其中一个页面往这个地址发送请求了,并且根据id获取到了是哪条数据需要编辑
应该返回一个页面,这个页面中包含点击编辑的那条原始数据。编辑页面和添加页面差不多。
编辑的页面这样的话么有带上id=2这条数据的name
因为编辑按钮a标签跳转的时候带着对应数据的id,那么由此获得这条数据并将它的内容name默认填入到编辑的input标签里
通过点击编辑按钮传到请求里的id查出满足条件的数据库记录,并传到html模板文件中进行调用其中的值进行渲染。使得编辑框里显示要编辑的原数据内容
如图,点击这个id的就显示出来要编辑的原数据。编辑的展示这样就没有问题
下面点击提交,要进行以下的逻辑操作:
要修改id=2的数据:由于发送POST请求之前,也要获取到obj对象,所有把POST请求处理放在后面
现在需要将可能遇到的问题写上。传进错误信息进行渲染
写这些功能先写正常的情况下,然后再考虑会出现的问题。
4、项目目录结构
from django.shortcuts import render, redirect, HttpResponse from app01 import models # 展示出版社 def publisher_list(request): # 从数据库中查询到出版社的信息 all_publishers = models.Publisher.objects.all().order_by('pk') # 返回一个包含出版社信息的页面 return render(request, 'publisher_list.html', {'all_publishers': all_publishers}) # 新增出版社 def add_publisher(request): # 对请求方式进行判断 if request.method == 'POST': # 处理POST请求 # 获取到出版社的名称 publisher_name = request.POST.get('publisher_name') # 判断出版社名称是否有重复的 if models.Publisher.objects.filter(name=publisher_name): return render(request, 'add_publisher.html', {'error': '出版社名称已存在'}) # 判断输入的值是否为空 if not publisher_name: return render(request, 'add_publisher.html', {'error': '不能输入为空'}) # 使用ORM将数据插入到数据库中 obj = models.Publisher.objects.create(name=publisher_name) # 跳转到展示出版社的页面 return redirect('/publisher_list/') # 返回一个包含form表单的页面 return render(request, 'add_publisher.html') # 新增出版社 def add_publisher(request): error = '' # 对请求方式进行判断 if request.method == 'POST': # 处理POST请求 # 获取到出版社的名称 publisher_name = request.POST.get('publisher_name') # 判断出版社名称是否有重复的 if models.Publisher.objects.filter(name=publisher_name): error = '出版社名称已存在' # 判断输入的值是否为空 if not publisher_name: error = '不能输入为空' if not error: # 使用ORM将数据插入到数据库中 obj = models.Publisher.objects.create(name=publisher_name) # 跳转到展示出版社的页面 return redirect('/publisher_list/') # 返回一个包含form表单的页面 return render(request, 'add_publisher.html', {'error': error}) # 删除出版社 def del_publisher(request): # 获取要删除的数据 pk = request.GET.get('id') obj_list = models.Publisher.objects.filter(pk=pk) if not obj_list: # 没有要删除的数据 return HttpResponse('要删除的数据不存在') # 删除该数据 # obj.delete() obj_list.delete() # 跳转到展示页面 return redirect('/publisher_list/') # 编辑出版社 def edit_publisher(request): error = '' # 查找要编辑的数据 pk = request.GET.get('id') # url上携带的参数 不是GET请求提交参数 obj_list = models.Publisher.objects.filter(pk=pk) if not obj_list: return HttpResponse('要编辑的数据不存在') obj = obj_list[0] if request.method == 'POST': # 处理POST请求 # 获取新提交的出版的名称 publisher_name = request.POST.get('publisher_name') if models.Publisher.objects.filter(name=publisher_name): # 新修改的名称已存在 error = '新修改的名称已存在' if obj.name == publisher_name: error = '名称未修改' if not publisher_name: error = '名称不能为空' if not error: # 修改数据 obj.name = publisher_name obj.save() # 保存数据到数据库中 # 跳转到出版社的展示页面 return redirect('/publisher_list/') # 返回一个包含原始数据的页面 return render(request, 'edit_publisher.html', {'obj': obj,'error':error})