Django之模型层
Posted 9527mwz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django之模型层相关的知识,希望对你有一定的参考价值。
Django ORM常用字段和参数
常用字段
AutoField
int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。
IntegerField
一个整数类型,范围在 -2147483648 to 2147483647。(一般不用它来存手机号(位数也不够),直接用字符串存,)
CharField
字符类型,必须提供max_length参数, max_length表示字符长度。
DateField
日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()实例。
DateTimeField
日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例。
自定义字段
from django.db import models
# Create your models here.
#Django中没有对应的char类型字段,但是我们可以自己创建
class FixCharField(models.Field):
'''
自定义的char类型的字段类
'''
def __init__(self,max_length,*args,**kwargs):
self.max_length=max_length
super().__init__(max_length=max_length,*args,**kwargs)
def db_type(self, connection):
'''
限定生成的数据库表字段类型char,长度为max_length指定的值
:param connection:
:return:
'''
return 'char(%s)'%self.max_length
#应用上面自定义的char类型
class Class(models.Model):
id=models.AutoField(primary_key=True)
title=models.CharField(max_length=32)
class_name=FixCharField(max_length=16)
gender_choice=((1,'男'),(2,'女'),(3,'保密'))
gender=models.SmallIntegerField(choices=gender_choice,default=3)
字段集合
AutoField(Field)
- int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)
class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
BooleanField(Field)
- 布尔值类型
NullBooleanField(Field):
- 可以为空的布尔值
CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度
TextField(Field)
- 文本类型
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
IPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"
URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
FloatField(Field)
- 浮点型
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field)
- 二进制类型
字段参数
null
用于表示某个字段可以为空。
unique
如果设置为unique=True 则该字段在此表中必须是唯一的 。
db_index
如果db_index=True 则代表着为此字段设置索引。
default
为该字段设置默认值。
时间字段DataTimeFiled 和 DataFiled
auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段。
关系字段
1.ForeignKey
外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 ‘一对多‘中‘多‘的一方。
ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。
字段参数:
to
设置要关联的表
to_field
设置要关联的表的字段
on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。
models.CASCADE
删除关联数据,与之关联也删除
db_constraint
是否在数据库中创建外键约束,默认为True。
2.OneToOneFiled
一对一字段。
通常一对一字段用来扩展已有字段。(通俗的说就是一个人的所有信息不是放在一张表里面的,简单的信息一张表,隐私的信息另一张表,之间通过一对一外键关联)
字段参数:
to
设置要关联的表。
to_field
设置要关联的字段。
on_delete
级联操作 当删除关联表中的数据时,当前表与其关联的行的行为。
3.ManyToManyFiled
字段参数与一对多类似,不同在于不在表中产生具体字段,只是标识了多对多关系,Django自动产生第三张表
在Python脚本调用Django环境
从manage.py取下面代码置于脚本环境中
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "库名.settings")
import django
django.setup()
from app01 import models
单表操作
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_place=2)
publish = models.CharField(max_length=32)
author = models.CharField(max_length=32)
create_time = models.DateField(null=True)
# 配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
# 配置上auto_now=True,每次更新数据记录的时候会更新该字段。
新增数据
# 第一种:有返回值,并且就是当前被创建的数据对象
modles.Book.objects.create(name='',price='',publish='',author='',create_time='2019-5-1')
# 第二种:先实例化产生对象,然后调用save方法保存
book_obj = models.Book(name='',price='',publish='',author='',create_time='2019-5-1')
book_obj.save()
# 2.验证时间格式字段即可以传字符串也可以传时间对象
import datetime
ctime = datetime.datetime.now()
book = models.Book.objects.create(name='',price='',author='',create_time=ctime)
删除数据
"""删除数据"""
# 1.删除书名为xxx的这本书 queryset方法
res = models.Book.objects.filter(name='').delete()
# 2.删除书名为xxx的这本书 对象方法
res = models.Book.objects.filter(name='').first()
res.delete()
修改数据
# 1.queryset修改
models.Book.objects.filter(name='').update(price='')
# 2.对象修改
book = models.Book.objects.filter(name='').first()
book.price = 66.66
book.save() # 对象只有保存方法 这样也能实现修改需求
查询数据
<1> all(): 查询所有结果
<2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象
<3> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。(源码就去搂一眼~诠释为何只能是一个对象)
<4> exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象
<5> order_by(*field): 对查询结果排序('-id')/('price')
<6> reverse(): 对查询结果反向排序 >>>前面要先有排序才能反向
<7> count(): 返回数据库中匹配查询(QuerySet)的对象数量。
<8> first(): 返回第一条记录
<9> last(): 返回最后一条记录
<10> exists(): 如果QuerySet包含数据,就返回True,否则返回False
<11> values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
<12> values_list(*field): 它返回的是一个元组序列,
<13> distinct(): 从返回结果中剔除重复纪录
上述返回QuerySet对象的有:all(),filter(),exclude(),
order_by(),reverse(),distinct()
返回特殊QuerySet对象的有:values(),values_list()
返回具体对象:get(),first(),last()
# *******************
# 必须完全一样才可以去重(意味着带了id就没有意义了)
# res = models.Book.objects.all().values('name').distinct() 先查一个重复的值再去重
基于双下划线的查询
# 价格 大于 小于 大于等于 小于等于
filter(price__gt='90')
filter(price__lt='90')
filter(price_gte='90')
filter(price_lte='90')
# 存在与某几个条件中
filter(price__in=['11','22','33'])
# 在某个范围内
filter(price__range=[50,90])
# 模糊查询
filter(title__contains='西')
filter(title__icontains='P') # 忽略字母大小写
# 以什么开头 以什么结尾
filter(name__startswith='j')
filter(name__endswith='n')
# 按年查询
filter(create_time__year='2011')
正向反向概念
# 正向与方向的概念解释
# 一对一
# 正向:author---关联字段在author表里--->authordetail 按字段
# 反向:authordetail---关联字段在author表里--->author 按表名小写
# 查询jason作者的手机号 正向查询
# 查询地址是 :山东 的作者名字 反向查询
# 一对多
# 正向:book---关联字段在book表里--->publish 按字段
# 反向:publish---关联字段在book表里--->book 按表名小写_set.all() 因为一个出版社对应着多个图书
# 多对多
# 正向:book---关联字段在book表里--->author 按字段
# 反向:author---关联字段在book表里--->book 按表名小写_set.all() 因为一个作者对应着多个图书
# 连续跨表
# 查询图书是三国演义的作者的手机号,先查书,再正向查到作者,在正向查手机号
# 总结:基于对象的查询都是子查询,这里可以用django配置文件自动打印sql语句的配置做演示
Django终端打印SQL语句
如果你想知道你对数据库进行操作时,Django内部到底是怎么执行它的sql语句时可以加下面的配置来查看
在Django项目的settings.py文件中,在最后复制粘贴如下代码:
LOGGING =
'version': 1,
'disable_existing_loggers': False,
'handlers':
'console':
'level':'DEBUG',
'class':'logging.StreamHandler',
,
,
'loggers':
'django.db.backends':
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
,
多表操作
一对多操作
创建图书管理系统表(给作者表加一张作者详情表为了一对一的查询),诠释一对一关联其实就是外健关联再加一个唯一性约束而已
ForeignKey(unique=Ture) >>> OneToOneField()
# 即一对一可以用ForeignKey来做,但是需要设唯一性约束并且会报警告信息,不建议使用,建议用OneToOneField
# 用了OneToOneField和用ForeignKey会自动在字段后面加_id
# 用了ManyToMany会自动创建第三张表
新增数据
# 直接写id
models.Book.objects.create(title='红楼梦',price=66.66,publish_id=1)
# 传数据对象
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='三国义',price=199.99,publish=publish_obj)
删除数据
models.Book.objects.filter(pk=1).delete()
models.Publish.objects.filter(pk=1).delete() # 会进行级联删除
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.delete()
修改数据
# queryset修改
models.Book.objects.filter(pk=1).update(publish_id=3)
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.filter(pk=1).update(publish=publish_obj)
# 对象修改
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.publish_id = 3 # 点表中真实存在的字段名
book_obj.save()
publish_obj = models.Publish.objects.filter(pk=2).first()
book_obj.publish = publish_obj # 点orm中字段名 传该字段对应的表的数据对象
book_obj.save()
查询数据
1正向查找
a,对象查找
语法:
对象.关联字段.字段
要点:先拿到对象,再通过对象去查对应的外键字段,分两步
示例:
book_obj = models.Book.objects.first() # 第一本书对象(第一步)
print(book_obj.publisher) # 得到这本书关联的出版社对象
print(book_obj.publisher.name) # 得到出版社对象的名称
b,字段查找
语法:
关联字段__字段
要点:利用Django给我们提供的神奇的双下划线查找方式
示例:
models.Book.objects.all().values("publisher__name")
#拿到所有数据对应的出版社的名字,神奇的下划线帮我们夸表查询
2反向查询
a,对象查找
语法:
obj.表名_set
要点:先拿到外键关联多对一,一中的某个对象,由于外键字段设置在多的一方,所以这里还是借用Django提供的双下划线来查找
示例:
publisher_obj = models.Publisher.objects.first() # 找到第一个出版社对象
books = publisher_obj.book_set.all() # 找到第一个出版社出版的所有书
titles = books.values_list("title") # 找到第一个出版社出版的所有书的书名
结论:如果想通过一的那一方去查找多的一方,由于外键字段不在一这一方,所以用__set来查找即可
b,字段查找
语法:
表名__字段
要点:直接利用双下滑线完成夸表操作
titles = models.Publisher.objects.values("book__title")
一对多的书籍记录增删改查总结:
# 针对外键关联的字段 两种添加方式
# 第一种通过publish_id
# 第二种通过publish传出版社对象
# 删除书籍直接查询删除即可,删除出版社会级联删除
# 编辑数据也是两种对应的方式(对象点的方式(这里能点publish和publish_id)最后点save(),queryset方式update())
多对多操作
add()
# 给书籍绑定与作者之间的关系
# 添加关系 add:add支持传数字或对象,并且都可以传多个
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.add(1)
book_obj.authors.add(2,3)
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=3).first()
book_obj.authors.add(author_obj)
book_obj.authors.add(author_obj,author_obj1)
set()
# 修改书籍与作者的关系 set() set传的必须是可迭代对象!!!
book_obj = models.Book.objects.filter(pk=3).first()
# 可以传数字和对象,并且支持传多个
book_obj.authors.set((1,))
book_obj.authors.set((1,2,3))
author_list = models.Author.objects.all()
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.set(author_list)
remove() | clear()
# 删除书籍与作者的绑定关系
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.remove(1)
book_obj.authors.remove(2,3)
author_obj = models.Author.objects.all().first()
author_list = models.Author.objects.all()
book_obj.authors.remove(author_obj)
book_obj.authors.remove(*author_list) # 需要将queryset打散
# 清空 clear() 清空的是你当前这个表记录对应的绑定关系
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.clear()
多对多的书籍与作者的增删改查总结:
"""前提:先获取书籍对象,再通过书籍对象点authors来进行书籍作者的增删改查"""
# 1.给书籍新增作者add
# 1.add可以传作者id,也可以直接传作者对象,并且支持传多个位置参数(不要混着用)
# 2.给书籍删除作者remove
# 1.remove同样可以传id,对象,并且支持传多个位置参数(不要混着用)
# 3.直接清空书籍对象所有的作者数据clear()不用传任何参数
# 4.修改书籍对象所关联的作者信息set,注意点set括号内必须传可迭代对象,里面可以传id,对象
"""总结:一对多增删改,多对多add,remove,clear,set"""
基于双下划线的查询
# 一对一
-连表查询
-一对一双下划线查询
-正向:按字段,跨表可以在filter,也可以在values中
-反向:按表名小写,跨表可以在filter,也可以在values中
总结 其实你在查询的时候先把orm查询语句写出来,再看用到的条件是否在当前表内,在就直接获取,不在就按照正向按字段反向按表名来查即可
比如:
1.查询出版社为北方出版社的所有图书的名字和价格
res1 = Publish.objects.filter(name='').values('book__name','book__price')
res2 = Book.objects.filter(publish__name='').values('name','price')
2.查询北方出版社出版的价格大于19的书
res1 = Publish.objects.filter(name='',book__price__gt=19).values('book__name','book__price)
"""
聚合分组查询
聚合查询aggregate
单纯使用聚合函数
from django.db.models import Max,Min,Count,Sum,Avg
# 查询所有书籍的作者个数
models.Book.objects.filter(pk=3).aggregate(count_num=Count('authors'))
# 查询所有出版社出版的书的平均价格
models.Publish.objects.aggregate(avg_price=Avg('book__price'))
models.Publish.objects.filter(name='东方出版社').aggregate(count_num=Count('book__id'))
分组查询annotate、
分组聚合
from django.db.models import Max,Min,Count,Sum,Avg
# 统计每个出版社出版的书的平均价格
models.Publish.objects.annotate(avg_price=Avg('book__price')).values('name','avg_price')
# 统计每一本书的作者个数
models.Book.objects.annotate(count_num=Count('authors')).values('title','count_num')
# 统计出每个出版社卖的最便宜的书的价格
models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
# 查询每个作者出的书的总价格
models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
以上是关于Django之模型层的主要内容,如果未能解决你的问题,请参考以下文章