多对多三种创建方式
1.全自动创建
class Book(models.Model): title = models.CharField(max_length=32) authors = models.ManyToManyField(to='Author') class Author(models.Model): name = models.CharField(max_length=32)
优势: 自动创建第三张表,并且提供了add、remove、set、clear四种操作
劣势: 第三张表无法创建更多的字段 扩展性较差
2.纯手动创建
class Book(models.Model): title = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') others = models.CharField(max_length=32) join_time = models.DateField(auto_now_add=True)
优势: 第三张表完全由自己创建,扩展性强
劣势: 编写繁琐 并且不再支持add、remove、set、clear以及正反向概念
3.半自动创建
class Book(models.Model): title = models.CharField(max_length=32) authors = models.ManyToManyField(to='Author',
"""告诉ORM,书与作者之间通过Book2Author建立外键关系,通过book和author两个字段来维护,不需要再另外创建第三张表"""
through='Book2Author',
through_fields=('book','author') )
class Author(models.Model):
name = models.CharField(max_length=32)
class Book2Author(models.Model):
book = models.ForeignKey(to='Book', on_delete=models.CASCADE)
author = models.ForeignKey(to='Author', on_delete=models.CASCADE)
others = models.CharField(max_length=32)
join_time = models.DateField(auto_now_add=True)
优势: 第三张表完全由自己创建,扩展性强,正反向概念依然清晰可用
劣势: 编写繁琐不再支持add、remove、set、clear
django内置序列化组件(drf前身)
from app01 import models from django.http import JsonResponse def ab_ser_func(request): # 1.查询所有的书籍对象 book_queryset = models.Book.objects.all() # queryset [对象、对象] # 2.封装成大字典返回 data_dict = {} for book_obj in book_queryset: temp_dict = {} temp_dict['pk'] = book_obj.pk temp_dict['title'] = book_obj.title temp_dict['price'] = book_obj.price temp_dict['info'] = book_obj.info data_dict[book_obj.pk] = temp_dict # {1:{},2:{},3:{},4:{}} return JsonResponse(data_dict)
前端查后端接口的网站:https://www.bejson.com/
数据序列化
把数据转成json格式大字典。
Django内置的serializers
- 导入组件
from django.core import serializers
- 组件使用/参数介绍
serializers.serialize('json', user_queryset) # 第一个参数是指定需要序列化的类型 # 第二个参数是指定需要序列化的数据 # 返回的结果是一个列表套字典格式的序列化之后的数据
代码示例
from django.core import serializers def dj_json(request): user_list = models.User.objects.all() res = serializers.serialize("json",user_list) return HttpResponse(res)
- 显示结果
从上面的结果我们可以发现 :
序列化器是将对象转成格式字符串, 但字段不能控制, 现阶段我们如果要做序列化, 最好使用for循环+列表套字典; 但这个组件在后面drf中会使用到。
使用for循环+列表套字典做序列化
def for_json(request): user_list = models.User.objects.all() l = [] for obj in user_list: l.append({'id':obj.id,'name':obj.name,'pwd':obj.pwd}) return JsonResponse(l, safe=False)
- 显示结果
字段可控, 比较清晰
批量操作数据
需求:先创建一张书籍表,往书籍表中插入10万条数据
方法一:
# models.py
from django.db import models class Books(models.Model): title = models.CharField(max_length=32)
# urls.py
from app01 import views urlpatterns = [ path('ab_bk/',views.ab_bk_func), ]
# views.py
from django.shortcuts import render from app01 import models def ab_bk_func(request): # 1.往books表中插入10万条数据 for i in range(1,100000): models.Books.objects.create(title='第%s本书' % i) # 2.查询出所有的表中数据并展示到前端页面 book_queryset = models.Books.objects.all() return render(request,'BkPage.html',locals())
# BkPage.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script> </head> <body> {% for book_obj in book_queryset %} <p>{{ book_obj.title }}</p> {% endfor %} </body> </html>
直接循环插入,速度较慢,10秒插入500条左右。
方法二:bulk_create()
# views.py
from django.shortcuts import render from app01 import models def ab_bk_func(request): book_obj_list =[] # 可以用列表生成式[...for i in...if...],或生成器表达式(...for i in...if...) for i in range(1,100000): book_obj = models.Book(title='第%s本书' % i) # 类名加括号主生对象 book_obj_list.append(book_obj) # 批量插入数据 models.Book.objects.bulk_create(book_obj_list) # 查询出所有的表中数据并展示到前端页面 book_queryset = models.Books.objects.all() return render(request,'BkPage.html',locals())
使用orm提供的批量插入操作,5s插入10万条左右。
分页器
参考博客:https://www.cnblogs.com/Dominic-Ji/articles/12035722.html
推导流程
第一步:queryset支持切片操作(正数)
# views.py
from django.shortcuts import render from app01 import models def ab_bk_func(request): book_queryset = models.Book.objects.all()[0:10] return render(request,'BkPage.html',locals())
展示结果:
第二步:研究各个参数之间的数学关系
# 每页固定展示多少条数据、起始位置、终止位置 """ per_page_num = 10 current_page start_num end_num 1 0 10 2 10 20 3 20 30 start_num = (current_page -1) * per_page_num
end_num = (current_page * per_page_num
"""
代码修改:
from django.shortcuts import render from app01 import models def ab_bk_func(request): per_page_num = 10 # 每页展示多少条数据 current_page = request.GET.get('page',1) try: current_page = int(current_page) except BaseException: current_page = 1 # 用户可能会输入一个不存在的数字,所以这里进行一个报错处理 start_num = (current_page -1) * per_page_num # 起始展示位置 end_num = current_page * per_page_num # 终止展示位置 book_queryset = models.Book.objects.all()[start_num:end_num] return render(request,'BkPage.html',locals())
结果展示:
第三步:自定义页码参数
# BkPage.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"> </head> <body> <div class="container"></div> <div class="row"></div> <h1 class="text-center">数据展示</h1> <div class="text-center"> {% for book_obj in book_queryset %} <p>{{ book_obj.title }}</p> {% endfor %} </div> <nav aria-label="Page navigation" class="text-center"> <ul class="pagination"> <li> <a href="#" aria-label="Previous"> <span aria-hidden="true">«</span> </a> </li> <li><a href="?page=1">1</a></li> <li><a href="?page=2">2</a></li> <li><a href="?page=3">3</a></li> <li><a href="?page=4">4</a></li> <li><a href="?page=5">5</a></li> <li> <a href="#" aria-label="Next"> <span aria-hidden="true">»</span> </a> </li> </ul> </nav> </body> </html>
展示结果:
第四步:前端展示分页器样式,总页码数问题
# views.py
from django.shortcuts import render from app01 import models def ab_bk_func(request): per_page_num = 10 # 每页展示多少条数据 book_queryset = models.Book.objects.all() total_count = book_queryset.count() total_pages,more = divmod(total_count,per_page_num) if more: total_pages += 1 # 由于模板语法没有range功能,我们需要循环产生很多分页标签,所以考虑后端生成传递给html页面 html_str = '' for i in range(1, total_pages + 1): html_str += '<li><a href="?page=%s">%s</a></li>'% (i, i)
current_page = request.GET.get('page',1) try: current_page = int(current_page) except BaseException: current_page = 1 # 用户可能会输入一个不存在的数字,所以这里进行一个报错处理 start_num = (current_page -1) * per_page_num # 起始展示位置 end_num = current_page * per_page_num # 终止展示位置 book_queryset = book_queryset[start_num:end_num] return render(request,'BkPage.html',locals())
# BkPage.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"> </head> <body> <div class="container"></div> <div class="row"></div> <h1 class="text-center">数据展示</h1> <div class="text-center"> {% for book_obj in book_queryset %} <p>{{ book_obj.title }}</p> {% endfor %} </div> <nav aria-label="Page navigation" class="text-center"> <ul class="pagination"> <li> <a href="#" aria-label="Previous"> <span aria-hidden="true">«</span> </a> </li> {{ html_str | safe}} <li> <a href="#" aria-label="Next"> <span aria-hidden="true">»</span> </a> </li> </ul> </nav> </body> </html>
展示结果:
第五步:前端页面页码个数渲染问题
# views.py
from django.shortcuts import render from app01 import models def ab_bk_func(request): per_page_num = 10 # 每页展示多少条数据 current_page = request.GET.get('page', 1) try: current_page = int(current_page) except BaseException: current_page = 1 # 用户可能会输入一个不存在的数字,所以这里进行一个报错处理 book_queryset = models.Book.objects.all() total_count = book_queryset.count() total_pages,more = divmod(total_count,per_page_num) if more: total_pages += 1 # 由于模板语法没有range功能,我们需要循环产生很多分布标签,所以考虑后端生成传递给html页面 html_str = '' for i in range(current_page - 5, current_page + 6): # 以选中的页码为中心,左右各展示5个页码 if current_page == i: html_str += '<li class="active"><a href="?page=%s">%s</a></li>'% (i, i) # 给选中的页面添加样式 else: html_str += '<li><a href="?page=%s">%s</a></li>' % (i, i) start_num = (current_page -1) * per_page_num # 起始展示位置 end_num = current_page * per_page_num # 终止展示位置 book_queryset = book_queryset[start_num:end_num] return render(request,'BkPage.html',locals())
展示结果:
第六问:问题优化
from django.shortcuts import render from app01 import models def ab_bk_func(request): per_page_num = 10 # 每页展示多少条数据 current_page = request.GET.get('page', 1) try: current_page = int(current_page) except BaseException: current_page = 1 # 用户可能会输入一个不存在的数字,所以这里进行一个报错处理 book_queryset = models.Book.objects.all() total_count = book_queryset.count() total_pages,more = divmod(total_count,per_page_num) if more: total_pages += 1 # 由于模板语法没有range功能,我们需要循环产生很多分布标签,所以考虑后端生成传递给html页面 html_str = '' xxx = current_page if xxx < 6: xxx = 6 # 加上此条判断,如果页码小于6的话,则都以6为中心 for i in range(xxx - 5, xxx + 6): # 以选中的页码为中心,左右各展示5个页码 if current_page == i: html_str += '<li class="active"><a href="?page=%s">%s</a></li>'% (i, i) # 给选中的页面添加样式 else: html_str += '<li><a href="?page=%s">%s</a></li>' % (i, i) start_num = (current_page -1) * per_page_num # 起始展示位置 end_num = current_page * per_page_num # 终止展示位置 book_queryset = book_queryset[start_num:end_num] return render(request,'BkPage.html',locals())
结果展示:
自定义分页器的使用
django自带分页器模块但是使用起来很麻烦,所以我们自己封装了一个。
在django项目或某个应用下创建一个目录utils(文件夹名字无所谓),在此目录下创建一个py文件mypage.py(名字无所谓),把自定义分页器代码拷贝进去即可。
自定义分页器
class Pagination(object): def __init__(self, current_page, all_count, per_page_num=2, 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)
自定义分页器的使用
# urls.py
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('ab_pg/',views.ab_pg_func), ]
# views.py
def ab_pg_func(request): book_queryset = models.Book.objects.all() from app01.utils.mypage import Pagination # 导入分页器中的类 current_page = request.GET.get('page') page_obj = Pagination(current_page=current_page,all_count=book_queryset.count()) # 产生一个对象 page_queryset = book_queryset[page_obj.start:page_obj.end] # 切片操作 return render(request,'pgPage.html',locals())
# pgPage.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"> </head> <body> {% for book_obj in page_queryset %} <p>{{ book_obj.title }}</p> {% endfor %} {{ page_obj.page_html|safe }} </body> </html>
form组件
form组件的功能
我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来。
与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息.。
Django form组件就实现了上面所述的功能。
总结一下,其实form组件的主要功能如下:
1 生成页面可用的HTML标签 2 对用户提交的数据进行校验 3 保留上次输入内容
普通方式手写注册功能
# views.py
# 注册 def register(request): error_msg = "" if request.method == "POST": username = request.POST.get("name") pwd = request.POST.get("pwd") # 对注册信息做校验 if len(username) < 6: # 用户长度小于6位 error_msg = "用户名长度不能小于6位" else: # 将用户名和密码存到数据库 return HttpResponse("注册成功") return render(request, "register.html", {"error_msg": error_msg})
# login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册页面</title> </head> <body> <form action="/reg/" method="post"> {% csrf_token %} <p> 用户名: <input type="text" name="name"> </p> <p> 密码: <input type="password" name="pwd"> </p> <p> <input type="submit" value="注册"> <p style="color: red">{{ error_msg }}</p> </p> </form> </body> </html>
使用form组件实现注册功能
# views.py
先定义好一个RegForm类:
from django import forms # 按照Django form组件的要求自己写一个类 class RegForm(forms.Form): name = forms.CharField(label="用户名") pwd = forms.CharField(label="密码")
再写一个视图函数:
# 使用form组件实现注册方式 def register2(request): form_obj = RegForm() if request.method == "POST": # 实例化form对象的时候,把post提交过来的数据直接传进去 form_obj = RegForm(request.POST) # 调用form_obj校验数据的方法 if form_obj.is_valid(): return HttpResponse("注册成功") return render(request, "register2.html", {"form_obj": form_obj})
# login2.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册2</title> </head> <body> <form action="/reg2/" method="post" novalidate autocomplete="off"> {% csrf_token %} <div> <label for="{{ form_obj.name.id_for_label }}">{{ form_obj.name.label }}</label> {{ form_obj.name }} {{ form_obj.name.errors.0 }} </div> <div> <label for="{{ form_obj.pwd.id_for_label }}">{{ form_obj.pwd.label }}</label> {{ form_obj.pwd }} {{ form_obj.pwd.errors.0 }} </div> <div> <input type="submit" class="btn btn-success" value="注册"> </div> </form> </body> </html>
看网页效果发现 也验证了form的功能:
• 前端页面是form类的对象生成的 -->生成HTML标签功能 • 当用户名和密码输入为空或输错之后 页面都会提示 -->用户提交校验功能 • 当用户输错之后 再次输入 上次的内容还保留在input框 -->保留上次输入内容
标签:num,form,models,html,current,book,组件,序列化,page From: https://www.cnblogs.com/chen-ao666/p/16995262.html