6.1 - 图书增删改查页面
Posted Alice的小屋
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了6.1 - 图书增删改查页面相关的知识,希望对你有一定的参考价值。
一、需求及注意事项
需求:
1.列出图书列表、出版社列表、作者列表
2.点击作者,会列出其出版的图书列表
3.点击出版社,会列出其下的图书列表
4.可以创建、修改、删除 图书、作者、出版社
完成及注意事项:
1.注册
/register/
2.登录
/login/
3.注销
/logout/
4.图书列表
/book_list/
/add_book/
/update_book/303/
/del_book/
5.出版社列表
/publisher_list/
/add_publisher/
/update_publisher/105/
/update_book/306/105/publisher/ # 通过出版社,修改该书,之后返回出版社页面
/del_publisher/
/book_list/105/publisher/ # 通过出版社,查看该出版社得图书列表
/add_book/105/publisher/ # 通过出版社,增加该出版社得某本书
/del_book/105/publisher/ # 通过出版社,删除该出版社得某本书
6.作者列表
/author_list/
/add_author/
/update_author/67/
/update_book/307/67/author/ # 通过作者,修改该书,之后返回作者页面
/del_author/
/book_list/67/author/ # 通过作者,查看该作者得图书列表
/add_book/67/author/ # 通过作者,增加该作者得某本书
/del_book/67/author/ # 通过作者,删除该作者得某本书
7.验证是否登录(session),跳转到之前访问得页面:
采用中间件 mymiddleware.py
def process_request(self,request):pass
白名单 黑名单
8.验证是否登录,装饰器!
def check_login(func):pass
最后采用中间件,
否则每一个函数都有要加装饰器。(@check_login)
9.批量插入测试数据
脚本 myscript.py
models.Book.objects.bulk_create(ret1)
10.自定义分页器
mypage.py
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
封装
上一页,下一页,首页,尾页
11.模板继承
base.html
{% extends \'base.html\' %}
{{ block.super }} 可以引用基类模板的内容
12.静态文件
jquery-3.2.1.js
bootstrap-3.3.7
ajax发送post请求时
init_ajax.js
取 csrf token 的值发送
删除,页面得模态框 插件
sweetalert
13.settings配置文件
中间件
mysql
终端打印sql语句
logging
登录注册,会有日志记录
14.自定义过滤器和标签
templatetags/my_tag_filter.py
{% load my_tag_filter %}
{{ path|update_path:book.id}}
15.urls
分发,名称空间,有名分组,反向解析
re_path(r\'^update_book/(?P<id>\\d+)\',views.update_book),
正则反向解析,需要赋值
return redirect(reverse(\'book:book_publisher\',args=(type_id,)))
16.FBV CBV
FBV(function base views) 视图里使用函数处理请求
CBV(class base views) 视图里使用类处理请求
path(\'login/\',views.LoginView.as_view(), name = \'login\')
class LoginView(View):pass
给CBV加装饰器:
@method_decorator(check_login)
def dispatch(self, request, *args, **kwargs):pass
17.表单forms组件
myforms.py
为forms组件赋初值
https://docs.djangoproject.com/en/2.0/ref/forms/api/
publisher_form.initial = {\'name\':publisher.name}
表单控件
给每一个key添加样式
def __init__(self,*args,**kwargs):pass
select下拉框:新增加的出版社,想不重启,立刻就显示:
def __init__(self,*args,**kwargs):pass
新增加书时,选择新增加的出版社
不能立刻通过校验is_valid 需要等一会,就通过了
18.DateTimeField
注意点:
TIME_ZONE = \'Asia/Shanghai\'
USE_TZ = False
https://blog.csdn.net/win_turn/article/details/53000770
auto_now
auto_now_add
timezone
http://www.nanerbang.com/article/5488/
django:DateTimeField如何自动设置为当前时间并且能被修改:
DateTimeField.auto_now 如果为true 无法赋值,每次更新为最新时间;
DateTimeField.auto_now_add 如果为true 无法赋值,第一次创建时间
from django.db import models
import django.utils.timezone as timezone
class Doc(models.Model):
add_date = models.DateTimeField(\'保存日期\',default = timezone.now)
mod_date = models.DateTimeField(\'最后修改日期\', auto_now = True)
19.ORM表关系
一对一,(author authordetail)
删除author时,应该删除authordetail,关联的author就被删除了!
一对多,(book publisher)
删除出版社下面的某本书,拿到书的id,删除这本书;
多对多,(book author)
清除绑定关系,不应该删除书;
启动:
配置sql
python manage.py makemigrations
python manage.py migrate
详情:
需查看代码!!
二、数据库
三、页面效果
四、主要代码
urls.py
from django.urls import path,re_path from book import views urlpatterns = [ # path(\'login/\',views.login, name = \'login\'), # FBV path(\'login/\',views.LoginView.as_view(), name = \'login\'), # CBV # path(\'register/\',views.register, name = \'register\'), path(\'register/\',views.RegisterView.as_view(), name = \'register\'), path(\'exist_user/\',views.exist_user), path(\'logout/\',views.logout), path(\'book_list/\',views.book_list, name = \'book_list\'), # re_path(r\'^book_list/(\\d+)/(publisher)\',views.book_list), re_path(r\'^book_list/(?P<field_id>\\d+)/(?P<field_type>publisher)\',views.book_list), # 有名分组 # re_path(r\'^book_list/(\\d+)/(author)\',views.book_list), re_path(r\'^book_list/(?P<field_id>\\d+)/(?P<field_type>author)\',views.book_list), # 有名分组 path(\'del_book/\',views.del_book, name = \'del_book\'), re_path(r\'^del_book/(\\d+)/(publisher)\',views.del_book), re_path(r\'^del_book/(\\d+)/(author)\',views.del_book), path(\'add_book/\',views.add_book,name=\'add_book\'), re_path(r\'^add_book/(\\d+)/(publisher)\',views.add_book), re_path(r\'^add_book/(\\d+)/(author)\',views.add_book), re_path(r\'^update_book/(?P<book_id>\\d+)/(?P<field_id>\\d+)/(?P<field_type>publisher)\',views.update_book), re_path(r\'^update_book/(?P<book_id>\\d+)/(?P<field_id>\\d+)/(?P<field_type>author)\',views.update_book), re_path(r\'^update_book/(?P<book_id>\\d+)\',views.update_book), # 这个得放到下面 否则会截走 上面两个 因为第一个也是正则 path(\'publisher_list/\',views.publisher_list, name = \'publisher_list\'), path(\'del_publisher/\',views.del_publisher, name = \'del_publisher\'), path(\'add_publisher/\',views.add_publisher, name = \'add_publisher\'), re_path(r\'^update_publisher/(\\d+)\',views.update_publisher), path(\'author_list/\',views.author_list, name = \'author_list\'), path(\'del_author/\',views.del_author, name = \'del_author\'), path(\'add_author/\',views.add_author, name = \'add_author\'), re_path(r\'^update_author/(\\d+)\',views.update_author), ]
models.py
from django.db import models from django.utils import timezone # 出版社类 class Publisher(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32) def __str__(self): return self.name # 书类 class Book(models.Model): id = models.AutoField(primary_key=True) title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_places=2) publish_date = models.DateTimeField(default=timezone.now()) # 书只能关联一个出版社, 外键通常建在多的那一边 publisher = models.ForeignKey(to=\'Publisher\',on_delete=models.CASCADE) def __str__(self): return self.title # 作者类 class Author(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=16) # 多对多, 建在哪边都可以 books = models.ManyToManyField(to=\'Book\',related_name=\'authors\') detail = models.OneToOneField(to=\'AuthorDetail\',null=True,on_delete=models.CASCADE) def __str__(self): return self.name # 作者详情 class AuthorDetail(models.Model): age = models.IntegerField() addr = models.TextField() # 登录注册的用户信息 class UserInfo(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=16) pwd = models.CharField(max_length=32)
myforms.py
# -*- coding:utf-8 -*- from django import forms from django.forms import widgets from django.core.exceptions import ValidationError from book import models widget_input = widgets.TextInput(attrs={\'class\':\'form-control\'}) widg_num = widgets.NumberInput(attrs={\'class\':\'form-control\'}) widget_password = widgets.PasswordInput(attrs={\'class\':\'form-control\'}) class PublisherForm(forms.Form): name = forms.CharField( label=\'出版社名称\', min_length=4, max_length=32, error_messages={ \'required\':\'不能为空\', \'min_length\':\'不能小于4位\', }, widget=widget_input ) def clean_name(self): val = self.cleaned_data.get(\'name\') if not val.isdigit(): return val else: raise ValidationError(\'名称不能是纯数字\') class AuthorForm(forms.Form): name = forms.CharField( label=\'姓名\', min_length=2, max_length=16, error_messages={ \'required\':\'不能为空\', \'min_length\':\'不能小于2位\', }, # widget = widget_input ) age = forms.IntegerField( label=\'年龄\', min_value=1, max_value=150, error_messages={ \'required\':\'不能为空\', \'invalid\':\'格式错误\', \'min_value\':\'不能小于1岁\', \'max_value\':\'不能大于100岁\', }, # widget = widget_input ) addr = forms.CharField( label=\'地址\', min_length=2, max_length=32, error_messages={ \'required\':\'不能为空\', \'min_length\':\'不能小于2位\' }, # widget=widget_input, ) # 给每一个key 添加样式 def __init__(self,*args,**kwargs): super(AuthorForm, self).__init__(*args,**kwargs) for field in iter(self.fields): self.fields[field].widget.attrs.update({ \'class\':\'form-control\' }) def clean_name(self): val = self.cleaned_data.get(\'name\') if not val.isdigit(): return val else: raise ValidationError(\'姓名不能是纯数字\') def clean_addr(self): val = self.cleaned_data.get(\'addr\') if not val.isdigit(): return val else: raise ValidationError(\'地址不能是纯数字\') class BookForm(forms.Form): title = forms.CharField( label=\'书名称\', max_length=32, error_messages={ \'required\':\'不能为空\', }, widget=widget_input ) price = forms.DecimalField( label=\'价格\', max_digits=8, decimal_places=2, error_messages={ \'required\':\'不能为空\', \'max_digits\':\'不能超过8位\', }, widget=widg_num ) # 最后选择了自己去添加 select 。。。。。。 # publisher = forms.ChoiceField( # label=\'出版社\', # # choices=((1,\'篮球\'),(2,\'足球\')), # choices=models.Publisher.objects.all().values_list(\'id\',\'name\'), # initial=1, # widget=widgets.Select(attrs={\'class\':\'form-control\'}) # ) # # # 可以不用重启 刷新就有新的数据 # def __init__(self,*args,**kwargs): # super(BookForm, self).__init__(*args,**kwargs) # self.fields[\'publisher\'].widget.choices = models.Publisher.objects.all().values_list(\'id\',\'name\') def clean_title(self): val = self.cleaned_data.get(\'title\') if not val.isdigit(): return val else: raise ValidationError(\'名称不能为纯数字\') def clean_price(self): val = self.cleaned_data.get(\'price\') if val < 0: raise ValidationError(\'价格不能为负数\') else: return val class LoginForm(forms.Form): username = forms.CharField( label=\'用户名\', min_length=2, max_length=16, error_messages={ \'required\':\'不能为空\', \'min_length\':\'不能小于2位\' }, widget=widget_input ) password = forms.CharField( label=\'密码\', min_length=6, max_length=16, error_messages={ \'required\':\'不能为空\', \'min_length\':\'不能小于6位\' }, widget=widget_password ) def clean_username(self): val = self.cleaned_data.get(\'username\') if not val.isdigit(): return val else: raise ValidationError(\'用户名不能是纯数字\')
views.py
from django.shortcuts import render,HttpResponse,redirect,reverse from django.utils.decorators import method_decorator from django.core import serializers from django.views import View import json import datetime import logging from functools import wraps from book import models from book import myforms from utils.mypage import MyPaginator from utils.hash_pwd import salt_pwd logger = logging.getLogger(__name__) # 生成一个名为collect的实例 collect_logger = logging.getLogger(\'collect\') def check_login(func): \'\'\' 判断用户有没有登录得装饰器 :param func: :return: \'\'\' @wraps(func) def inner(request,*args,**kwargs): # 拿到当前访问网址 url = request.get_full_path() if request.session.get(\'user\'): return func(request,*args,**kwargs) else: return redirect(\'/login/?next={}\'.format(url)) return inner # def login(request): # \'\'\' # 登录 # :param request: # :return: # \'\'\' # login_form = myforms.LoginForm() # return render(request,\'login.html\',{\'login_form\':login_form}) # # # def register(request): # \'\'\' # 注册 # :param request: # :return: # \'\'\' # register_form = myforms.LoginForm() # return render(request,\'register.html\',{\'register_form\':register_form}) class LoginView(View): \'\'\' CBV 登录视图 \'\'\' def get(self,request): login_form = myforms.LoginForm() return render(request, \'login.html\', {\'login_form\': login_form}) def post(self,request): login_form = myforms.LoginForm(request.POST) if login_form.is_valid(): username = login_form.cleaned_data.get(\'username\') password = login_form.cleaned_data.get(\'password\') password = salt_pwd(password, username) if models.UserInfo.objects.filter(name=username,pwd=password): # 设置session request.session[\'user\'] = username # request.session.set_expiry(1800) # 设置session得失效时间 # 登录成功 写log logger.info(\'用户:\'+username+\' 登录成功\') next_url = request.GET.get(\'next\') if next_url: return redirect(next_url) # 跳转到之前访问得页面 else: return redirect(reverse(\'book:book_list\')) else: # 登录失败,写log logger.error(\'用户:\'+username+\' 登录时,用户名或密码错误\') return render(request, \'login.html\', {\'login_form\': login_form,\'error_msg\':\'用户名或密码错误\'}) else: return render(request, \'login.html\', {\'login_form\': login_form}) class RegisterView(View): \'\'\' CBV 注册视图 \'\'\' # @method_decorator(check_login) 给cbv 加装饰器 逻辑上不应该加在这里,但可以验证装饰器加成功了 def dispatch(self, request, *args, **kwargs): return super(RegisterView, self).dispatch(request,*args,**kwargs) def get(self,request): register_form = myforms.LoginForm() return render(request,\'register.html\',{\'register_form\':register_form}) 6.1 - 图书管理系统Java全栈web网页技术:15.书城项目实战四:管理端图书的增删改查(后台)
Java全栈web网页技术:15.书城项目实战四:管理端图书的增删改查(后台)