django小练习—小型图书管理系统
- 功能分析:
- 使用django内建的用户系统实现支持用户的注册和登录;
- 登录之后能显示数据库中的图书信息
- 支持分页功能
- 能够对已有的图书信息进行修改和删除
- 能够添加新的图书信息
- 能够将当前页的图书信息导出
- 开发
-
铺垫:
- 创建项目:cd到指定的文件夹,
django-admin startproject bookManageSystem
- 创建应用:cd到项目文件夹,然后
python manage.py startapp user
和python manage.py startapp bookstore
- 创建数据库:
mysql -uroot -p
进入mysql,然后create database bookManageSystem default charset utf8
- settings基本配置:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'user', 'bookstore' ] DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'bookManageSystem', 'USER': 'root', 'PASSWORD': '你的数据库密码', 'HOST': '127.0.0.1', 'PORT': '3306' } } LANGUAGE_CODE = 'zh-Hans' TIME_ZONE = 'Asia/Shanghai'
- 创建项目:cd到指定的文件夹,
-
用户app:
-
铺垫:
-
在user文件夹下手动创建templates文件夹,下面再创建一个user文件夹,用来存放html模板文件。
-
配置分布式路由: 在主路由文件中添加
path('user/',include('user.urls')),
,在应用文件夹user下面手动创建一个urls.py文件,结构和主路由文件一致。 -
views.py文件中导入:
from django.contrib.auth import authenticate,login,logout from django.contrib.auth.models import User from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render
urls.py文件中:
from django.urls import path from . import views
-
-
网站首页(相当简约)
- 路由: 在应用文件夹user的urls.py中添加
path('index',views.index_view),
- 视图函数:render了一个非常简单的页面。
def index_view(request): return render(request,'user/index.html')
- 模板文件:极致的简约,没有任何修饰(hhh)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>图书管理系统</h1> <p> <a href="/user/register">注册</a> <a href="/user/login">登录</a> </p> </body> </html>
- 路由: 在应用文件夹user的urls.py中添加
-
注册功能:
-
路由:在user下面的urls文件中添加
path('register', views.register_view),
-
视图函数:
def register_view(request): #注册 #分get和post进行处理:点击注册时,是get请求,负责render一个页面,在注册界面提交信息时,是post请求,负责处理数据 if request.method == 'GET': return render(request,'user/register.html') elif request.method == 'POST': username = request.POST['username'] password_1 = request.POST['password_1'] password_2 = request.POST['password_2'] if password_1 != password_2: return HttpResponse('两次密码输入不一致') #TODO 查询用户名是否已经注册 old_user = User.objects.filter(username=username) if old_user: return HttpResponse('用户名已注册') #务必使用create_user创建用户 user = User.objects.create_user(username=username,password=password_1) #如果需要注册后免登录 # login(request,user) # return HttpResponseRedirect('/index') return HttpResponseRedirect('/user/index')
-
模板文件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册</title> </head> <body> <form action="/user/register" method="post"> {% csrf_token %} <p> 用户名:<input type="text" name="username"> </p> <p> 密码:<input type="text" name="password_1"> </p> <p> 确认密码:<input type="text" name="password_2"> </p> <p> <input type="submit" value="注册"> </p> </form> </body> </html>
-
-
登录功能:
-
路由:
path('login', views.login_view),
-
视图函数:
def login_view(request): #登录 if request.method == 'GET': return render(request,'user/login.html') elif request.method == 'POST': username = request.POST['username'] password = request.POST['password'] #校验用户信息 user = authenticate(username=username,password=password) if not user: return HttpResponse('用户名或密码错误') else: #校验成功 #记录会话状态 login(request,user) return HttpResponseRedirect('/bookstore/all_book')
-
模板文件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="/user/login" method="post"> {% csrf_token %} <p> 用户名: <input type="text" name="username"> </p> <p> 密码: <input type="password" name="password"> </p> <p> <input type="submit" value="登录"> </p> </form> </body> </html>
-
-
退出功能:
- 路由:
path('logout',views.logout_view)
- 视图函数:
def logout_view(request): logout(request) return HttpResponseRedirect('/user/index')
- 路由:
-
-
bookstore应用:
-
铺垫:
-
在user文件夹下手动创建templates文件夹,下面再创建一个user文件夹,用来存放html模板文件。
-
配置分布式路由: 在主路由文件中添加
path('bookstore/',include('bookstore.urls'))
,在应用文件夹user下面手动创建一个urls.py文件,结构和主路由文件一致。 -
views文件中引入:
from django.shortcuts import render from django.core.paginator import Paginator from django.http import HttpResponse, HttpResponseRedirect import csv #引入模型类 from .models import Book from django.contrib.auth.decorators import login_required
urls文件中引入:
from django.urls import path from . import views
-
-
创建模型类:
-
编写模型类: 在boostore这个应用文件夹下的models.py 文件夹中编写:
from django.db import models # Create your models here. class Book(models.Model): #根据ORM的映射规则,一个类实际上就是一张表(表名=应用名_类名),类中的每个属性就对应着表中的一个字段 title = models.CharField('书名',max_length=50,default='',unique=True) pub = models.CharField('出版社',max_length=100,default='') price = models.DecimalField('价格',max_digits=7,decimal_places=2) #任何关于表结构的修改,务必在对应模型类上修改 market_price = models.DecimalField('零售价',max_digits=7,decimal_places=2,default=0.0) is_active = models.BooleanField('是否活跃',default=True) # 改变当前模型类对应的表的名字 class Meta: db_table = 'book' def __str__(self): return '%s-%s-%s-%s'%(self.title,self.price,self.pub,self.market_price)
-
与数据库同步:
python manage.py makemigrations
和python manage.py migrate
-
创建数据:cd在项目文件夹下,使用
python manage.py shell
打开django shell,先导入模型类:from bookstore.models import Book
,然后创建数据:Book.objects.create(title='Python',pub='清华大学出版社',price=30,market_price=35,is_active=True)
-
-
显示图书:
- 路由:在bookstore文件夹中的urls文件中添加
path('all_book',views.all_book),
- 视图函数:
@login_required def all_book(request): #在视图中要做的就是将书全查出来,然后发送给视图进行渲染 #在这里用filter(is_active=True)进行筛选,就能巧妙实现删除了数据时候,该数据不再被显示的要求 all_book = Book.objects.filter(is_active=True) # /bookstore/all_book?page=4 # 拿到当前的页码,没有的话就默认是1 page_num = request.GET.get('page', 1) # 初始化paginator # 第一个参数是总的数据,第二个参数是每页显示的条数 paginator = Paginator(all_book, 2) # 初始化具体页码对应的page对象,管具体的每一页 current_page = paginator.page(int(page_num)) return render(request,'bookstore/all_book.html',locals()) #locals可以将函数内的局部变量以字典的形式传进render中。
- 模板文件:在bookstore/template下的bookstore文件夹中写:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>所有书籍</title> </head> <body> <p> <a href="/user/logout">退出</a> </p> <P> <a href="/bookstore/output_csv?page={{ current_page.number }}">导出本页数据</a> </P> <p> <a href="/bookstore/add_book">添加数据</a> </p> <table border="'1"> <tr> <th>id</th> <th>title</th> <th>pub</th> <th>price</th> <th>market_price</th> <th>op</th> </tr> {% for book in current_page %} <tr> <td>{{ book.id }}</td> <td>{{ book.title }}</td> <td>{{ book.pub }}</td> <td>{{ book.price }}</td> <td>{{ book.market_price }}</td> <td> <a href="/bookstore/update_book/{{ book.id }}">更新</a> {#点击a标签,发个get,拿到页面;然后修改数据,点击更新,就要给后台提交一些数据,这里就是POST#} <a href="/bookstore/delete_book?book_id={{ book.id }}">删除</a> </td> </tr> {% endfor %} </table> {% if current_page.has_previous %} <a href="/bookstore/all_book?page={{ current_page.previous_page_number }}">上一页</a> {% else %} 上一页 {% endif %} {#当前页显示页码,非当前页显示a标签#} {% for page_num in paginator.page_range %} {% if page_num == current_page.number %} {{ page_num }} {% else %} <a href="/bookstore/all_book?page={{ page_num }}">{{ page_num }}</a> {% endif %} {% endfor %} {% if current_page.has_next %} <a href="/bookstore/all_book?page={{ current_page.next_page_number }}">下一页</a> {% else %} 下一页 {% endif %} </body> </html> {#T层通过视图作为桥梁,拿到m层的数据进行渲染,使用if for等关键字的时候是{% %},记得在最后end,使用视图传过来的参数时{{ }}#}
- 路由:在bookstore文件夹中的urls文件中添加
-
修改图书:
-
路由:
path('update_book/<int:book_id>',views.update_book),
使用path转换器把要修改的图书的id传给视图函数。 -
视图函数:
@login_required def update_book(request,book_id): # 书是通过path转换器过来,也就是路由中会带着,比如:bookstore/update_book/1,结尾就是id #先获取指定书的信息(get的时候得查书的信息,因为得给render的页面传过去用于显示,post的时候也得查,因为修改单个数据是一查二改三保存) try: book = Book.objects.get(id = book_id,is_active=True) except Exception as e: print('--update book error is %s'%(e)) return HttpResponse('--The book is not existed') # get拿页面,post处理数据 if request.method == 'GET': return render(request,'bookstore/update_book.html',locals()) #得把书的信息传进去,因为得显示。 elif request.method == 'POST': price = request.POST['price'] market_price = request.POST['market_price'] #改 book.price = price book.market_price = market_price #保存 book.save() #跳转回all_book页面,形成一个闭环 return HttpResponseRedirect('/bookstore/all_book')
-
模板文件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>更改书籍</title> </head> <body> {#action是负责表单的这个数据具体要提交到哪里去,还提交给这个地址#} <form action="/bookstore/update_book/{{ book_id }}" method="post"> {% csrf_token %} <p> title <input type="text" value="{{ book.title }}" disabled="disabled"> </p> <p> pub <input type="text" value="{{ book.pub }}" disabled="disabled"> </p> <p> price <input type="text" name="price" value="{{ book.price }}"> </p> <p> market_price <input type="text" name="market_price" value="{{ book.market_price }}"> </p> <p> <input type="submit" value="更新"> <a href="/bookstore/all_book">返回</a> </p> </form> </body> </html>
-
-
删除图书功能:
- 路由:
path('delete_book',views.delete_book),
- 视图函数:
@login_required def delete_book(request): #通过获取查询字符串book_id拿到要删除的book的id,将其is_active改为False,然后302跳转至all_book. # 先做视图,再做页面,再绑定路由 #拿到要删除的书的id book_id = request.GET.get('book_id') if not book_id: return HttpResponse('---请求异常') # 查询要删的书的信息 try: book = Book.objects.get(id=book_id,is_active=True) except Exception as e: print('---delete book get error %s'%(e)) return HttpResponse('--The book id is error') #将其is_active改成False book.is_active = False book.save() #因为伪删除相当于修改,所以一查二改三保存,要记得保存 #302跳转至all_book return HttpResponseRedirect('/bookstore/all_book')
- 路由:
-
增加图书功能:
- 路由:
path('add_book',views.add_book)
- 视图函数:
@login_required def add_book(request): if request.method == 'GET': return render(request,'bookstore/add_book.html') elif request.method == 'POST': title = request.POST['title'] pub = request.POST['pub'] price = request.POST['price'] market_price = request.POST['market_price'] Book.objects.create(title=title,pub=pub,price=price,market_price=market_price,is_active=True) return HttpResponseRedirect('/bookstore/all_book')
- 模板文件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加书籍</title> </head> <body> <form action="/bookstore/add_book" method="post"> {% csrf_token %} <p> 标题:<input type="text" name="title"> </p> <p> 出版社:<input type="text" name="pub"> </p> <p> 定价:<input type="text" name="price"> </p> <p> 零售价:<input type="text" name="market_price"> </p> <input type="submit" value="保存"> </form> </body> </html>
- 路由:
-
导出数据功能:
- 路由:
path('output_csv',views.output_csv),
- 视图函数:
@login_required def output_csv(request): # 在这里用filter(is_active=True)进行筛选,就能巧妙实现删除了数据时候,该数据不再被显示的要求 all_book = Book.objects.filter(is_active=True) # 拿到当前的页码,没有的话就默认是1 page_num = request.GET.get('page', 1) # 初始化paginator # 第一个参数是总的数据,第二个参数是每页显示的条数 paginator = Paginator(all_book, 2) # 初始化具体页码对应的page对象,管具体的每一页 current_page = paginator.page(int(page_num)) response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment;filename="myBookOnPage%s"'%(page_num) writer = csv.writer(response) writer.writerow(['id','title','pub','price','market_price']) for book in current_page: writer.writerow([book.id,book.title,book.pub,book.price,book.market_price]) return response
- 路由:
-
-