一、需求及注意事項
需求:
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}) def post(self,request): register_form = myforms.LoginForm(request.POST) if register_form.is_valid(): username = register_form.cleaned_data.get('username') password = register_form.cleaned_data.get('password') r_password = request.POST.get('r_password') if r_password == password: # 后台也需要判斷用戶名是否已存在, if not models.UserInfo.objects.filter(name=username).first(): password = salt_pwd(password,username) models.UserInfo.objects.create(name=username,pwd=password) # 注冊成功之后,設置session登錄狀態 request.session['user'] = username # 注冊成功 寫入log 並收集特定信息的日志 collect_logger.info('用戶:'+username+' 注冊') return redirect(reverse('book:book_list')) else: return render(request,'register.html',{'register_form':register_form,'error_msg':'確認密碼不符合'}) return render(request,'register.html',{'register_form':register_form}) def exist_user(request): ''' 查看用戶是否已經存在 :param request: :return: ''' if request.method == 'POST': username = request.POST.get('username') user_obj = models.UserInfo.objects.filter(name=username).first() reg = {'status':1,'msg':'用戶已存在'} if user_obj else {'status':0,'msg':'用戶不存在'} return HttpResponse(json.dumps(reg)) def logout(request): ''' 注銷 :param request: :return: ''' request.session.delete() return redirect(reverse('book:login')) # @check_login def book_list(request,field_id = 0, field_type = 'src'): ''' 書列表 3中情況 從book_list下來的書 從publisher_list 下來的書 從author_list下來的書 :param request: :param field_id: :param field_type: /publisher_list / author_list :return: ''' books = None filter_field = { 'publisher': 'publisher_id', 'author': 'authors__id' } field_dict = {filter_field.get(field_type): field_id} if field_type in ('publisher', 'author') else {} books = models.Book.objects.filter(**field_dict).values('id', 'title', 'price', 'publish_date', 'publisher__name').order_by('-id') ''' 注意上面得簡化方法 if field_type == 'publisher': books = models.Book.objects.filter(publisher_id=field_id).values('id','title','price','publish_date','publisher__name').order_by('-id') elif field_type == 'author': books = models.Book.objects.filter(authors__id=field_id).values('id','title','price','publish_date','publisher__name').order_by('-id') else: books = models.Book.objects.all().values('id', 'title', 'price', 'publish_date', 'publisher__name').order_by('-id') ''' current_page_num = request.GET.get('page', 1) page_obj = MyPaginator(books,current_page_num) current_path ={'path':request.path} ret_dic = page_obj.show_page # 頁碼返回的是字典 ret_dic.update(current_path) # 兩個字典拼接 # return render(request,'book_list.html',page_obj.show_page) return render(request,'book_list.html',ret_dic) def del_book(request, field_id = 0, field_type = 'src'): ''' 刪除一本書 3中情況 從book_list下來的書 從publisher_list 下來的書 從author_list下來的書 :param request: :param field_id: :param field_type: :return: ''' delete_id = request.POST.get('delete_id') if field_type == 'author': # 清除綁定關系 author_id = field_id author_obj = models.Author.objects.filter(id = author_id).first() try: author_obj.books.remove(delete_id) reg = {'status': 1, 'msg': '刪除成功'} except Exception as e: reg = {'status':0,'msg':'刪除失敗'} else: # 其他情況都刪除書 book_list publishe_list下來的書 try: models.Book.objects.filter(id=delete_id).delete() reg = {'status':1,'msg':'刪除成功'} except Exception as e: reg = {'status':0,'msg':'刪除失敗'} return HttpResponse(json.dumps(reg)) def add_book(request,field_id = 0, field_type = 'src'): ''' 增加書 :param request: :param field_id: :param field_type: :return: ''' current_publisher_id = 0 current_author_id = 0 if field_type == 'publisher': current_publisher_id = int(field_id) elif field_type == 'author': current_author_id = int(field_id) book_form = myforms.BookForm() if request.method == 'POST': if current_publisher_id: # 前端設置select disbaled 不能傳到后台了,因此需要需要這樣做 publisher_id = current_publisher_id else: publisher_id = int(request.POST.get('publisher')) if request.POST.get('publish_date') != '': publish_date = request.POST.get('publish_date') else: publish_date = datetime.datetime.now() book_form = myforms.BookForm(request.POST) if book_form.is_valid(): book_obj = models.Book.objects.create( title = book_form.cleaned_data.get('title'), price = book_form.cleaned_data.get('price'), publish_date = publish_date, publisher_id = publisher_id, ) if current_author_id: book_obj.authors.add(current_author_id) # 綁定多對多關系 # return redirect(reverse('book:book_list')) # 因為有3種情況,分別跳到自己對應的頁面下 return redirect(request.path.replace('add_book','book_list')) publisher = models.Publisher.objects.all().values('id', 'name').order_by('-id') return render(request,'add_book.html',{'book_form':book_form, 'publisher':publisher, "current_publisher_id":current_publisher_id}) def update_book(request, book_id, field_id = 0, field_type = 'src'): ''' 修改書 :param request: :param book_id: :param field_id: :param field_type: :return: ''' book = models.Book.objects.filter(id=book_id).first() book_form = myforms.BookForm() book_form.initial = {'title':book.title,'price':book.price} if request.method == 'POST': if request.POST.get('publish_date') != '': publish_date = request.POST.get('publish_date') else: publish_date = datetime.datetime.now() book_form = myforms.BookForm(request.POST) if book_form.is_valid(): models.Book.objects.filter(id=book_id).update( title=book_form.cleaned_data.get('title'), price=book_form.cleaned_data.get('price'), publish_date=publish_date, publisher_id=request.POST.get('publisher'), ) # 有3種情況,分別跳到自己對應的頁面下 if field_type in ('publisher','author'): new_url = reverse('book:book_list') + field_id + '/' + field_type else: new_url = reverse('book:book_list') return redirect(new_url) publisher = models.Publisher.objects.all().values('id', 'name').order_by('-id') return render(request,'update_book.html',{'book_form':book_form, 'publisher':publisher, 'current_publisher_id':book.publisher_id, 'publish_date':book.publish_date}) # @check_login def publisher_list(request): ''' 出版社列表 :param request: :return: ''' publishers = models.Publisher.objects.all().order_by('-id') current_page_num = request.GET.get('page',1) page_obj = MyPaginator(publishers,current_page_num) return render(request,'publisher_list.html',page_obj.show_page) def del_publisher(request): ''' 刪除一個出版社 :param request: :return: ''' delete_id = request.POST.get('delete_id') try: models.Publisher.objects.filter(id=delete_id).delete() reg = {'status':1,'msg':'刪除成功'} except Exception as e: reg = {'status':0,'msg':'刪除失敗'} return HttpResponse(json.dumps(reg)) def add_publisher(request): ''' 增加出版社 :param request: :return: ''' publisher_form = myforms.PublisherForm() if request.method == 'POST': publisher_form = myforms.PublisherForm(request.POST) if publisher_form.is_valid(): models.Publisher.objects.create(**publisher_form.cleaned_data) return redirect(reverse('book:publisher_list')) return render(request,'add_publisher.html',{'publisher_form':publisher_form}) def update_publisher(request,publisher_id): ''' 修改出版社 :param request: :param publisher_id: :return: ''' publisher = models.Publisher.objects.filter(id=publisher_id).first() publisher_form = myforms.PublisherForm() publisher_form.initial = {'name': publisher.name} # 對forms組件初始化 if request.method == 'POST': publisher_form = myforms.PublisherForm(request.POST) if publisher_form.is_valid(): models.Publisher.objects.filter(id=publisher_id).update(**publisher_form.cleaned_data) return redirect(reverse('book:publisher_list')) return render(request, 'update_publisher.html', {'publisher_form': publisher_form}) # @check_login def author_list(request): ''' 作者列表 :param request: :return: ''' authors = models.Author.objects.all().values('id','detail_id','name','detail__age','detail__addr').order_by('-id') current_page_num = request.GET.get('page') page_obj = MyPaginator(authors,current_page_num) return render(request,'author_list.html',page_obj.show_page) def del_author(request): ''' 刪除一個作者 :param request: :return: ''' delete_id = request.POST.get('delete_id') try: # 刪Author關聯的不會被刪掉 # models.Author.objects.filter(id=delete_id).delete() # 刪AuthorDetail關聯的才會被刪掉 models.AuthorDetail.objects.filter(id=delete_id).delete() reg = {'status':1,'msg':'刪除成功'} except Exception as e: reg = {'status':0,'msg':'刪除失敗'} return HttpResponse(json.dumps(reg)) def add_author(request): ''' 增加作者 :param request: :return: ''' author_form = myforms.AuthorForm() if request.method == 'POST': author_form = myforms.AuthorForm(request.POST) if author_form.is_valid(): name = author_form.cleaned_data.get('name') age = author_form.cleaned_data.get('age') addr = author_form.cleaned_data.get('addr') authordetail = models.AuthorDetail.objects.create(age=age,addr=addr) models.Author.objects.create(name=name,detail=authordetail) return redirect(reverse('book:author_list')) return render(request,'add_author.html',{'author_form':author_form}) def update_author(request,author_id): ''' 修改作者 :param request: :param author_id: :return: ''' author = models.Author.objects.filter(id=author_id).values('name','detail__age','detail__addr').first() author_form = myforms.AuthorForm() author_form.initial = {'name':author.get('name'),'age':author.get('detail__age'),'addr':author.get('detail__addr')} if request.method == 'POST': author_form = myforms.AuthorForm(request.POST) if author_form.is_valid(): name = author_form.cleaned_data.get('name') age = author_form.cleaned_data.get('age') addr = author_form.cleaned_data.get('addr') models.Author.objects.filter(id=author_id).update(name=name) models.AuthorDetail.objects.filter(author__id=author_id).update(age=age,addr=addr) return redirect(reverse('book:author_list')) return render(request,'update_author.html',{'author_form':author_form})
templatetags/my_tag_filter.py

# -*- coding:utf-8 -*- from django import template from django.utils.safestring import mark_safe register = template.Library() # 自定義過濾器 @register.filter def path_filter(x): new_path = x.replace('book_list','add_book') return new_path @register.filter def id_filter(x): return int(x) @register.filter def user_tags(request): return request.session.get('user') # 自定義標簽 @register.filter def update_path(current_path,book_id): new_path = current_path.replace('book_list', 'update_book/'+str(book_id)) return new_path # 自定義標簽 @register.simple_tag def multi_tags(x,y,z): return x*y*z @register.simple_tag def my_input(id,arg): result = "<input type='text' id='%s' class='%s' />" %(id,arg,) return mark_safe(result)
settings.py

""" Django settings for mybms project. Generated by 'django-admin startproject' using Django 2.0.1. For more information on this file, see https://docs.djangoproject.com/en/2.0/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/2.0/ref/settings/ """ import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '@#zsz!ri$$$7g9)=38%n-8y#g+wsc6c-*_8veuzhm%go=h@(2(' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'book.apps.BookConfig', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'mymiddleware.MyAuthMD', ] ROOT_URLCONF = 'mybms.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'mybms.wsgi.application' # Database # https://docs.djangoproject.com/en/2.0/ref/settings/#databases # DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), # } # } DATABASES ={ 'default':{ 'ENGINE': 'django.db.backends.mysql', 'NAME': 'mybms', 'HOST': '127.0.0.1', 'PORT': 3306, 'USER': 'root', 'PASSWORD': '123', } } # Password validation # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/2.0/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.0/howto/static-files/ STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static') ] # 終端打印sql語句 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } } # logging 配置 BASE_LOG_DIR = os.path.join(BASE_DIR,'log') LOGGING = { 'version': 1, # 保留字 'disable_existing_loggers': False, # 禁用已經存在的 logger 實例 'formatters': { # 詳細的日志格式 'standard': { 'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' '[%(levelname)s][%(message)s]' }, # 簡單的日志格式 'simple': { 'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' }, # 定義一個特殊的日志格式 'collect': { 'format': '%(message)s' } }, # 過濾器 'filters': { # DEBUG = True 的情況 才過濾 'require_debug_true': { '()': 'django.utils.log.RequireDebugTrue', }, }, # 處理器 'handlers': { # 在終端打印 'console': { 'level': 'DEBUG', 'filters': ['require_debug_true'], # 只有在Django debug為True時才在屏幕打印日志 'class': 'logging.StreamHandler', 'formatter': 'simple' }, # 默認 'default': { 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自動切 'filename': os.path.join(BASE_LOG_DIR, "bms_info.log"), # 日志文件 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M 一般配500M 'backupCount': 3, # 最多備份3個 'formatter': 'standard', 'encoding': 'utf-8', }, # 專門用來記 錯誤日志 'error': { 'level': 'ERROR', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自動切 'filename': os.path.join(BASE_LOG_DIR, "bms_err.log"), # 日志文件 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M 'backupCount': 5, 'formatter': 'standard', 'encoding': 'utf-8', }, # 專門 定義一個 收集特定信息的日志 'collect': { 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自動切 'filename': os.path.join(BASE_LOG_DIR, "bms_collect.log"), 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M 'backupCount': 5, 'formatter': 'collect', 'encoding': "utf-8" } }, 'loggers': { # 默認的logger應用如下配置 '': { 'handlers': ['default', 'console', 'error'], # 上線之后可以把'console'移除 'level': 'DEBUG', 'propagate': True, # 向不向更高級別的logger傳遞 }, # 名為 'collect'的logger還單獨處理 'collect': { 'handlers': ['console', 'collect'], 'level': 'INFO', } }, }
urls.py

from django.contrib import admin from django.urls import path,re_path,include from book import views urlpatterns = [ # re_path(r'^$', views.login), re_path(r'^$', views.LoginView.as_view()), re_path(r'^',include(('book.urls','book'))), ]
init_ajax.js

// 從cooikie 取 csrf token 的值 function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken'); // 將csrftoken 設置到ajax 請求頭中,后續的ajax請求就會自動攜帶這個csrf token function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function (xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } });
base.html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> {% block title %} <title>base</title> {% endblock title %} <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/plugins/sweetalert/sweetalert.css"> <style type="text/css"> .my-container{margin-top: 20px;} .showSweetAlert h2{padding-top: 20px;} .my-error-span{ color: red;display: block;font-size: 12px;} </style> </head> <body> <div class="container my-container"> <div class="row"> <div class="col-md-2"> {% block leftlist %} <div class="row"> <ul class="nav nav-pills nav-stacked my-left-con"> <li role="presentation"><a href="{% url 'book:book_list' %}">圖書列表</a></li> <li role="presentation"><a href="{% url 'book:publisher_list' %}">出版社列表</a></li> <li role="presentation"><a href="{% url 'book:author_list' %}">作者列表</a></li> </ul> </div> {% endblock %} </div> <div class="col-md-2"> {% block info%} <h3>信息</h3> {% endblock info%} {% block cancel %} {% load my_tag_filter %} <p>歡迎:{{ request|user_tags }}</p> <a href="/logout/">注銷</a> {% endblock %} </div> <div class="col-md-8"> {% block con %} <h3>內容</h3> {% endblock %} {% block paginator %} <nav class="my-page" aria-label="Page navigation"> <ul class="pagination"> <li><a href="?page=1">首頁</a></li> {% if current_page_data.has_previous %} <li> <a href="?page={{ current_page_data.previous_page_number }}" aria-label="Previous"> <span aria-hidden="true">«</span> </a> </li> {% else %} <li class="disabled"> <a href="#" aria-label="Previous"> <span aria-hidden="true">«</span> </a> </li> {% endif %} {% for item in page_range %} {% if current_page_num == item %} <li class="active"><a href="?page={{ item }}">{{ item }}</a></li> {% else %} <li><a href="?page={{ item }}">{{ item }}</a></li> {% endif %} {% endfor %} {% if current_page_data.has_next %} <li> <a href="?page={{ current_page_data.next_page_number }}" aria-label="Next"> <span aria-hidden="true">»</span> </a> </li> {% else %} <li class="disabled"> <a href="#" aria-label="Next"> <span aria-hidden="true">»</span> </a> </li> {% endif %} <li><a href="?page={{ paginator.num_pages }}">尾頁</a></li> </ul> </nav> {% endblock paginator%} </div> </div> </div> <script src="/static/jquery-3.2.1.min.js"></script> <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script> <script src="/static/plugins/sweetalert/sweetalert.min.js"></script> <script src="/static/init_ajax.js"></script> {% block script %} <script type="text/javascript"> $(function () { //沒有數據時,不顯示頁碼 var length = $('.table').children('tbody').children().length; if(length === 0){ $('.my-page').css('display','none') } //頁面加載時 左側導航樣式修改 var pathname = window.location.pathname; if(pathname.indexOf("book_list/")>0){ $('.my-left-con li').eq(0).addClass('active') }else if(pathname.indexOf("publisher_list/")>0){ $('.my-left-con li').eq(1).addClass('active') }else if(pathname.indexOf("author_list/")>0){ $('.my-left-con li').eq(2).addClass('active') } }); // 刪除按鈕點擊 獲取url 會有這樣的url(/book_list/2/publisher/)所以使用indexOf $('.my-delete').click(function () { var delete_id = $(this).parent().siblings('td').eq(1).text(); var row = $(this).parent().parent(); var pathname = window.location.pathname; var url = null; // replace 的原因 會有這樣的url(/book_list/2/author/) // 一對多和多對多刪除情況不一樣,一個清除綁定關系,一個時刪除 if(pathname.indexOf("book_list/")>0){ url = pathname.replace('/book_list/','/del_book/'); }else if(pathname.indexOf("publisher_list/")>0){ url = '/del_publisher/'; }else if(pathname.indexOf("author_list/")>0){ url = '/del_author/' } // 刪除插件的應用 swal({ title: "確定要刪除嗎? ", text: "刪了就找不回來了", type: "warning", showCancelButton: true, // 顯不顯示取消按鈕 confirmButtonClass: "btn-danger", confirmButtonText: "是,就是刪除", //按鈕上的文字 closeOnConfirm: false }, function(){ $.ajax({ url:url, type:'post', data:{'delete_id':delete_id}, success:function (ret) { var data = JSON.parse(ret); if(data.status === 1){ row.remove(); swal(data.msg, "恭喜您", "success"); }else{ swal(data.msg, "你可以嘗試在刪一次", "error"); } } }); }); }); </script> {% endblock script %} </body> </html>
book_list.html

{% extends 'base.html' %} {% block title %} <title>book_list</title> {% endblock title %} {% block info %} <h3>books</h3> {% endblock info %} {% block con %} {# 這里使用了自定義的過濾器 #} {% load my_tag_filter %} <a href="{{ path|path_filter }}" class="btn btn-info">創建</a> <table class="table table-bordered table-hover table-responsive table-striped" > <thead> <tr> <th>序號</th> <th>ID</th> <th>名稱</th> <th>價格</th> <th>出版社</th> <th>出版日期</th> <th>修改操作</th> <th>刪除操作</th> </tr> </thead> <tbody> {% for book in current_page_data %} <tr> <td>{{ forloop.counter|add:seque }}</td> <td>{{ book.id }}</td> <td>{{ book.title }}</td> <td>{{ book.price }}</td> <td>{{ book.publisher__name }}</td> <td>{{ book.publish_date|date:'Y-m-d' }}</td> {# <td><a href="/update_book/{{ book.id }}/" class="btn btn-success">修改</a></td>#} <td><a href="{{ path|update_path:book.id}}" class="btn btn-success">修改</a></td> <td><button class="btn btn-danger my-delete">刪除</button></td> </tr> {% endfor %} </tbody> </table> {% endblock con %}
hash_pwd.py

# -*- coding:utf-8 -*- import hashlib def salt_pwd(password,username): # 把用戶名當作鹽 用戶名只能唯一 return hashlib.md5(password.encode('utf-8') + username.encode('utf-8')).hexdigest()
mypage.py
# -*- coding:utf-8 -*- from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger class MyPaginator(object): def __init__(self, data, current_page_num, per_count = 5, show_page_count = 7): """ 自定義分頁器 :param data: 要分頁的所以數據 :param current_page_num: 當前頁碼 :param per_count: 每頁顯示的總數 :param show_page_count: 顯示頁碼的個數 """ self.data = data self.current_page_num = current_page_num self.per_count = per_count self.show_page_count = show_page_count self.half_page = int(self.show_page_count / 2) self.paginator = Paginator(self.data, self.per_count) # 獲取當前頁碼 try: self.current_page_num = int(self.current_page_num) if self.current_page_num < 1: self.current_page_num = 1 elif self.current_page_num > self.paginator.num_pages: self.current_page_num = self.paginator.num_pages except Exception as e: self.current_page_num = 1 # 顯示頁碼的范圍 if self.paginator.num_pages > self.show_page_count: if self.current_page_num - self.half_page < 1: self.page_range = range(1, self.show_page_count + 1) elif self.current_page_num + self.half_page > self.paginator.num_pages: self.page_range = range(self.paginator.num_pages - self.show_page_count + 1, self.paginator.num_pages + 1) else: self.page_range = range(self.current_page_num - self.half_page, self.current_page_num + self.half_page + 1) else: self.page_range = self.paginator.page_range # 獲取當前頁的數據 try: self.current_page_data = self.paginator.page(self.current_page_num) except EmptyPage as e: self.current_page_data = self.paginator.page(1) except PageNotAnInteger as e: self.current_page_data = self.paginator.page(self.paginator.num_pages) except Exception as e: self.current_page_data = self.paginator.page(1) @property def show_page(self): dic_page = {'current_page_data':self.current_page_data, 'paginator':self.paginator, 'current_page_num':self.current_page_num, 'page_range':self.page_range, 'seque':self.per_count*(self.current_page_num-1)} return dic_page
mymiddleware.py
# -*- coding:utf-8 -*- # 自定義中間件 from django.utils.deprecation import MiddlewareMixin from django.shortcuts import redirect,HttpResponse,render class MyAuthMD(MiddlewareMixin): white_list = ['/login/','/register/','/','/exist_user/'] # 白名單 一定要有,因為還沒有登錄,不需要驗證是否登錄 black_list = ['/black/',] # 黑名單 def process_request(self,request): # 拿到當前訪問網址 next_url = request.path if next_url in self.white_list or request.session.get('user'): return elif next_url in self.black_list: return HttpResponse('This is an illegal URL') else: return redirect('/login/?next={}'.format(next_url))
myscript.py
# -*- coding:utf-8 -*- import os if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mybms.settings") import django django.setup() from book import models import random # 批量插入book # ret1 = [models.Book(title='水滸傳{}'.format(i),price=random.randint(10,100),publisher_id=1) for i in range(50)] # ret2 = [models.Book(title='三國演義{}'.format(i),price=random.randint(10,100),publisher_id=2) for i in range(50)] # models.Book.objects.bulk_create(ret1) # models.Book.objects.bulk_create(ret2) # 批量插入publisher # ret1 = [models.Publisher(name='火星{}出版社'.format(i)) for i in range(50)] # models.Publisher.objects.bulk_create(ret1) # 批量插入Author # ret = [models.AuthorDetail(age=random.randint(20,50), addr = '北京{}'.format(i)) for i in range(50)] # models.AuthorDetail.objects.bulk_create(ret) # li = [] # for item in models.AuthorDetail.objects.all().values('id'): # ret = models.Author(name='alice_'+str(item.get('id')), detail_id=item.get('id')) # li.append(ret) # # models.Author.objects.bulk_create(li) # 作者綁定書 author book # for item in models.Author.objects.all(): # item.books.add(random.randint(100,200),random.randint(100,200),random.randint(100,200),random.randint(100,200))