Django操作之ORM与自定制分页
一.创建表
Django自带数据库ORM是基于SQLite,如果使用sqlite则不需要做配置修改,如果需要基于mysql连接则需要做相关配置:
1.在安装好的mysql中创建数据库,注意字符集选择utf8以便支持汉字(ORM不支持创建数据库,只能创建表)
2.在创建的Django项目同名文件夹中 setting.py 配置对于mysql数据库的连接:
1 # 1.将默认的设置注释掉: 2 # DATABASES = { 3 # 'default': { 4 # 'ENGINE': 'django.db.backends.sqlite3', 5 # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 6 # } 7 # } 8 9 # 2.设置对于mysql的连接: 10 DATABASES = { 11 'default': { 12 'ENGINE': 'django.db.backends.mysql', 13 'NAME':'new', #数据库名称 14 'USER': 'root', #用户名 15 'PASSWORD': 'pwd', #密码 16 'HOST': 'localhost', #连接地址,可以不填 17 'PORT': 3306, #端口 18 } 19 }
3.在同层目录下的__init__.py文件导入对应模块:
import pymysql
pymysql.install_as_MySQLdb()
此时基本配置已经完成,接下来进入处理业务代码的app01文件夹下的models.py中进行对ORM的建表操作
1 from django.db import models #导入django ORM对应模块 2 3 # Create your models here. 4 class Class1(models.Model): #继承models模块定制类,即创建表,类名为表明 5 cid = models.AutoField(primary_key=True) #创建主键,AutoField为自增(主键可以不写,不写时默认生成一行名称为ID的主键列) 6 cname = models.CharField(max_length=32) #创建char类型字符串,最大长度32 7 8 class Teacher(models.Model): 9 tid = models.AutoField(primary_key=True) 10 tname = models.CharField(max_length=32) 11 12 class Student(models.Model): 13 sid = models.AutoField(primary_key=True) 14 sname = models.CharField(max_length=32) 15 Class = models.ForeignKey(Class1) #创建外键,括号里面填写外键关系的表名 这个类在生成时,Class列会显示为Class_id即自写的列名加_id 16 17 class Guanxi(models.Model): 18 teacher = models.ForeignKey(Teacher) 19 Class = models.ForeignKey(Class1)
当类表定制完毕时,cmd切换到Django项目的根目录,运行命令:(注意将python添加至环境变量)
python manage.py makemigrations
python manage.py migrate
此时数据表即完成创建成功,如有报错,注意查看某个字段是否类型错误,检查之前步骤是否有遗漏。
如果是用的sqlite创建,可以用pycharm自带的可视化以及Django的sqlite结合查看表。(将项目中的sqlite3拖拽到pycharm编辑器右上角点开的Database中)
二:基于ORM对表进行增删改查:
本次操作在views模块中,因此首先导入之前创建的表类
from app01 import models
1 查: 2 res = models.UserType.objects.all() #获取表所有数据,得到一个QuerySet[obj,obj,obj],类似一个个对象组成的列表,循环列表取到的为对象,拥有表的字段属性。 3 for row in res: 4 print(row.cid,row.cname) 5 6 res = models.UserType.objects.all() .first() #获取表所有数据的第一行,实际为一个Obj对象,可以res.cid,res.cname取值 7 8 res = models.UserType.objects.filter(cid=1) 9 res = models.UserInfo.objects.filter(id__gt=1) id大于1 10 res = models.UserInfo.objects.filter(id__lt=1) id小于1 11 #filter查询时,括号里面可以添加条件,得到的依然为QuerySet,需要取值依旧需要for 循环 或者后面加.first()方法,当然也可以按索引取值,得到的也是obj对象 12 13 res = models.UserInfo.objects.all().values('id','name') #实际还是QuerySet,括号里面可以限定需要查询的列,不同的是,这里循环出来的不再是对象,而是一个个的字典 QuerySet[{'id':'xx','name':'xx'} ] 取值需按字典key取值。 14 # 跨表 __ 15 # result = models.UserInfo.objects.all().values('id','name',"ut__title") 16 # for item in result: 17 # print(item['id'],item['name'],item['ut__title']) 18 19 20 res = models.UserInfo.objects.all().values_list('id','name') #得到QuerySet[(1,'f'), ] 循环出来的还是一个个的列表,列表索引取值。 21 22 # 跨表 __ 23 # result = models.UserInfo.objects.all().values_list('id','name',"ut__title") 24 # for item in result: 25 # print(item[0],item[1],item[2]) 26 27 28 29 增: 30 #增加为create(),括号里面填写数据行需要的各个数据即可 31 models.UserType.objects.create(title='普通用户') 32 33 34 35 删: 36 #删除需在查询到数据的基础上,后面加上.delete()方法即可 37 models.UserInfo.objects.filter(id=1).delete() 38 39 40 改: 41 #对应的,修改也是在查询的基础上,加.update()方法 42 models.Student.objects.filter(sid=a1).update(sname=name,Class_id=cid) 43 44 #外键操作:以下操作基于2个表已经建立外键关系的基础 45 ##正向操作: 46 # UserInfo,ut是FK字段 - 正向操作 PS: 一个用户只有一个用户类型 47 obj = models.UserInfo.objects.all().first() 48 print(obj.name,obj.age,obj.ut.title) 49 #通过.外键名.对应关系表的字段列名即可拿到UserType表里面的title列值 50 #同样的如果有多个关系表连表操作时,可以一直.外键无穷无尽的连表取到需要的值 51 52 #反向操作: 53 # UserType, 表名小写_set.all() - 反向操作 PS: 一个用户类型下可以有很多用户 54 obj = models.UserType.objects.all().first() #取到用户类型的其中一个Obj 55 print('用户类型',obj.id,obj.title) 56 #通过用户类型查看该类型下有多少个用户 57 for row in obj.userinfo_set.all(): 58 print(row.name,row.age) 59 60 61 result = models.UserType.objects.all() 62 for item in result: 63 print(item.title,item.userinfo_set.filter(name='xx'))
1 有三个表: 2 class Boy(models.Model): 3 name = models.CharField(max_length=32) 4 5 class Girl(models.Model): 6 nick = models.CharField(max_length=32) 7 8 class Love(models.Model): 9 b = models.ForeignKey('Boy',null=True) 10 g = models.ForeignKey('Girl',null=True) 11 #联合唯一索引 12 #class Meta: 13 # unique_together = [ 14 # ('b','g'), 15 #] 16 17 1.通过Boy表中name为条件查找Girl表有关联的nick值: 18 插数据: 19 # obj_list = [ 20 # models.Boy(name='刘德华'), 21 # models.Boy(name='张学友'), 22 # models.Boy(name='郭富城'), 23 # models.Boy(name='黎明'), 24 # ] 25 # objs = [ 26 # models.Girl(nick='陈慧琳'), 27 # models.Girl(nick='容祖儿'), 28 # models.Girl(nick='郑秀文'), 29 # models.Girl(nick='蒙嘉慧'), 30 # ] 31 # models.Boy.objects.bulk_create(obj_list,5) 32 # models.Girl.objects.bulk_create(objs,5) 33 建立关系 34 # models.Love.objects.create(b_id=1,g_id=1) 35 # models.Love.objects.create(b_id=1,g_id=4) 36 # models.Love.objects.create(b_id=2,g_id=2) 37 # models.Love.objects.create(b_id=2,g_id=3) 38 # models.Love.objects.create(b_id=2,g_id=4) 39 多对多的关系查询: 40 # lo_list = models.Love.objects.filter(b__name='刘德华').values('b__name','g__nick') 41 # for i in lo_list: 42 # print(i['b__name'],i['g__nick']) 43 # 44 # lov_list = models.Love.objects.filter(b__name='张学友').select_related('b','g') 45 # for row in lov_list: 46 # print(row.b.name,row.g.nick) 47 48 二。两个表的关系也可以不再建关系表Love,通过Django内置ORM的功能同样可以建立表之间的关系,查找时通过间接查找。 49 class Boy(models.Model): 50 name = models.CharField(max_length=32) 51 m = models.ManyToManyField('Girl') 52 53 class Girl(models.Model): 54 nick = models.CharField(max_length=32) 55 56 如此,同样可以建立关系,只不过增删改查相应也有变化。 57 58 obj = models.Boy.objects.filter(name='刘德华').first() #取到筛选出的Boy对象: 59 #增 60 obj.m.add(1) 61 obj.m.add(*[2,3,]) 62 63 # 删: 64 obj.m.remove(1,) 65 obj.m.remove(*[2,]) 66 67 #改 68 obj.m.set([1,2,]) #实为重置,将之前的全部删除重新建立关系 69 70 #查 71 q = obj.m.all() #q得到对应关系的Girl对象
三.Django之自定义分页
由于Django自带分页具有局限性,且其它框架都不包含分页功能,因此这里自定义一个分页功能模块。
1 # Create your views here. 2 #定制分页类: 3 class PageInfo(object): 4 def __init__(self,current_page,all_count,per_page,base_url,show_page=11): 5 """ 6 7 :param current_page:当前页 8 :param all_count: 数据库总行数 9 :param per_page: 每页显示行数 10 :param base_url: 需要分页的url 11 :param show_page: 页面可以显示的页数 12 :return: 13 """ 14 """将传入的当前页转为int 如果传入的为字符串,抛出异常直接 15 将当前页设置为第一页""" 16 try: 17 self.current_page = int(current_page) 18 except Exception as e: 19 self.current_page = 1 20 21 self.per_page = per_page 22 """通过数据库所有数据的行数除以每页显示的行数,得到需要一共 23 显示的页数,如果有余数则商要+1,余下的行单独一页""" 24 a,b = divmod(all_count,per_page) 25 if b: 26 a = a+1 27 self.all_pager = a 28 self.show_page = show_page 29 self.base_url = base_url 30 31 """通过当前页码以及页面显示行数来获取页面上显示的具体行的数据 32 如第1页就是1-10行,第2页为11-20行,""" 33 def start(self): 34 return (self.current_page-1) * self.per_page 35 def end(self): 36 return self.current_page * self.per_page 37 38 """实际执行函数""" 39 def pager(self): 40 41 page_list = [] 42 #如果页面上显示11页,样式应该是当前页前面一半后面一半,所以-1然后/2 43 half = int((self.show_page-1)/2) 44 # 如果数据总页数<11 show_page,即不足11页,就只显示数据一共能显示的页数 45 if self.all_pager < self.show_page: 46 begin = 1 47 stop = self.all_pager 48 # :param begin:当前页面显示的最左页,即第一页 49 # :param stop: 当前显示的最右,即最后一页 50 else: 51 # 如果当前页 <=5,永远显示1,11 52 if self.current_page <= half: 53 begin = 1 54 stop =self.show_page 55 else: 56 # 如果当前页+5 大于实际存在的最大页,比如一共有35页,当前页为 57 # 32,再取后5页就是37,则36、37为空,所以加个判定,超过极限就 58 # 取最右页为实际最大页,最左就是-11,即还是一共显示11页, 59 if self.current_page + half > self.all_pager: 60 begin = self.all_pager - self.show_page + 1 61 stop = self.all_pager 62 else: 63 #这里为正常情况,显示当前页的前5页跟后5页 64 begin = self.current_page-half 65 stop = self.current_page + half 66 67 #上一页功能 68 #当前页小于等于1,上一页没有内容 69 if self.current_page <= 1: 70 prev = "<li><a href='#'>上一页</a></li>" 71 #拼接字符串生成li a标签 上一页页数为当前页-1,添加到列表中 72 else: 73 prev = "<li><a href='%s?page=%s'>上一页</a></li>" %(self.base_url,self.current_page-1,) 74 page_list.append(prev) 75 76 #当前页面同时显示的页面,由上面赋值的最左与最右页决定,+1是因为for循环range不顾尾 77 for i in range(begin,stop+1): 78 if i == self.current_page: 79 # 当前页给一个class,CSS显示效果区别于其它页 80 temp = "<li class='active'><a href='%s?page=%s'>%s</a></li>" % (self.base_url, i, i,) 81 else: 82 temp = "<li><a href='%s?page=%s'>%s</a></li>" % (self.base_url, i, i,) 83 page_list.append(temp) 84 85 #下一页功能,同样的当前页>=最大页一样不跳转 86 if self.current_page >= self.all_pager: 87 nex = "<li><a href='#'>下一页</a></li>" 88 else: 89 nex = "<li><a href='%s?page=%s'>下一页</a></li>" % (self.base_url, self.current_page + 1,) 90 page_list.append(nex) 91 #此时列表中按顺序含有上一页的li a标签,需要显示的11页li a标签,以及下一页标签 92 #拼接成字符串并作为函数的返回值以供调用接收 93 return ''.join(page_list)