一、创建模型类
models.py文件中设置ORM字段。
介绍:
Django提供了很多字段类型,比如URL/Email/IP/ ,但是mysql数据没有这些类型,这类型存储到数据库上本质是字符串数据类型,其主要目的是为了封装底层SQL语句;
类型:
1、字符串类
models.CharField 对应的是MySQL的varchar数据类型
char 和 varchar的区别 :
共同点,是存储数据的长度,不能超过max_length限制;
不同点,是varchar根据数据实际长度存储,char按指定max_length()存储数据;所以,前者更节省硬盘空间;
name = models.CharField(max_length=32)
# 以下都是在数据库中,本质都是字符串数据类型,此类字段只是在Django自带的admin中生效
EmailField(CharField) # email
IPAddressField(Field) # IP
URLField(CharField) # URL
SlugField(CharField) # Slug
UUIDField(Field) # UUID
FilePathField(Field) # 文件路径(/usr/local/python/)
FileField(Field)
ImageField(FileField) # 图片路径(本质也就是图片的位置路径)
CommaSeparatedIntegerField(CharField)
2、时间字段
create_time = models.DateTimeField(null=True) # 年月日时分秒
date = models.DateField() # 年月日
3、数字字段(整数、小数)
(max_digits=30,decimal_places=10) 即,总长度30,小数位10位
数字:
num = models.IntegerField() # 整数 (也分 tiny等,即小整数,大整数,超大整数...)
num = models.FloatField() # 浮点
price = models.DecimalField(max_digits=8, decimal_places=3) # 精确浮点
4、枚举字段
应用场景?以及和外键对比,什么优势?
1、key-value对应,选项固定情况,即不是动态变化的。如:设备状态(使用中、未使用、故障、已下线、其他),前后端数据交互时,数据是1、2、3,只需告诉前端分别对应的是什么就可以了,因为不会改变。
2、无需连表查询性能低,省硬盘空间(选项不固定时用外键)
3、在modle文件里不能动态增加(选项一成不变,用Django的choice)
# 元祖嵌套元祖,其实可以看做是字典的生成式一种,数据表中保存的是1\2\3,key-value对应
choice=(
(1,'男人'),
(2,'女人'),
(3,'其他')
)
lover = models.IntegerField(choices=choice) # 枚举类型
5、其他
索引
db_index = True 表示设置索引
unique = True 唯一的意思,设置唯一索引
class Meta:
# 格式:元祖的嵌套,另外,联合索引有基本原则:最左前缀(并有序的)!!!
unique_together = (
('email','ctime'), # 联合唯一索引
)
index_together = (
('email','ctime'), # 联合索引(不做限制)
)
二、ORM操作
背景:
1、orm使用方式:
orm操作可以使用类实例化,obj.save的方式;
也可以使用create()的形式;
2、QuerySet数据类型介绍
QuerySet与惰性机制
所谓惰性机制:
Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行。
QuerySet特点:
<1> 可迭代的
<2> 可切片
<3> 惰性计算和缓存机制
def queryset(request):
books = models.Book.objects.all()[:10] # 切片 应用分页
books = models.Book.objects.all()[::2]
book = models.Book.objects.all()[6] # 索引
print(book.title)
for obj in books: # 可迭代
print(obj.title)
# 惰性计算:
# 等于一个生成器,不应用books不会执行任何SQL操作
# query_set缓存机制:
# 1次数据库查询结果query_set都会对应一块缓存,再次使用该query_set时,不会发生新的SQL操作;
# 作用:这样减小了频繁操作数据库给数据库带来的压力;
books = models.Book.objects.all()
authors = models.Author.objects.all()
for author in authors:
print(author.name)
print('-----')
models.Author.objects.filter(id=1).update(name='张某')
for author in authors:
print(author.name)
# 但是有时候取出来的数据量太大会撑爆缓存,可以使用迭代器优雅得解决这个问题;
models.Publish.objects.all().iterator()
return HttpResponse('OK')
基本操作
增加
# 准备三个表,作者(Author),书籍的分类(Classify),书籍出版(Publish)
def orm(request):
单表
1、方式一:表.objects.create()
models.Publish.objects.create(name='浙江出版社', addr="浙江.杭州")
models.Classify.objects.create(category='武侠')
models.Author.objects.create(name='金庸',sex='男', age=89, university='东吴大学')
2、方式二:类实例化,obj = 类(属性=XX) obj.save()
obj = models.Author(name='吴承恩', age=518, sex='男', university='龙溪学院')
obj.save()
一对多
1、方式一:表.objects.create(外键名_id=那个类的id)
models.Book.objects.create(title='笑傲江湖', price=200, date=1968, classify_id=6, publish_id=6)
2、方式二:类实例化,obj=类(属性=X, 外键=obj) obj.save()
classify_obj = models.Classify.objects.get(category='武侠')
publish_obj = models.Publish.objects.get(name='河北出版社')
# 注意以上获取得是和 book对象 向关联的(外键)的对象
book_obj = models.Book(title='西游记', price=234, date=1556, classify=classify_obj, publish=publish_obj)
book_obj.save()
多对多
如果两表之间存在双向1对N关系,就无法使用外键来描述其关系了;
只能使用多对多的方式,新增第三张表关系描述表;
book = models.Book.objects.get(title='笑傲江湖')
author1 = models.Author.objects.get(name='金庸')
author2 = models.Author.objects.get(name='张根')
book.author.add(author1, author2)
# 书籍和作者是多对多关系,
# 如果两表之间存在多对多关系,例如书籍相关的所有作者对象集合,作者也关联的所有书籍对象集合
# add() 添加
# clear() 清空
# remove() 删除某个对象
return HttpResponse('OK')
删除
修改
# 方式1:update()
models.Book.objects.filter(id=1).update(price=3)
# 方式2:obj.save()
book_obj = models.Book.objects.get(id=1)
book_obj.price=5
book_obj.save()
查找
def ormquery(request):
books = models.Book.objects.filter(id__gt=2, price__lt=100)
book = models.Book.objects.get(title__endswith='金') # 单个对象,没有找到会报错
book1 = models.Book.objects.filter(title__endswith='金').first()
book2 = models.Book.objects.filter(title__icontains='瓶').last()
# 一般:query_set 是对象集合 [对象1、对象2、.... ]
books = models.Book.objects.all()
# values:query_set 是字典集合 [{一条记录},{一条记录} ]
books = models.Book.objects.values(
'title','price',
'publish__name',
'date',
'classify__category', # 正向连表: 外键字段__对应表字段
'author__name', # 反向连表: 小写表名__对应表字段
'author__sex', # 区别:正向 外键字段__,反向 小写表名__
)
books = models.Book.objects.values('title','publish__name').distinct()
# exclude 按条件排除。。。
# distinct()去重,
# exits()查看数据是否存在? 返回 true 和false
a = models.Book.objects.filter(title__icontains='金').
return HttpResponse('OK')
一对多,多对多
1、一对多(ForeignKey)
使用时,通常定义在多的一方
# 背景:老师(一)、学生(多,外键字段名:teacher = modles.ForeignKey(Teacher,...))
# obj对象处理:id为1的老师,有哪些学生?
teacher = Teacher.objects.get(id = 1) # 先获取对象
# 查
# 方式一:
Student类中,外键字段有定义 related_name=’teacher_student’, 可以直接用
studentList = teacher.teacher_student.all()
# 方式二:
外键定义没写 related_name=’student_teacher’, 可以改写成:
studentList = teacher.student_set.all() # 反向
# 增
# 方式一:create方式
teacher.student_set.create(name="tom", age=14)
# 方式二:obj通过使用add
student_obj = Student(name="tom", age=14)
teacher.student_set.add(student_obj)
# 删
student_obj = teacher.student_set.get(name="tom")
student_obj.delete()
2、多对多(ManyToManyField)
特点:
1、迁移时,会自动生成一个中间表(表示俩个表的对应关系)
2、增删改查,同时也适用一对多,一对一,修改少量参数即可
from django.db import models
class Goods(models.Model): # 商品
g_name = models.CharField(max_length=20)
g_price = models.DecimalField(max_digits=5, decimal_places=2)
gc = models.ForeignKey("Category", null=True, on_delete=models.SET_NULL) # gc为外键,一对多,类别表为母表
class Category(models.Model): # 类别
c_name = models.CharField(max_length=20)
class Store(models.Model): # 商家
s_name = models.CharField(max_length=30)
s_detail = models.TextField(blank=True, null=True)
sc = models.ManyToManyField("Category") # 与类别表进行多对多关联
查看
# 专业术语来讲,多对多关系,多对多字段设置在那个表,那个表就是子表
# 查找指定商家下面的所有分类
# 方式一:正向查询
Store.objects.get(s_name="商家C").sc.all() # 写法:子表对象.子表多对多字段.过滤条件
(all()/filter())
<QuerySet [<Category: Category object>, <Category: Category object>]>
# 方式二:反向查询
Category.objects.filter(store__s_name="商家C")
Out[25]: <QuerySet [<Category: Category object>, <Category: Category object>]> # 母表对象.filter(子表表名小写__子表字段名="过滤条件")
# 从分类查询,查看那些商家里面发布指定的某一个分类
# 方式一:
(Category.objects.get(c_name="电脑整机")).store_set.all() # 写法:母表对象.子表表名小写_set.过滤条件
<QuerySet [<Store: Store object>, <Store: Store object>]>
# 方式二:
Store.objects.filter(sc=Category.objects.get(c_name="电脑整机"))
<QuerySet [<Store: Store object>, <Store: Store object>]>
# 方式三:
Store.objects.filter(sc__c_name="电脑整机") # filter(子表外键字段__母表字段='过滤条件')
<QuerySet [<Store: Store object>, <Store: Store object>]>
# 方式四:
Store.objects.filter(sc=category) # filter得到QuerySet,写法:filter(子表外键字段=母表主键对象),此处和一对多略有不同,是子表外键字段而不是外键字段_母表主键
<QuerySet [<Store: Store object>, <Store: Store object>]>
增加
# 普通添加(一对一)
# 添加商家
# 方式一:
Store.objects.create(s_name="商家A", s_detail="物美价廉,抄底折扣。。。。")
Out[2]: <Store: Store object>
# 方式二:
Store(s_name="商家B", s_detail="大促销").save()
# 添加类别
Category.objects.create(c_name="电脑整机")
<Category: Category object>
Category(c_name="文具耗材").save()
# ---------------------------------------------------
# 总结:增与改(增添子表或母表数据参照一对一的增,多对多重点在于关系表的对应关系变更 - 中间表的对应关系)
# ---------------------------------------------------
# 多对多
# 创建商家C并添加全部分类(多对多添加时,add 追加对象,而Category.objects.all()是对象集合)
方式一:
Store.objects.create(s_name="商家C").sc.add(*(Category.objects.all())) # 如果商户已存在则把create改成get
方式二:
store = Store.objects.get(s_name="商家C")
store.sc = (Category.objects.all())
store.save()
# ---------------------------------------------------
# 创建商家D添加指定分类
# 方式一:
store = Store.objects.create(s_name="商家D")
category = Category.objects.filter(c_name__in=["电脑整机","文具耗材"]) # 单个改成get,全部改成all
store.sc.add(*category) # add是追加模式,category是一个查询集(对象集合)
# store.sc.clear() # 清空此商家的商品(其实就是清除了,中间表的对应关系)
# 方式二:
# 让指定商品分类添加指定的商家,反向查询
store = Store.objects.create(s_name="商家E")
category = Category.objects.get(c_name="电脑整机")
category.store_set.add(store)
# 效果与上面一样
# ---------------------------------------------------
# 让所有商家都添加这个分类
stores = Store.objects.all()
category = Category.objects.get(c_name="电脑整机")
category.store_set.add(*stores)
category.store_set.clear() # 让所有商家去除这个分类,删除的是这个类
category.store_set.all().delete() # 是删除这个类的所有商家,删除的是商家
# ---------------------------------------------------
# 总结
# 只有子表才有"子表名小写_set"的写法,得到的是一个QuerySet集合(对象集合),后边可以接.add(),.remove(),.update(),.delete(),.clear()
删除
# 问题一:让指定商家清空分类 (即,这个商家和所有分类表没有关系了,面向的是商家)
s = Store.objects.get(s_name="商家C")
s.sc = ""
s.save()
s=Store.objects.get(s_name="商家C")
c = Category.objects.all() # 对象集合
s.sc.remove(*c)
s = Store.objects.get(s_name="商家C")
s.sc.clear()
# 删除母表与子表关联关系
# 问题二:让所有商家去掉指定分类 (即,这个指定分类和所有商家表没有关系了,面向的是指定分类)
s = Store.objects.all()
c = Category.objects.get(c_name="电脑整机")
c.store_set.remove(*s)
c = Category.objects.get(c_name="电脑整机")
c.store_set.clear()
# 删除商家该子表数据
# 删除所有指定分类的全部商家
c = Category.objects.get(c_name="电脑整机")
c.store_set.all().delete()
# 删除所有商家 (对比下,区别理解)
Store.objects.all().delete()
bug坑点
我在多对多字段中,定义了related_name字段,导致role__id__in=role_ids,反向查询使用“小写表名__字段名”失效,报错:无法解析role字段,即根本没能找到Role的这个关联表。
参考文件
多对多处理,首选看看:https://blog.csdn.net/maidou931019/article/details/74852536
多层级--跨表关系查询,首选看看:https://blog.csdn.net/pushiqiang/article/details/81127442
很全面和详细的,非常不错的一篇:https://blog.csdn.net/qq_26870933/article/details/81563149
大牛神器
借鉴链接:
模型基础--field--方法--ORM:https://code.ziqiangxuetang.com/django/django-models.html
自定义field:https://code.ziqiangxuetang.com/django/django-custom-field.html
ORM
参考链接:
https://code.ziqiangxuetang.com/django/django-queryset-api.html
https://code.ziqiangxuetang.com/django/django-queryset-advance.html