Book增删改查
Book增删改查
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), # 首页 url(r'^$',views.home,name='home'), # 图书的展示 url(r'^book/list/',views.book_list,name='book_list'), # 书籍的添加 url(r'^book/add/',views.book_add,name='book_add'), # 书籍的编辑 url(r'^book/edit/(?P<edit_id>\d+)/',views.book_edit,name='book_edit'), # 书籍的删除 url(r'^book/delete/(\d+)/',views.book_delete,name='book_delete') ]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.db import models # Create your models here. from django.db import models # Create your models here. class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_places=2) publish_date = models.DateField(auto_now_add=True) # 一对多 publish = models.ForeignKey(to='Publish') # 多对多 authors = models.ManyToManyField(to='Author') def __str__(self): return self.title class Publish(models.Model): name = models.CharField(max_length=32) addr = models.CharField(max_length=64) email = models.EmailField() # varchar(254) 该字段类型不是给models看的 而是给后面我们会学到的校验性组件看的 def __str__(self): return '对象:%s'%self.name class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() # 一对一 author_detail = models.OneToOneField(to='AuthorDetail') class AuthorDetail(models.Model): phone = models.BigIntegerField() # 电话号码用BigIntegerField或者直接用CharField addr = models.CharField(max_length=64)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.shortcuts import render,redirect,HttpResponse from app01 import models # Create your views here. def home(request): return render(request,'home.html') def book_list(request): # 先查询出所有的书籍信息 传递给html页面 book_queryset = models.Book.objects.all() return render(request,'book_list.html',locals()) def book_add(request): if request.method == 'POST': # 获取前端提交过来的所有数据 title = request.POST.get("title") price = request.POST.get("price") publish_date = request.POST.get("publish_date") publish_id = request.POST.get("publish") authors_list = request.POST.getlist("authors") # [1,2,3,4,] # 操作数据库存储数据 # 书籍表 book_obj = models.Book.objects.create(title=title,price=price,publish_date=publish_date,publish_id=publish_id) # 书籍与作者的关系表 book_obj.authors.add(*authors_list) # 跳转到书籍的展示页面 """ redirect括号内可以直接写url 其实也可以直接写别名 但是如果你的别名需要额外给参数的话,那么就必须使用reverse解析了 """ return redirect('book_list') # 先获取当前系统中所有的出版社信息和作者信息 publish_queryset = models.Publish.objects.all() author_queryset = models.Author.objects.all() return render(request,'book_add.html',locals()) def book_edit(request,edit_id): # 获取当前用户想要编辑的书籍对象 展示给用户看 edit_obj = models.Book.objects.filter(pk=edit_id).first() if request.method == 'POST': title = request.POST.get("title") price = request.POST.get("price") publish_date = request.POST.get("publish_date") publish_id = request.POST.get("publish") authors_list = request.POST.getlist("authors") # [1,2,3,4,] models.Book.objects.filter(pk=edit_id).update(title=title, price=price, publish_date=publish_date, publish_id=publish_id ) # 该第三张关系表 edit_obj.authors.set(authors_list) return redirect('book_list') publish_queryset = models.Publish.objects.all() author_queryset = models.Author.objects.all() return render(request,'book_edit.html',locals()) def book_delete(request,delete_id): # 简单粗暴 直接删除 models.Book.objects.filter(pk=delete_id).delete() # 直接跳转到展示页 return redirect('book_list')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> {% block css %} {% endblock %} </head> <body> <nav class="navbar navbar-inverse"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">图书管理系统</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">图书 <span class="sr-only">(current)</span></a></li> <li><a href="#">作者</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">更多 <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> <li role="separator" class="divider"></li> <li><a href="#">One more separated link</a></li> </ul> </li> </ul> <form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search"> </div> <button type="submit" class="btn btn-default">Submit</button> </form> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Jason</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">更多操作 <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div class="container-fluid"> <div class="row"> <div class="col-md-3"> <div class="list-group"> <a href="#" class="list-group-item active"> 首页 </a> <a href="{% url 'book_list' %}" class="list-group-item">图书列表</a> <a href="#" class="list-group-item">出版社列表</a> <a href="#" class="list-group-item">作者列表</a> <a href="#" class="list-group-item">更多</a> </div> </div> <div class="col-md-9"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">BMS</h3> </div> <div class="panel-body"> {% block content %} <div class="jumbotron"> <h1>欢迎来到亚洲最大的线上读书平台!</h1> <p>这里有的没有的书籍都有</p> <p><a class="btn btn-primary btn-lg" href="#" role="button">点击有你好看哟~</a></p> </div> <div class="row"> <div class="col-sm-6 col-md-4"> <div class="thumbnail"> <img src="https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3190214567,3115602234&fm=26&gp=0.jpg" alt="..."> <div class="caption"> <h3>Thumbnail label</h3> <p>...</p> <p><a href="#" class="btn btn-primary" role="button">Button</a> <a href="#" class="btn btn-default" role="button">Button</a></p> </div> </div> </div> <div class="col-sm-6 col-md-4"> <div class="thumbnail"> <img src="https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2345197983,4250806042&fm=26&gp=0.jpg" alt="..."> <div class="caption"> <h3>Thumbnail label</h3> <p>...</p> <p><a href="#" class="btn btn-primary" role="button">Button</a> <a href="#" class="btn btn-default" role="button">Button</a></p> </div> </div> </div> <div class="col-sm-6 col-md-4"> <div class="thumbnail"> <img src="https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3844373782,4000514073&fm=26&gp=0.jpg" alt="..."> <div class="caption"> <h3>Thumbnail label</h3> <p>...</p> <p><a href="#" class="btn btn-primary" role="button">Button</a> <a href="#" class="btn btn-default" role="button">Button</a></p> </div> </div> </div> </div> {% endblock %} </div> </div> </div> </div></div> {% block js %} {% endblock %} </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{% extends 'home.html' %} {% block content %} <a href="{% url 'book_add' %}" class="btn btn-success">添加</a> <br> <br> <table class="table table-hover table-striped"> <thead> <tr> <th>ID</th> <th>书名</th> <th>价格</th> <th>出版日期</th> <th>出版社</th> <th>作者</th> <th>操作</th> </tr> </thead> <tbody> {% for book_obj in book_queryset %} <tr> <td>{{ book_obj.pk }}</td> <td>{{ book_obj.title }}</td> <td>{{ book_obj.price }}</td> <td>{{ book_obj.publish_date|date:'Y-m-d' }}</td> <td>{{ book_obj.publish.name }}</td> <td> {% for author_obj in book_obj.authors.all %} {% if forloop.last %} {{ author_obj.name }} {% else %} {{ author_obj.name }}、 {% endif %} {% endfor %} </td> <td> <a href="{% url 'book_edit' book_obj.pk %}" class="btn btn-primary btn-xs">编辑</a> <a href="{% url 'book_delete' book_obj.pk %}" class="btn btn-danger btn-xs">删除</a> </td> </tr> {% endfor %} </tbody> </table> {% endblock %}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{% extends 'home.html' %} {% block content %} <h1 class="text-center">书籍编辑</h1> <form action="" method="post"> <p>书名: <input type="text" name="title" class="form-control" value="{{ edit_obj.title }}"> </p> <p>价格: <input type="text" name="price" class="form-control" value="{{ edit_obj.price }}"> </p> <p>出版日期: <input type="date" name="publish_date" class="form-control" value="{{ edit_obj.publish_date|date:'Y-m-d' }}"> </p> <p>出版社: <select name="publish" id="" class="form-control"> {% for publish_obj in publish_queryset %} {# 针对当前书籍对象的出版社应该默认选中#} {% if edit_obj.publish == publish_obj %} <option value="{{ publish_obj.pk }}" selected>{{ publish_obj.name }}</option> {% else %} <option value="{{ publish_obj.pk }}">{{ publish_obj.name }}</option> {% endif %} {% endfor %} </select> </p> <p>作者: <select name="authors" id="" multiple class="form-control"> {% for author_obj in author_queryset %} {% if author_obj in edit_obj.authors.all %} <option value="{{ author_obj.pk }}" selected>{{ author_obj.name }}</option> {% else %} <option value="{{ author_obj.pk }}">{{ author_obj.name }}</option> {% endif %} {% endfor %} </select> </p> <input type="submit" value="编辑" class="btn btn-info btn-block"> </form> {% endblock %}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{% extends 'home.html' %} {% block content %} <h1 class="text-center">书籍添加</h1> <form action="" method="post"> <p>书名: <input type="text" name="title" class="form-control"> </p> <p>价格: <input type="text" name="price" class="form-control"> </p> <p>出版日期: <input type="date" name="publish_date" class="form-control"> </p> <p>出版社: <select name="publish" id="" class="form-control"> {% for publish_obj in publish_queryset %} <option value="{{ publish_obj.pk }}">{{ publish_obj.name }}</option> {% endfor %} </select> </p> <p>作者: <select name="authors" id="" multiple class="form-control"> {% for author_obj in author_queryset %} <option value="{{ author_obj.pk }}">{{ author_obj.name }}</option> {% endfor %} </select> </p> <input type="submit" value="新增" class="btn btn-primary btn-block"> </form> {% endblock %}
Other
SweetAlert : https://www.sweetalert.cn/guides.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class User(models.Model): username = models.CharField(max_length=32) age = models.IntegerField() # 性别 gender_choices = ( (1,'男'), (2,'女'), (3,'其他'), ) gender = models.IntegerField(choices=gender_choices) score_choices = ( ('A','优秀'), ('B','良好'), ('C','及格'), ('D','不合格'), ) # 保证字段类型跟列举出来的元祖第一个数据类型一致即可 score = models.CharField(choices=score_choices,null=True) """ 该gender字段存的还是数字 但是如果存的数字在上面元祖列举的范围之内 那么可以非常轻松的获取到数字对应的真正的内容 1.gender字段存的数字不在上述元祖列举的范围内容,直接返回数字 2.如果在如何获取对应的中文信息 """ ################################### # tests.py from django.test import TestCase # Create your tests here. import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day66.settings") import django django.setup() from app01 import models # models.User.objects.create(username='jason',age=18,gender=1) # models.User.objects.create(username='egon',age=85,gender=2) # models.User.objects.create(username='tank',age=40,gender=3) # 存的时候 没有列举出来的数字也能存(范围还是按照字段类型决定) # models.User.objects.create(username='tony',age=45,gender=4) # 取 # user_obj = models.User.objects.filter(pk=1).first() # print(user_obj.gender) # 只要是choices参数的字段 如果你想要获取对应信息 固定写法 get_字段名_display() # print(user_obj.get_gender_display()) user_obj = models.User.objects.filter(pk=4).first() # 如果没有对应关系 那么字段是什么还是展示什么 print(user_obj.get_gender_display()) # 4
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.db import models # 全自动:利用orm自动帮我们创建第三张关系表 class Book(models.Model): name = models.CharField(max_length=32) authors = models.ManyToManyField(to='Author') class Author(models.Model): name = models.CharField(max_length=32) """ 优点:代码不需要你写 非常的方便 还支持orm提供操作第三张关系表的方法... 不足之处:第三张关系表的扩展性极差(没有办法额外添加字段...) """ # 纯手动 class Book(models.Model): name = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) class Book2Author(models.Model): book_id = models.ForeignKey(to='Book') author_id = models.ForeignKey(to='Author') ''' 优点:第三张表完全取决于你自己进行额外的扩展 不足之处:需要写的代码较多,不能够再使用orm提供的简单的方法 不建议你用该方式 ''' # 半自动 class Book(models.Model): name = models.CharField(max_length=32) authors = models.ManyToManyField(to='Author', through='Book2Author', through_fields=('book', 'author') ) class Author(models.Model): name = models.CharField(max_length=32) # books = models.ManyToManyField(to='Book', # through='Book2Author', # through_fields=('author','book') # ) class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') """ through_fields字段先后顺序 判断的本质: 通过第三张表查询对应的表 需要用到哪个字段就把哪个字段放前面 你也可以简化判断 当前表是谁 就把对应的关联字段放前面 半自动:可以使用orm的正反向查询 但是没法使用add,set,remove,clear这四个方法 """ # 总结:你需要掌握的是全自动和半自动 为了扩展性更高 一般我们都会采用半自动(写代码要给自己留一条后路)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
##ajax $('#btn').click(function () { // 朝后端发送ajax请求 $.ajax({ // 1.指定朝哪个后端发送ajax请求 url:'', // 不写就是朝当前地址提交 // 2.请求方式 type:'post', // 不指定默认就是get 都是小写 dataType:'JSON', // 3.数据 {#data:{'username':'jason','password':123},#} data:{'i1':$('#d1').val(),'i2':$('#d2').val()}, // 4.回调函数:当后端给你返回结果的时候会自动触发 args接受后端的返回结果 success:function (args) { console.log(typeof args) } }) }) """ 朝发送请求的方式 1.浏览器地址栏直接输入url回车 GET请求 2.a标签href属性 GET请求 3.form表单 GET请求/POST请求 4.ajax GET请求/POST请求 针对后端如果是用HttpResponse返回的数据 回调函数不会自动帮你反序列化 如果后端直接用的是JsonResponse返回的数据 回调函数会自动帮你反序列化 HttpResponse解决方式 1.自己在前端利用JSON.parse() 2.在ajax里面配置一个参数 dataType:'JSON', """ # 我们主要研究post请求数据的编码格式 """ get请求数据就是直接放在url后面的 url?username=jason&password=123 """ # 可以朝后端发送post请求的方式 """ 1.form表单 2.ajax请求 """ """ 前后端传输数据的编码格式 urlencoded formdata json """ # 研究form表单 默认的数据编码格式是urlencoded 数据格式:username=jason&password=123 django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装到request.POST中 username=jason&password=123 >>> request.POST 如果你把编码格式改成formdata,那么针对普通的键值对还是解析到request.POST中而将文件解析到request.FILES中 file 提交文件:form表单需要加上属性enctype="multipart/form-data" form表单是没有办法发送json格式数据的 # 研究ajax 默认的编码格式也是urlencoded 数据格式:username=jason&age=20 django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装到request.POST中 username=jason&age=20 >>> request.POST ### ajax发送json格式数据 """ 前后端传输数据的时候一定要确保编码格式跟数据真正的格式是一致的 {"username":"jason","age":25} 在request.POST里面肯定找不到 django针对json格式的数据 不会做任何的处理 request对象方法补充 request.is_ajax() 判断当前请求是否是ajax请求 返回布尔值 """ <script> $('#d1').click(function () { $.ajax({ url:'', type:'post', dataType: "json", //表示返回值类型 data:JSON.stringify({'username':'jason','age':25}), //JSON.stringify转换成json类型 contentType:'application/json', // 指定编码格式 success:function () { } }) }) </script> json_bytes = request.body json_str = json_bytes.decode('utf-8') json_dict = json.loads(json_str) # json.loads括号内如果传入了一个二进制格式的数据那么内部自动解码再反序列化 json_dict = json.loads(json_bytes) """ ajax发送json格式数据需要注意点 1.contentType参数指定成:application/json 2.数据是真正的json格式数据,用JSON.stringify转换成json类型 3.django后端不会帮你处理json格式数据需要你自己去request.body获取并处理 import json def ab_json(request): if request.is_ajax(): # print(request.is_ajax()) # print(request.POST) # print(request.FILES) # print(request.body) # b'{"username":"jason","age":25}' # 针对json格式数据需要你自己手动处理 json_bytes = request.body # json_str = json_bytes.decode('utf-8') # json_dict = json.loads(json_str) # json.loads括号内如果传入了一个二进制格式的数据那么内部自动解码再反序列化 json_dict = json.loads(json_bytes) # {'username': 'jason', 'age': 25} <class 'dict'> print(json_dict,type(json_dict)) # {'username': 'jason', 'age': 25} <class 'dict'> return render(request,'ab_json.html') """ ### ajax发送文件 """ ajax发送文件需要借助于js内置对象FormData """ <script> // 点击按钮朝后端发送普通键值对和文件数据 $('#d4').on('click',function () { // 1 需要先利用FormData内置对象 let formDateObj = new FormData(); // 2 添加普通的键值对 formDateObj.append('username',$('#d1').val()); formDateObj.append('password',$('#d2').val()); // 3 添加文件对象 formDateObj.append('myfile',$('#d3')[0].files[0]) // 4 将对象基于ajax发送给后端 $.ajax({ url:'', type:'post', data:formDateObj, // 直接将对象放在data后面即可 // ajax发送文件必须要指定的两个参数 contentType:false, // 不需使用任何编码 django后端能够自动识别formdata对象 processData:false, // 告诉你的浏览器不要对你的数据进行任何处理 success:function (args) { } }) }) </script> def ab_file(request): if request.is_ajax(): if request.method == 'POST': print(request.POST) print(request.FILES) return render(request,'ab_file.html') """ 总结: 1.需要利用内置对象FormData // 2 添加普通的键值对 formDateObj.append('username',$('#d1').val()); formDateObj.append('password',$('#d2').val()); // 3 添加文件对象 formDateObj.append('myfile',$('#d3')[0].files[0]) 2.需要指定两个关键性的参数 contentType:false, // 不需使用任何编码 django后端能够自动识别formdata对象 processData:false, // 告诉你的浏览器不要对你的数据进行任何处理 3.django后端能够直接识别到formdata对象并且能够将内部的普通键值自动解析并封装到request.POST中 文件数据自动解析并封装到request.FILES中 """
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import json from django.http import JsonResponse from django.core import serializers def ab_ser(request): user_queryset = models.User.objects.all() # [{},{},{},{},{}] # user_list = [] # for user_obj in user_queryset: # tmp = { # 'pk':user_obj.pk, # 'username':user_obj.username, # 'age':user_obj.age, # 'gender':user_obj.get_gender_display() # } # user_list.append(tmp) # return JsonResponse(user_list,safe=False) # return render(request,'ab_ser.html',locals()) # 序列化 res = serializers.serialize('json',user_queryset) """会自动帮你将数据变成json格式的字符串 并且内部非常的全面""" return HttpResponse(res) """ [ {"pk": 1, "username": "jason", "age": 25, "gender": "male"}, {"pk": 2, "username": "egon", "age": 31, "gender": "female"}, {"pk": 3, "username": "kevin", "age": 32, "gender": "others"}, {"pk": 4, "username": "tank", "age": 40, "gender": 4} ] ### django自带的序列化 [ { "model": "app01.user", "pk": 1, "fields": {"username": "jason", "age": 25, "gender": 1}}, { "model": "app01.user", "pk": 2, "fields": {"username": "egon", "age": 31, "gender": 2}}, { "model": "app01.user", "pk": 3, "fields": {"username": "kevin", "age": 32, "gender": 3}}, { "model": "app01.user", "pk": 4, "fields": {"username": "tank", "age": 40, "gender": 4}} ] 写接口就是利用序列化组件渲染数据然后写一个接口文档 该交代交代一下就完事 """
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def ab_pl(request): # 先给Book插入一万条数据 # for i in range(10000): # models.Book.objects.create(title='第%s本书'%i) # # 再将所有的数据查询并展示到前端页面 book_queryset = models.Book.objects.all() # 批量插入 # book_list = [] # for i in range(100000): # book_obj = models.Book(title='第%s本书'%i) # book_list.append(book_obj) # models.Book.objects.bulk_create(book_list) """ 当你想要批量插入数据的时候 使用orm给你提供的bulk_create能够大大的减少操作时间 :param request: :return: """ return render(request,'ab_pl.html',locals())
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#####views.py def user_list(request): user_queryset = models.User.objects.all() return render(request,'user_list.html',locals()) import time def delete_user(request): """ 前后端在用ajax进行交互的时候 后端通常给ajax的回调函数返回一个字典格式的数据 :param request: :return: """ if request.is_ajax(): if request.method == 'POST': back_dic = {"code":1000,'msg':''} time.sleep(3) # 模拟操作数据的延迟 delete_id = request.POST.get('delete_id') models.User.objects.filter(pk=delete_id).delete() back_dic['msg'] = '数据已经删了,你赶紧跑路!' # 我们需要告诉前端我们操作的结果 return JsonResponse(back_dic) ############## # 用户展示页面 url(r'^user/list/',views.user_list), url(r'^delete/user/',views.delete_user), ############## #####user_list.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> <style> div.sweet-alert h2 { padding-top: 10px; } </style> {% load static %} <link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}"> <script src="{% static 'dist/sweetalert.min.js' %}"></script> </head> <body> <div class="container-fluid"> <h1 class="text-center">数据展示</h1> <div class="row"> <div class="col-md-8 col-md-offset-2"> <table class="table-striped table table-hover"> <thead> <tr> <th>ID</th> <th>username</th> <th>age</th> <th>gender</th> <th>actions</th> </tr> </thead> <tbody> {% for user_obj in user_queryset %} <tr> <td>{{ user_obj.pk }}</td> <td>{{ user_obj.username }}</td> <td>{{ user_obj.age }}</td> <td>{{ user_obj.get_gender_display }}</td> <td> <button class="btn btn-primary btn-xs">编辑</button> <button class="btn btn-danger btn-xs del" delete_id="{{ user_obj.pk }}">删除</button> </td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> <script> $('.del').on('click',function () { // 先将当前标签对象存储起来 let currentBtn = $(this); // 二次确认弹框 swal({ title: "你确定要删吗?", text: "你可要考虑清除哦,可能需要拎包跑路哦!", type: "warning", showCancelButton: true, confirmButtonClass: "btn-danger", confirmButtonText: "是的,老子就要删!", cancelButtonText: "算了,算了!", closeOnConfirm: false, closeOnCancel: false, showLoaderOnConfirm: true }, function(isConfirm) { if (isConfirm) { // 朝后端发送ajax请求删除数据之后 再弹下面的提示框 $.ajax({ {#url:'/delete/user/' + currentBtn.attr('delete_id'), // 1 传递主键值方式1#} url:'/delete/user/', // 2 放在请求体里面 type:'post', data:{'delete_id':currentBtn.attr('delete_id')}, success:function (args) { // args = {'code':'','msg':''} // 判断响应状态码 然后做不同的处理 if(args.code === 1000){ swal("删了!", args.msg, "success"); // 1.lowb版本 直接刷新当前页面 {#window.location.reload()#} // 2.利用DOM操作 动态刷新 currentBtn.parent().parent().remove() }else{ swal('完了','出现了未知的错误','info') } } }) } else { swal("怂逼", "不要说我认识你", "error"); } }); }) </script> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Pagination(object): def __init__(self, current_page, all_count, per_page_num=10, pager_count=11): """ 封装分页相关数据 :param current_page: 当前页 :param all_count: 数据库中的数据总条数 :param per_page_num: 每页显示的数据条数 :param pager_count: 最多显示的页码个数 """ try: current_page = int(current_page) except Exception as e: current_page = 1 if current_page < 1: current_page = 1 self.current_page = current_page self.all_count = all_count self.per_page_num = per_page_num # 总页码 all_pager, tmp = divmod(all_count, per_page_num) if tmp: all_pager += 1 self.all_pager = all_pager self.pager_count = pager_count self.pager_count_half = int((pager_count - 1) / 2) @property def start(self): return (self.current_page - 1) * self.per_page_num @property def end(self): return self.current_page * self.per_page_num def page_html(self): # 如果总页码 < 11个: if self.all_pager <= self.pager_count: pager_start = 1 pager_end = self.all_pager + 1 # 总页码 > 11 else: # 当前页如果<=页面上最多显示11/2个页码 if self.current_page <= self.pager_count_half: pager_start = 1 pager_end = self.pager_count + 1 # 当前页大于5 else: # 页码翻到最后 if (self.current_page + self.pager_count_half) > self.all_pager: pager_end = self.all_pager + 1 pager_start = self.all_pager - self.pager_count + 1 else: pager_start = self.current_page - self.pager_count_half pager_end = self.current_page + self.pager_count_half + 1 page_html_list = [] # 添加前面的nav和ul标签 page_html_list.append(''' <nav aria-label='Page navigation>' <ul class='pagination'> ''') first_page = '<li><a href="?page=%s">首页</a></li>' % (1) page_html_list.append(first_page) if self.current_page <= 1: prev_page = '<li class="disabled"><a href="#">上一页</a></li>' else: prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,) page_html_list.append(prev_page) for i in range(pager_start, pager_end): if i == self.current_page: temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,) else: temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,) page_html_list.append(temp) if self.current_page >= self.all_pager: next_page = '<li class="disabled"><a href="#">下一页</a></li>' else: next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,) page_html_list.append(next_page) last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,) page_html_list.append(last_page) # 尾部添加标签 page_html_list.append(''' </nav> </ul> ''') return ''.join(page_html_list)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.shortcuts import render,HttpResponse from app01 import models from utils.mypage import Pagination def ab_pl(request): # 先给Book插入一万条数据 # for i in range(10000): # models.Book.objects.create(title='第%s本书'%i) # # 再将所有的数据查询并展示到前端页面 # book_queryset = models.Book.objects.all() # 批量插入 # book_list = [] # for i in range(100000): # book_obj = models.Book(title='第%s本书'%i) # book_list.append(book_obj) # models.Book.objects.bulk_create(book_list) """ 当你想要批量插入数据的时候 使用orm给你提供的bulk_create能够大大的减少操作时间 :param request: :return: """ # # 分页 # book_list = models.Book.objects.all() # # # 想访问哪一页 # current_page = request.GET.get('page',1) # 如果获取不到当前页码 就展示第一页 # # 数据类型转换 # try: # current_page = int(current_page) # except Exception: # current_page = 1 # # 每页展示多少条 # per_page_num = 10 # # 起始位置 # start_page = (current_page - 1) * per_page_num # # 终止位置 # end_page = current_page * per_page_num # # # 计算出到底需要多少页 # all_count = book_list.count() # # page_count, more = divmod(all_count, per_page_num) # if more: # page_count += 1 # # page_html = '' # xxx = current_page # if current_page < 6: # current_page = 6 # for i in range(current_page-5,current_page+6): # if xxx == i: # page_html += '<li class="active"><a href="?page=%s">%s</a></li>'%(i,i) # else: # page_html += '<li><a href="?page=%s">%s</a></li>'%(i,i) # # # # book_queryset = book_list[start_page:end_page] book_queryset = models.Book.objects.all() current_page = request.GET.get('page',1) all_count = book_queryset.count() # 1 传值生成对象 page_obj = Pagination(current_page=current_page,all_count=all_count) # 2 直接对总数据进行切片操作 page_queryset = book_queryset[page_obj.start:page_obj.end] # 3 将page_queryset传递到页面 替换之前的book_queryset return render(request,'ab_pl.html',locals()) """ per_page_num = 10 current_page start_page end_page 1 0 10 2 10 20 3 20 30 4 30 40 per_page_num = 5 current_page start_page end_page 1 0 5 2 5 10 3 10 15 4 15 20 start_page = (current_page - 1) * per_page_num end_page = current_page * per_page_num """
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> {% for book_obj in page_queryset %} <p>{{ book_obj.title }}</p> <nav aria-label="Page navigation"> </nav> {% endfor %} {#利用自定义分页器直接显示分页器样式#} {{ page_obj.page_html|safe }} </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.shortcuts import render,HttpResponse # Create your views here. def ab_form(request): back_dic = {'username':'','password':''} if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if 'python666' in username: back_dic['username'] = '不符合社会主义核心价值观' if len(password) < 3: back_dic['password'] = '不能太短 不好!' """ 无论是post请求还是get请求 页面都能够获取到字典 只不过get请求来的时候 字典值都是空的 而post请求来之后 字典可能有值 """ return render(request,'ab_form.html',locals()) from django import forms from django.core.validators import RegexValidator class MyForm(forms.Form): # username字符串类型最小3位最大8位 username = forms.CharField(min_length=3,max_length=8,label='用户名',initial='jason',required=False, error_messages={ 'min_length':'用户名最少3位', 'max_length':'用户名最大8位', 'required':"用户名不能为空" }, widget=forms.widgets.TextInput(attrs={'class':'form-control'}) ) # password字符串类型最小3位最大8位 password = forms.CharField(min_length=3,max_length=8,label='密码', error_messages={ 'min_length': '密码最少3位', 'max_length': '密码最大8位', 'required': "密码不能为空" }, widget=forms.widgets.PasswordInput(attrs={'class':'form-control c1 c2'}) ) confirm_password = forms.CharField(min_length=3, max_length=8, label='确认密码', error_messages={ 'min_length': '确认密码最少3位', 'max_length': '确认密码最大8位', 'required': "确认密码不能为空" }, widget=forms.widgets.PasswordInput(attrs={'class':'form-control'}) ) # email字段必须符合邮箱格式 xxx@xx.com email = forms.EmailField(label='邮箱', error_messages={ 'invalid':'邮箱格式不正确', 'required': "邮箱不能为空" }, widget=forms.widgets.EmailInput(attrs={'class':'form-control'}) ) phone = forms.CharField( validators=[ RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头') ], ) # radio gender = forms.ChoiceField( choices=((1, "男"), (2, "女"), (3, "保密")), label="性别", initial=3, widget=forms.widgets.RadioSelect() ) # select hobby = forms.ChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=3, widget=forms.widgets.Select() ) # 多选 hobby1 = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.SelectMultiple() ) # 单选checkbox keep = forms.ChoiceField( label="是否记住密码", initial="checked", widget=forms.widgets.CheckboxInput() ) # 多选checkbox hobby2 = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() ) # 钩子函数 # 局部钩子 def clean_username(self): # 获取到用户名 username = self.cleaned_data.get('username') if '666' in username: # 提示前端展示错误信息 self.add_error('username','光喊666是不行滴~') # 将钩子函数钩去出来数据再放回去 return username # 全局钩子 def clean(self): password = self.cleaned_data.get('password') confirm_password = self.cleaned_data.get('confirm_password') if not confirm_password == password: self.add_error('confirm_password','两次密码不一致') # 将钩子函数钩出来数据再放回去 return self.cleaned_data def index(request): # 1 先产生一个空对象 form_obj = MyForm() if request.method == 'POST': # 获取用户数据并且校验 """ 1.数据获取繁琐 2.校验数据需要构造成字典的格式传入才行 ps:但是request.POST可以看成就是一个字典 """ # 3.校验数据 form_obj = MyForm(request.POST) # 4.判断数据是否合法 if form_obj.is_valid(): # 5.如果合法 操作数据库存储数据 return HttpResponse('OK') # 5.不合法 有错误 # 2 直接将该空对象传递给html页面 return render(request,'index.html',locals())
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> <form action="" method="post" novalidate> {# <p>第一种渲染方式:代码书写极少,封装程度太高 不便于后续的扩展 一般情况下只在本地测试使用</p>#} {# {{ form_obj.as_p }}#} {# {{ form_obj.as_ul }}#} {# {{ form_obj.as_table }}#} {# <p>第二种渲染方式:可扩展性很强 但是需要书写的代码太多 一般情况下不用</p>#} {# <p>{{ form_obj.username.label }}:{{ form_obj.username }}</p>#} {# <p>{{ form_obj.password.label }}:{{ form_obj.password }}</p>#} {# <p>{{ form_obj.email.label }}:{{ form_obj.email }}</p>#} <p>第三种渲染方式(推荐使用):代码书写简单 并且扩展性也高</p> {% for form in form_obj %} <p> {{ form.label }}:{{ form }} <span style="color: red">{{ form.errors.0 }}</span> </p> {% endfor %} <input type="submit" class="btn btn-info"> </form> </body> </html> #####################ab_form.html中 <form action="" method="post"> <p>username: <input type="text" name="username"> <span style="color: red">{{ back_dic.username }}</span> </p> <p>password: <input type="text" name="password"> <span style="color: red">{{ back_dic.password }}</span> </p> <input type="submit" class="btn btn-info"> </form>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.shortcuts import render,HttpResponse,redirect # Create your views here. # 校验用户是否登陆的装饰器 """ 用户如果在没有登陆的情况下想访问一个需要登陆的页面 那么先跳转到登陆页面 当用户输入正确的用户名和密码之后 应该跳转到用户之前想要访问的页面去 而不是直接写死 """ def login_auth(func): def inner(request,*args,**kwargs): # print(request.path_info) # print(request.get_full_path()) # 能够获取到用户上一次想要访问的url target_url = request.get_full_path() if request.COOKIES.get('username'): return func(request,*args,**kwargs) else: return redirect('/login/?next=%s'%target_url) return inner def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if username == 'jason' and password == '123': # 获取用户上一次想要访问的url target_url = request.GET.get('next') # 这个结果可能是None if target_url: obj = redirect(target_url) else: # 保存用户登陆状态 obj = redirect('/home/') # 让浏览器记录cookie数据 obj.set_cookie('username', 'jason666') # 超时时间3秒到期 """ 浏览器不单单会帮你存 而且后面每次访问你的时候还会带着它过来 """ # 跳转到一个需要用户登陆之后才能看的页面 return obj return render(request,'login.html') @login_auth def home(request): # 获取cookie信息 判断你有没有 # if request.COOKIES.get('username') == 'jason666': # return HttpResponse("我是home页面,只有登陆的用户才能进来哟~") # # 没有登陆应该跳转到登陆页面 # return redirect('/login/') return HttpResponse("我是home页面,只有登陆的用户才能进来哟~") @login_auth def index(request): # 获取cookie信息 判断你有没有 # if request.COOKIES.get('username') == 'jason666': # return HttpResponse("我是home页面,只有登陆的用户才能进来哟~") # # 没有登陆应该跳转到登陆页面 # return redirect('/login/') return HttpResponse("我是index页面,只有登陆的用户才能进来哟~") @login_auth def func(request): # 获取cookie信息 判断你有没有 # if request.COOKIES.get('username') == 'jason666': # return HttpResponse("我是home页面,只有登陆的用户才能进来哟~") # # 没有登陆应该跳转到登陆页面 # return redirect('/login/') return HttpResponse("我是func页面,只有登陆的用户才能进来哟~") @login_auth def logout(request): obj = redirect('/login/') obj.delete_cookie('username') return obj def set_session(request): request.session['hobby'] = 'girl' # request.session['hobby1'] = 'girl1' # request.session.set_expiry() """ 内部发送了那些事 1.django内部会自动帮你生成一个随机字符串 2.django内部自动将随机字符串和对应的数据存储到django_session表中 2.1先在内存中产生操作数据的缓存 2.2在响应结果django中间件的时候才真正的操作数据库 3.将产生的随机字符串返回给客户端浏览器保存 """ return HttpResponse('嘿嘿嘿') def get_session(request): # print(request.session.get('hobby')) if request.session.get('hobby'): print(request.session) print(request.session.get("hobby")) print(request.session.get("hobby1")) """ 内部发送了那些事 1.自动从浏览器请求中获取sessionid对应的随机字符串 2.拿着该随机字符串去django_session表中查找对应的数据 3. 如果比对上了 则将对应的数据取出并以字典的形式封装到request.session中 如果比对不上 则request.session.get()返回的是None """ return HttpResponse("哈哈哈") return HttpResponse("大爷 关门了 ") def del_session(request): request.session.delete() return HttpResponse('删喽') from django.views import View from django.utils.decorators import method_decorator """ CBV中django不建议你直接给类的方法加装饰器 无论该装饰器能都正常给你 都不建议直接加 """ @method_decorator(login_auth,name='get') # 方式2(可以添加多个针对不同的方法加不同的装饰器) @method_decorator(login_auth,name='post') class MyLogin(View): @method_decorator(login_auth) # 方式3:它会直接作用于当前类里面的所有的方法 def dispatch(self, request, *args, **kwargs): return super().dispatch(request,*args,**kwargs) # @method_decorator(login_auth) # 方式1:指名道姓 def get(self,request): return HttpResponse("get请求") def post(self,request): return HttpResponse('post请求')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
* forms组件源码 ```python # 入口:form_obj.is_valid() # 校验字段和钩子函数的执行 # 报错提示 其实可以有两种方式(针对局部钩子函数) 1.self.add_error() 2.raise ValidationError() """ python源码里面使用最频繁的其实就是反射 """ ``` * cookie与session ```python # 由于http协议是无状态的 # cookie概念 服务端设置保存在客户端浏览器上的键值对(只要符合前面的定义都可以叫cookie) cookie虽然是服务端设置的但是浏览器可以选择不保存 # session概念 存储在服务端上的键值对(用来标识当前用户) 需要基于cookie才能工作 其实大部分的保存状态的实现都需要基于cookie来做 # 在web领域没有绝对的安全 基本上防御措施都需要程序员自己写代码完善,并且之内完善没法杜绝 ``` * django操作cookie ```python # 需要借助于HttpResponse对象 # 设置cookie obj.set_cookie(key,value) # 超时时间 obj.set_cookie(key,value,max_age/expires) expires 针对IE需要用这个参数 数字是以秒为单位 # 加盐 obj.set_signed_cookie(key,value,salt='盐') # 获取 request.COOKIES.get(key) request.get_signed_cookie(key,salt='盐') # 删除 obj.delete_cookie(key) """ 校验用户是否登陆才能访问视图函数的装饰器 能够记录用户在没有登陆之前想要访问的页面,登陆之后跳转到对应的页面 request.path request.path_info request.get_full_path() """ ``` * django操作session ```python """ 1.session是存储在服务端的 django默认情况下是需要借助于django_session表来存储数据 也就意味着如果你想要操作session那么必须先执行数据库迁移命令让django先把django_session表创建出来(no such table:django_session) 2.django默认的session过期时间是14天 3.session存储在服务端 可以有很多地方存储 1.表 2.文件 3.缓存 4.其他 ... """ # 设置 request.session[key] = value """三件事 #设置session if request.COOKIE.get("sessionid"): 更新 在django—session表中创建一条记录: session-key session-data ltv8zy1kh5lxj1if1fcs2pqwodumr45t 更新数据 else: 1 生成随机字符串 ltv8zy1kh5lxj1if1fcs2pqwodumr45t 2 通过cookie发送给客户端 response.set_cookie("sessionid",ltv8zy1kh5lxj1if1fcs2pqwodumr45t) 3 服务端保存 在django—session表中创建一条记录: session-key session-data ltv8zy1kh5lxj1if1fcs2pqwodumr45t {"is_login":True,"username":"yuan"} { 随机字符串1: {'username':'bubu','email':x''...} } """ # 获取 request.session.get(key) """三件事 # 获取session: 1 获取客户端cookie中的随机字符串 request.COOKIE.get("sessionid") # ltv8zy1kh5lxj1if1fcs2pqwodumr45t 2 去session中查找有没有随机字符 django-session表中查找记录: session-key session-data ltv8zy1kh5lxj1if1fcs2pqwodumr45t {"is_login":True,"username":"yuan"} obj=django—session.objects .filter(session-key=ltv8zy1kh5lxj1if1fcs2pqwodumr45t).first() 3 去session对应key的value中查看是否有 is_login obj.session-data.get("is_login") """ # 删除 request.session.delete() # 删除当前会话的所有Session数据(只删数据库) request.session.flush() # 全部删除(完成上面3步) 推荐使用 # 设置超时时间 request.session.set_expiry() 1.数字 秒数 2.datetime/timedelta格式 日期格式 3.None 参加全局失效策略 4.0 窗口关闭即失效 ``` * CBV如何添加装饰器 ```python """ django针对CBV添加装饰器需要你导入一个模块 """ from django.utils.decorators import method_decorator # 第一种 class MyCBV(View): def get(self,request): return HttpResponse() @method_decorator(login_auth) def post(self,request): return HttpResponse() # 第二种 @method_decorator(login_auth,name='post') @method_decorator(index_de,name='get') class MyCBV(View): def get(self,request): return HttpResponse() def post(self,request): return HttpResponse() # 第三种 class MyCBV(View): @method_decorator(login_auth) def dispatch(self,request,*args,**kwargs): """ 看CBV源码可以得出 CBV里面所有的方法在执行之前都需要先经过 dispatch方法(该方法你可以看成是一个分发方法) """ super().dispatch(request,*args,**kwargs) def get(self,request): return HttpResponse() def post(self,request): return HttpResponse() ``` * django中间件 首先django自带七个中间件,每个中间件都有各自对应的功能 并且django还支持程序员自定义中间件 你在用django开发项目的项目的时候,只要是涉及到全局相关的功能都可以使用中间件方便的完成 * 全局用户身份校验 * 全局用户权限校验(补充) * 全局访问频率校验 * ... * 基于django中间件一个重要的变成思想(补充) * csrf跨站请求伪造 * auth模块/settings源码(看时间来定) ### django中间件 ```python """ django中间件是django的门户 1.请求来的时候需要先经过中间件才能到达真正的django后端 2.响应走的时候最后也需要经过中间件才能发送出去 django自带七个中间件 """ django请求生命周期流程图 研究django中间件代码规律 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', ] class SessionMiddleware(MiddlewareMixin): def process_request(self, request): session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME) request.session = self.SessionStore(session_key) def process_response(self, request, response): return response class CsrfViewMiddleware(MiddlewareMixin): def process_request(self, request): csrf_token = self._get_token(request) if csrf_token is not None: # Use same token next time. request.META['CSRF_COOKIE'] = csrf_token def process_view(self, request, callback, callback_args, callback_kwargs): return self._accept(request) def process_response(self, request, response): return response class AuthenticationMiddleware(MiddlewareMixin): def process_request(self, request): request.user = SimpleLazyObject(lambda: get_user(request)) """ django支持程序员自定义中间件并且暴露给程序员五个可以自定义的方法 1.必须掌握 process_request process_response 2.了解即可 process_view process_template_response process_exception """ ``` ### 如何自定义中间件 ```python """ 1.在项目名或者应用名下创建一个任意名称的文件夹 2.在该文件夹内创建一个任意名称的py文件 3.在该py文件内需要书写类(这个类必须继承MiddlewareMixin) 然后在这个类里面就可以自定义五个方法了 (这五个方法并不是全部都需要书写,用几个写几个) 4.需要将类的路径以字符串的形式注册到配置文件中才能生效 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', '你自己写的中间件的路径1', '你自己写的中间件的路径2', '你自己写的中间件的路径3', ] """ """ 1.必须掌握 process_request 1.请求来的时候需要经过每一个中间件里面的process_request方法 结果的顺序是按照配置文件中注册的中间件从上往下的顺序依次执行 2.如果中间件里面没有定义该方法,那么直接跳过执行下一个中间件 3.如果该方法返回了HttpResponse对象,那么请求将不再继续往后执行 而是直接原路返回(校验失败不允许访问...) process_request方法就是用来做全局相关的所有限制功能 process_response 1.响应走的时候需要结果每一个中间件里面的process_response方法 该方法有两个额外的参数request,response 2.该方法必须返回一个HttpResponse对象 1.默认返回的就是形参response 2.你也可以自己返回自己的 3.顺序是按照配置文件中注册了的中间件从下往上依次经过 如果你没有定义的话 直接跳过执行下一个 研究如果在第一个process_request方法就已经返回了HttpResponse对象,那么响应走的时候是经过所有的中间件里面的process_response还是有其他情况 是其他情况 就是会直接走同级别的process_reponse返回 flask框架也有一个中间件但是它的规律 只要返回数据了就必须经过所有中间件里面的类似于process_reponse方法 2.了解即可 process_view 路由匹配成功之后执行视图函数之前,会自动执行中间件里面的该放法 顺序是按照配置文件中注册的中间件从上往下的顺序依次执行 process_template_response 返回的HttpResponse对象有render属性的时候才会触发 顺序是按照配置文件中注册了的中间件从下往上依次经过 process_exception 当视图函数中出现异常的情况下触发 顺序是按照配置文件中注册了的中间件从下往上依次经过 """ ``` ### csrf跨站请求伪造 ```python """ 钓鱼网站 我搭建一个跟正规网站一模一样的界面(中国银行) 用户不小心进入到了我们的网站,用户给某个人打钱 打钱的操作确确实实是提交给了中国银行的系统,用户的钱也确确实实减少了 但是唯一不同的时候打钱的账户不适用户想要打的账户变成了一个莫名其妙的账户 大学英语四六级 考之前需要学生自己网站登陆缴费 内部本质 我们在钓鱼网站的页面 针对对方账户 只给用户提供一个没有name属性的普通input框 然后我们在内部隐藏一个已经写好name和value的input框 如何规避上述问题 csrf跨站请求伪造校验 网站在给用户返回一个具有提交数据功能页面的时候会给这个页面加一个唯一标识 当这个页面朝后端发送post请求的时候 我的后端会先校验唯一标识,如果唯一标识不对直接拒绝(403 forbbiden)如果成功则正常执行 """ ``` ### 如何符合校验 ```python # form表单如何符合校验 <form action="" method="post"> {% csrf_token %} <p>username:<input type="text" name="username"></p> <p>target_user:<input type="text" name="target_user"></p> <p>money:<input type="text" name="money"></p> <input type="submit"> </form> # ajax如何符合校验 // 第一种 利用标签查找获取页面上的随机字符串 {#data:{"username":'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},#} // 第二种 利用模版语法提供的快捷书写 {#data:{"username":'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},#} // 第三种 通用方式直接拷贝js代码并应用到自己的html页面上即可 data:{"username":'jason'} ``` ```js 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'); 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); } } }); ``` ### csrf相关装饰器 ```python """ 1.网站整体都不校验csrf,就单单几个视图函数需要校验 2.网站整体都校验csrf,就单单几个视图函数不校验 """ from django.views.decorators.csrf import csrf_protect,csrf_exempt from django.utils.decorators import method_decorator """ csrf_protect 需要校验 针对csrf_protect符合我们之前所学的装饰器的三种玩法 csrf_exempt 忽视校验 针对csrf_exempt只能给dispatch方法加才有效 """ # @csrf_exempt # @csrf_protect def transfer(request): if request.method == 'POST': username = request.POST.get('username') target_user = request.POST.get('target_user') money = request.POST.get('money') print('%s给%s转了%s元'%(username,target_user,money)) return render(request,'transfer.html') from django.views import View # @method_decorator(csrf_protect,name='post') # 针对csrf_protect 第二种方式可以 # @method_decorator(csrf_exempt,name='post') # 针对csrf_exempt 第二种方式不可以 @method_decorator(csrf_exempt,name='dispatch') class MyCsrfToken(View): # @method_decorator(csrf_protect) # 针对csrf_protect 第三种方式可以 # @method_decorator(csrf_exempt) # 针对csrf_exempt 第三种方式可以 def dispatch(self, request, *args, **kwargs): return super(MyCsrfToken, self).dispatch(request,*args,**kwargs) def get(self,request): return HttpResponse('get') # @method_decorator(csrf_protect) # 针对csrf_protect 第一种方式可以 # @method_decorator(csrf_exempt) # 针对csrf_exempt 第一种方式不可以 def post(self,request): return HttpResponse('post') ``` ### 补充知识点 ```python # 模块:importlib import importlib res = 'myfile.b' ret = importlib.import_module(res) # from myfile import b # 该方法最小只能到py文件名 print(ret) ``` ### 重要思想 ```python import settings import importlib def send_all(content): for path_str in settings.NOTIFY_LIST: #'notify.email.Email' module_path,class_name = path_str.rsplit('.',maxsplit=1) # module_path = 'notify.email' class_name = 'Email' # 1 利用字符串导入模块 module = importlib.import_module(module_path) # from notify import email # 2 利用反射获取类名 cls = getattr(module,class_name) # Email、QQ、Wechat # 3 生成类的对象 obj = cls() # 4 利用鸭子类型直接调用send方法 obj.send(content) ```
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.shortcuts import render,HttpResponse # Create your views here. def index(request): print('我是视图函数index') obj = HttpResponse('index') def render(): print('内部的render') return HttpResponse("O98K") obj.render = render return obj from django.views.decorators.csrf import csrf_protect,csrf_exempt from django.utils.decorators import method_decorator """ csrf_protect 需要校验 针对csrf_protect符合我们之前所学的装饰器的三种玩法 csrf_exempt 忽视校验 针对csrf_exempt只能给dispatch方法加才有效 """ # @csrf_exempt # @csrf_protect def transfer(request): if request.method == 'POST': username = request.POST.get('username') target_user = request.POST.get('target_user') money = request.POST.get('money') print('%s给%s转了%s元'%(username,target_user,money)) return render(request,'transfer.html') from django.views import View # @method_decorator(csrf_protect,name='post') # 针对csrf_protect 第二种方式可以 # @method_decorator(csrf_exempt,name='post') # 针对csrf_exempt 第二种方式不可以 @method_decorator(csrf_exempt,name='dispatch') class MyCsrfToken(View): # @method_decorator(csrf_protect) # 针对csrf_protect 第三种方式可以 # @method_decorator(csrf_exempt) # 针对csrf_exempt 第三种方式可以 def dispatch(self, request, *args, **kwargs): return super(MyCsrfToken, self).dispatch(request,*args,**kwargs) def get(self,request): return HttpResponse('get') # @method_decorator(csrf_protect) # 针对csrf_protect 第一种方式可以 # @method_decorator(csrf_exempt) # 针对csrf_exempt 第一种方式不可以 def post(self,request): return HttpResponse('post')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" ├── test ├── start.py ├── settings.py ├── notify/ ├── __init__.py ├── email.py ├── msg.py ├── qq.py └── wechat.py """ #####start.py import notify notify.send_all('快下课了') #####settings.py NOTIFY_LIST = [ 'notify.email.Email', 'notify.qq.QQ', 'notify.wechat.Wechat', # 'notify.msg.Msg', ] #####notify/__init__.py import settings import importlib def send_all(content): for path_str in settings.NOTIFY_LIST: #'notify.email.Email' module_path,class_name = path_str.rsplit('.',maxsplit=1) # module_path = 'notify.email' class_name = 'Email' # 1 利用字符串导入模块 module = importlib.import_module(module_path) # from notify import email # 2 利用反射获取类名 cls = getattr(module,class_name) # Email、QQ、Wechat # 3 生成类的对象 obj = cls() # 4 利用鸭子类型直接调用send方法 obj.send(content) #####notify/email.py class Email(object): def __init__(self): pass # 发送邮箱需要做的前期准备工作 def send(self, content): print('邮箱通知:%s' % content) #####notify/msg.py class Msg(object): def __init__(self): pass def send(self,content): print('短信通知:%s'%content) #####notify/qq.py class QQ(object): def __init__(self): pass # 发送qq需要做的前期准备工作 def send(self, content): print('qq通知:%s' % content) #####notify/wechat.py class Wechat(object): def __init__(self): pass # 发送微信需要做的前期准备工作 def send(self,content): print('微信通知:%s'%content) ############ 正常思路 """ ├── test ├── notify.py ├── start.py """ ###########notify.py def wechat(content): print('微信通知:%s'%content) def qq(content): print('qq通知:%s'%content) def email(content): print('邮箱通知:%s'%content) ###########start.py from notify import * def send_all(content): wechat(content) qq(content) email(content) if __name__ == '__main__': send_all('啥时候放长假')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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'); 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); } } });
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> <h1>我是正儿八经的网站</h1> <form action="/csrf/" method="post"> {# {% csrf_token %}#} <p>username:<input type="text" name="username"></p> <p>target_user:<input type="text" name="target_user"></p> <p>money:<input type="text" name="money"></p> <input type="submit"> </form> <button id="d1">ajax请求</button> {% load static %} <script src="{% static 'js/mysetup.js' %}"></script> <script> $('#d1').click(function () { $.ajax({ url:'', type:'post', // 第一种 利用标签查找获取页面上的随机字符串 {#data:{"username":'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},#} // 第二种 利用模版语法提供的快捷书写 {#data:{"username":'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},#} // 第三种 通用方式直接拷贝js代码并应用到自己的html页面上即可 data:{"username":'jason'}, success:function () { } }) }) </script> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
###### settings.py # 全局配置:没有登陆跳转到指定的页面 LOGIN_URL = '/login/' # auth表配置 AUTH_USER_MODEL = 'app01.UserInfo' ###### models.py from django.db import models from django.contrib.auth.models import User,AbstractUser # Create your models here. # 第一种:一对一关系 不推荐 # class UserDetail(models.Model): # phone = models.BigIntegerField() # user = models.OneToOneField(to='User') # 第二种:面向对象的继承 class UserInfo(AbstractUser): """ 如果继承了AbstractUser 那么在执行数据库迁移命令的时候auth_user表就不会再创建出来了 而UserInfo表中会出现auth_user所有的字段外加自己扩展的字段 这么做的好处在于你能够直接点击你自己的表更加快速的完成操作及扩展 前提: 1.在继承之前没有执行过数据库迁移命令 auth_user没有被创建,如果当前库已经创建了那么你就重新换一个库 2.继承的类里面不要覆盖AbstractUser里面的字段名 表里面有的字段都不要动,只扩展额外字段即可 3.需要在配置文件中告诉django你要用UserInfo替代auth_user AUTH_USER_MODEL = 'app01.UserInfo' '应用名.表名' """ phone = models.BigIntegerField() create_time = models.DateTimeField(auto_now_add=True) ###### urls.py from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), # 登陆功能 url(r'^login/',views.login), # 校验用户是否登陆 url(r'^home/',views.home), url(r'^index/',views.index), # 修改密码 url(r'^set_password/',views.set_password), # 注销功能 url(r'^logout/',views.logout), # 注册功能 url(r'^register/',views.register) ] ###### views.py from django.shortcuts import render,redirect,HttpResponse from django.contrib import auth # Create your views here. """ 使用auth模块要用就用全套 """ def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # 去用户表中校验数据 # 1.表如何获取 # 2.密码如何比对 user_obj = auth.authenticate(request,username=username,password=password) # print(user_obj) # 用户对象 jason 数据不符合则返回None # print(user_obj.username) # jason # print(user_obj.password) # pbkdf2_sha256$36000$zeNDf8CkZj7y$b+e/CjzZoAnbBIpvUWgz25ybBDqDzRTmYAHPytxqRYQ= # 判断当前用户是否存在 if user_obj: # 保存用户状态 auth.login(request,user_obj) # 类似于request.session[key] = user_obj # 主要执行了该方法 你就可以在任何地方通过request.user获取到当前登陆的用户对象 return redirect('/home/') """ 1.自动查找auth_user标签 2.自动给密码加密再比对 该方法注意事项 括号内必须同时传入用户名和密码 不能只传用户名(一步就帮你筛选出用户对象) """ return render(request,'login.html') from django.contrib.auth.decorators import login_required # @login_required(login_url='/login/') # 局部配置:用户没有登陆跳转到login_user后面指定的网址 # @login_required # 全局配置 # @login_required(login_url='/xxx/') # 优先级 局部 > 全局 @login_required def home(request): """用户登陆之后才能看home""" print(request.user) # 用户对象 AnonymousUser匿名用户 # 判断用户是否登陆 print(request.user.is_authenticated()) # 自动去django_session里面查找对应的用户对象给你封装到request.user中 return HttpResponse('home') """ 1.如果局部和全局都有 该听谁的? 局部 > 全局 2.局部和全局哪个好呢? 全局的好处在于无需重复写代码 但是跳转的页面却很单一 局部的好处在于不同的视图函数在用户没有登陆的情况下可以跳转到不同的页面 """ # @login_required(login_url='/login/') @login_required def index(request): return HttpResponse('index') @login_required def set_password(request): if request.method == 'POST': old_password = request.POST.get('old_password') new_password = request.POST.get('new_password') confirm_password = request.POST.get('confirm_password') # 先校验两次密码是否一致 if new_password == confirm_password: # 校验老密码对不对 is_right = request.user.check_password(old_password) # 自己加码比对密码 if is_right: # 修改密码 request.user.set_password(new_password) # 仅仅是在修改对象的属性 request.user.save() # 这一步才是真正的操作数据库 return redirect('/login/') return render(request,'set_password.html',locals()) @login_required def logout(request): auth.logout(request) # 类似于request.session.flush() return redirect('/login/') from app01 import models from django.contrib.auth.models import User def register(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # 操作auth_user表写入数据 # User.objects.create(username=username,password=password) # 写入数据 不能用create 密码没有加密处理 # 创建普通用户 # User.objects.create_user(username=username,password=password) # 创建超级用户(了解):使用代码创建超级用户 邮箱是必填的 而用命令创建则可以不填 User.objects.create_superuser(username=username,email='123@qq.com',password=password) return render(request,'register.html')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
* django请求生命周期流程图 * django中间件 ```python """ django中间件类似于django的保安 1.请求来的时候需要先经过中间件才能到达urls.py继续匹配 2.响应走的时候最后也需要经过中间件才能真正离开django后端 django中间件能够做的事情 只要是涉及到项目全局的功能,你一定要想到中间件 1.全局身份校验 2.全局访问频率校验 3.全局权限校验 ... django默认有七个中间件(每个中间件其实就类似于一块独立的功能) django除了有自带的七个之外还支持用户自定义中间件并且暴露给用户五个可以自定义的方法 """ # 如何自定义中间件 1.在应用或者项目下创建一个任意名称的文件夹 2.在该文件夹内创建任意名称的py文件 3.在py文件内书写类 这个类需要继承所有中间件类都继承的MiddlewareMixin 4.需要在配置文件中书写类的完整路径 # 需要掌握的两个 1.process_request(self,request) 1.请求来的时候会按照配置文件中注册的中间件从上往下依次经过每一个中间件里面的该方法,如果没有定义直接跳过 2.该方法也可以自己返回HttpResponse对象,一旦返回则请求不再继续往后执行直接原路返回(可以借助于该方法实现很多限制校验功能) 2.process_response(self,request,response) 1.响应走的时候会按照配置文件中注册的中间件从下往上依次经过每一个中间件里面的该方法,如果没有定义直接跳过 2.形参response就是返回给前端浏览器的内容,也就意味着该方法要么将response返回要么自己返回一个HttpResponse对象 # 只要是形参中带有response的方法,那么该方法就必须返回response或者HttpResponse对象 """ 注意 当process_request返回HttpResponse对象之后会直接走同级别的process_response而不会将所有的process_response都走一遍 但是在flask框架也有类似的中间件 但是它的特点只要返回响应就必须将所有类似于process_response功能方法全部走一遍 """ # 了解即可的三个 1.process_view(self,view_name,*args,**kwargs) 路由匹配成功之后执行视图函数之前 2.process_exception(self,request,exception) 当后端视图函数出现报错的时候 3.process_template_response(self,request,response) 返回的HttpResponse对象必须含有render属性 ``` * csrf跨站请求伪造 ```python """ 钓鱼网站 本质: 用假网站代替真网站,获取用户数据朝真网站提交,中途修改一些参数 eg:网页银行转账的例子 解决办法: 在给用户返回的具有可以提交post请求的页面上添加一个唯一标记 之后该页面发送post请求到后端,会先校验该唯一标记 """ # form表单如何获取 在form表单内书写{% csrf_token %} <input type='hidden' name='csrfmiddlewaretoken' value='唯一的随机字符串'/> # ajax如何如何 1.自己利用标签查找获取值 data:{'csrfmiddlewaretoken':'$("[name=csrfmiddlewaretoken]").val()'} 2.利用模版语法 data:{'csrfmiddlewaretoken':'{{ csrf_token }}'} 3.通用的 js文件 拷贝固定的js代码导入到你的html页面上即可 ``` * csrf相关装饰器 ```python """ from django.views.decorators.csrf import csrf_protect,csrf_exempt csrf_protect:需要校验csrf 跟我们之前学习的三种给CBV加装饰器的玩法一样 csrf_exempt:忽略校验csrf 只能作用于dispatch方法 他们两个在FBV上使用方式一致,就是普通的装饰器玩法 而在CBV上两者却有区别 """ ``` * 参考django中间件功能设计扩展知识 ```python # 模块 importlib 能够以字符串的形式帮助你导入模块,但是需要注意最小单位只能到模块名 # 功能的插拔式设计 1.配置文件注册功能 2.importlib模块 3.字符串切割split 4.反射 5.面向对象及鸭子类型 # 一定要总结吸收该思想 ``` # 今日内容概要 * 权限管理(RBAC) 你可以借助于django中间件实现网站的权限校验(课外兴趣扩展) * Auth模块 只要是跟用户相关的登陆、注册、校验、修改密码、注销、验证用户是否登陆 * BBS(仿博客园小作业) 项目开发流程 表设计(七张表) 你们回去之后好好的把这七张关系捋一捋 # 今日内容详细 ### Auth模块 #### settings.py中配置 ```python # 全局配置:没有登陆跳转到指定的页面 LOGIN_URL = '/login/' # auth表配置 AUTH_USER_MODEL = 'app01.UserInfo' ``` ```python """ 其实我们在创建好一个django项目之后直接执行数据库迁移命令会自动生成很多表 django_session auth_user django在启动之后就可以直接访问admin路由,需要输入用户名和密码,数据参考的就是auth_user表,并且还必须是管理员用户才能进入 创建超级用户(管理员) python3 manage.py createsuperuser 依赖于auth_user表完成用户相关的所有功能 """ ``` ### 方法总结 ```python # 1.比对用户名和密码是否正确 user_obj = auth.authenticate(request,username=username,password=password) # 括号内必须同时传入用户名和密码 print(user_obj) # 用户对象 jason 数据不符合则返回None print(user_obj.username) # jason print(user_obj.password) # 密文 # 2.保存用户状态 auth.login(request,user_obj) # 类似于request.session[key] = user_obj # 主要执行了该方法 你就可以在任何地方通过request.user获取到当前登陆的用户对象 # 3.判断当前用户是否登陆 request.user.is_authenticated() # 4.获取当前登陆用户 request.user # 5.校验用户是否登陆装饰器 from django.contrib.auth.decorators import login_required # 局部配置 @login_required(login_url='/login/') # 全局配置 LOGIN_URL = '/login/' 1.如果局部和全局都有 该听谁的? 局部 > 全局 2.局部和全局哪个好呢? 全局的好处在于无需重复写代码 但是跳转的页面却很单一 局部的好处在于不同的视图函数在用户没有登陆的情况下可以跳转到不同的页面 # 6.比对原密码 request.user.check_password(old_password) # 7.修改密码 request.user.set_password(new_password) # 仅仅是在修改对象的属性 request.user.save() # 这一步才是真正的操作数据库 # 8.注销 auth.logout(request) # 9.注册 # 操作auth_user表写入数据 User.objects.create(username=username,password=password) # 写入数据 不能用create 密码没有加密处理 # 创建普通用户 User.objects.create_user(username=username,password=password) # 创建超级用户(了解):使用代码创建超级用户 邮箱是必填的 而用命令创建则可以不填 User.objects.create_superuser(username=username,email='123@qq.com',password=password) ``` ### 如何扩展auth_user表 ```python from django.db import models from django.contrib.auth.models import User,AbstractUser # Create your models here. # 第一种:一对一关系 不推荐 # class UserDetail(models.Model): # phone = models.BigIntegerField() # user = models.OneToOneField(to='User') # 第二种:面向对象的继承 class UserInfo(AbstractUser): """ 如果继承了AbstractUser 那么在执行数据库迁移命令的时候auth_user表就不会再创建出来了 而UserInfo表中会出现auth_user所有的字段外加自己扩展的字段 这么做的好处在于你能够直接点击你自己的表更加快速的完成操作及扩展 前提: 1.在继承之前没有执行过数据库迁移命令 auth_user没有被创建,如果当前库已经创建了那么你就重新换一个库 2.继承的类里面不要覆盖AbstractUser里面的字段名 表里面有的字段都不要动,只扩展额外字段即可 3.需要在配置文件中告诉django你要用UserInfo替代auth_user(******) AUTH_USER_MODEL = 'app01.UserInfo' '应用名.表名' """ phone = models.BigIntegerField() """ 你如果自己写表替代了auth_user那么 auth模块的功能还是照常使用,参考的表页由原来的auth_user变成了UserInfo """ ``` ### 项目开发流程 ```python # 1.需求分析 架构师+产品经理+开发者组长 在跟客户谈需求之前,会大致先了解客户的需求,然后自己先设计一套比较好写方案 在跟客户沟通交流中引导客户往我们之前想好的方案上面靠 形成一个初步的方案 # 2.项目设计 架构师干的活 编程语言选择 框架选择 数据库选择 主库:MySQL,postgreSQL,... 缓存数据库:redis、mongodb、memcache... 功能划分 将整个项目划分成几个功能模块 找组长开会 给每个组分发任务 项目报价 技术这块需要多少人,多少天(一个程序员一天1500~2000计算(大致)) 产品经理公司层面 再加点钱 公司财务签字确认 公司老板签字确认 产品经理去跟客户沟通 后续需要加功能 继续加钱 # 3.分组开发 组长找组员开会,安排各自功能模块 我们其实就是在架构师设计好的框架里面填写代码而已(码畜) 我们在写代码的时候 写完需要自己先测试是否有bug 如果是一些显而易见的bug,你没有避免而是直接交给了测试部门测出来 那你可能就需要被扣绩效了(一定要跟测试小姐姐搞好关系) 薪资组成 15K(合理合规合法的避税) 底薪 10K 绩效 3K 岗位津贴 1K 生活补贴 1K # 4.测试 测试部门测试你的代码 压力测试 ... # 5.交付上线 1.交给对方的运维人员 2.直接上线到我们的服务器上 收取维护费用 3.其他... ``` ### 表设计 ```python """ 一个项目中最最最重要的不是业务逻辑的书写 而是前期的表设计,只要将表设计好了,后续的功能书写才会一帆风顺 bbs表设计 1.用户表 继承AbstractUser 扩展 phone 电话号码 avatar 用户头像 create_time 创建时间 外键字段 一对一个人站点表 2.个人站点表 site_name 站点名称 site_title 站点标题 site_theme 站点样式 3.文章标签表 name 标签名 外键字段 一对多个人站点 4.文章分类表 name 分类名 外键字段 一对多个人站点 5.文章表 title 文章标题 desc 文章简介 content 文章内容 create_time 发布时间 数据库字段设计优化(******) (虽然下述的三个字段可以从其他表里面跨表查询计算得出,但是频繁跨表效率) up_num 点赞数 down_num 点踩数 comment_num 评论数 外键字段 一对多个人站点 多对多文章标签 一对多文章分类 6.点赞点踩表 记录哪个用户给哪篇文章点了赞还是点了踩 user ForeignKey(to="User") article ForeignKey(to="Article") is_up BooleanField() 1 1 1 1 2 1 1 3 0 2 1 1 7.文章评论表 记录哪个用户给哪篇文章写了哪些评论内容 user ForeignKey(to="User") article ForeignKey(to="Article") content CharField() comment_time DateField() # 自关联 parent ForeignKey(to="Comment",null=True) # ORM专门提供的自关联写法 parent ForeignKey(to="self",null=True) id user_id article_id parent_id 1 1 1 2 2 1 1 根评论子评论的概念 根评论就是直接评论当前发布的内容的 子评论是评论别人的评论 1.PHP是世界上最牛逼的语言 1.1 python才是最牛逼的 1.2 java才是 根评论与子评论是一对多的关系 """ ```