目录python
models中全部的字段类型其实本质就那几种,整形varchar什么的,都没有实际的约束做用,虽然在models中没有任何限制做用,可是仍是要分门别类,对于校验性组件校验很是有用
就好比说邮箱类型,你在输入邮箱的时候若是不按照邮箱格式输入,瞎鸡儿输入会提示你不合法,虽然输入的是字符串,可是不是规定的邮箱字符串git
字段 | 描述 |
---|---|
AutoField | int自增列,必须填入参数 primary_key=True。当model中若是没有自增列,则自动会建立一个列名为id的列。 |
IntegerField | 一个整数类型,范围在 -2147483648 to 2147483647。(通常不用它来存手机号(位数也不够),直接用字符串存,) |
BigIntegerField | |
CharField | 字符类型对应MySQL的varchar类型,必须提供max_length参数, max_length表示字符长度。 |
DateField | 日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],至关于Python中的datetime.datetime()实例。 |
DecimalField | 10进制小数, max_digits,小数总长度 ,decimal_places,小数位长度 |
EmailField | email字段,内部仍是varchar(254) |
BooleanField | 布尔值,传True或False,自动转成1/0 |
TextField | 存储大段文本 |
FileField | 专门用来存放文件路径,传值的时候,直接传文件对象,将路径保存到数据库 |
关系字段 | 描述 |
ForeignKey | 关系字段,外键类型在ORM中用来表示外键关联关系,通常把ForeignKey字段设置在 '一对多'中'多'的一方。 |
OneToOneField | 一对一字段。 |
ManyToManyField | 多对多表关系而且这一张多对多的关系表是有Django自动帮你建 |
参数 | 描述 |
---|---|
null | 用于表示某个字段能够为空。 |
unique | 若是设置为unique=True 则该字段在此表中必须是惟一的 。 |
db_index | 若是db_index=True 则表明着为此字段设置索引。 |
default | 为该字段设置默认值。 |
DateField和DateTimeField | 描述 |
auto_now_add | 配置auto_now_add=True,建立数据记录的时候会把当前时间添加到数据库。 |
auto_now | 配置上auto_now=True,每次更新数据记录的时候会更新该字段。 |
关系字段参数 | 描述 |
to | 设置要关联的表 |
to_field | 设置要关联的表的字段 |
on_delete | 当删除关联表中的数据时,当前表与其关联的行的行为。 |
db_constraint | 是否在数据库中建立外键约束,默认为True。 |
Django中的CharField对应的MySQL数据库中的varchar类型,没有设置对应char类型的字段,sql
可是Django容许咱们自定义新的字段,下面我来自定义对应于数据库的char类型数据库
自定义字段在实际项目应用中可能会常常用到django
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)
字段合集和对应关系并发
咱们来建立一张表:app
models.py: class Books(models.Model): title = models.CharField(max_length=254) price = models.DecimalField(max_digits=8,decimal_places=2) publish_date = models.DateField() 将表同步到MySQL中: python3 manage.py makemigrations python3 manage.py migrate
而后咱们在django测试文件中,若是单纯的测试某个py文件,须要手动配置测试脚本函数
app01/tests.py: # Create your tests here. import os if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day53.settings") import django django.setup() from app01 import models # 下面开始写测试的代码。 # 建立数据------------------------------------------------------------------------ # 1. create 方法 models.Books.objects.create(title='三国演义',price=123,publish_date='2019-11-11') from datetime import date ctime = date.today() models.Books.objects.create(title='红楼梦',price=888,publish_date=ctime) # 2. 利用对象的绑定方法建立数据 book_obj = models.Books(title='西游记',price=666,publish_date='2000-11-11') book_obj.save() # 插入的数据 # id title price publish_date # 4 三国演义 123 2019-11-11 # 5 红楼梦 888 2019-11-27 # 6 西游记 666 2000-11-11 # 修改数据------------------------------------------------------------------------ ''' 利用filter 自动查询当前表的主键字段,pk表明primary_key filter查询出来的是一个queryset对象: 只要是queryset的对象就能够无限制的调用queryset的方法 只要是queryset的对象就能够点query查看当前结果内部对应的sql语句 ''' # 1. 利用queryset方法修改数据 models.Books.objects.filter(pk=4).update(price=444) # 2. 利用对象方法修改数据 book_obj = models.Books.objects.get(pk=1) book_obj.price = 222 book_obj.save() # 不推荐使用,推荐使用queryset方法 ''' 利用对象修改,内部实际上是将全部的字段从新写进去 get 和 filter的区别: filter 获取到的是一个queryset对象,相似于一个列表,没有数据的时候不会报错 get获取到的数据就是数据自己,可是没有数据的时候会报错 ''' # 删除数据------------------------------------------------------------------------ # 1. 利用queryset方法 :delete() models.Books.objects.filter(pk=4).delete() # 2. 对象的方法 book_obj = models.Books.objects.get(pk=4) book_obj.delete() # 若是你想直接查看全部的orm语句内部对应的sql语句,能够在配置文件中配置: 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', }, } }
方法(加括号使用) | 描述 |
---|---|
all | 查询全部,返回QuerySet对象 |
filter | 筛选,至关于原生SQL的where条件,返回QuerySet对象 |
get | 筛选,获取数据对象自己,若是不存在报错,而且查询条件必须是惟一的。 |
first | 取QuerySet对象中第一个数据对象 |
last | 取QuerySet对象中最后一个数据对象 |
count | 统计数据的条数 |
values | 获取数据对象中指定的字段的值,返回列表套字典的QuerySet对象 |
values_list | 获取数据对象中指定的字段的值,返回列表套元组的QuerySet对象 |
order_by | 指定字段进行排序,默认升序,在字段前加- 是降序 |
reverse | 颠倒顺序,前提是颠倒的对象必须有顺序,也就是order_by事后的 |
exclude | 查询排除什么以外,返回QuerySet对象 |
exists | 判断查询结果是否有值,返回布尔值 |
distinct | 对指定字段查询出来的结果进行去重操做 |
# 查询数据 13 条------------------------------------------------------------------------ # 1. all() 查询全部,返回queryset对象 print(models.Books.objects.all()) # 2. filter() 筛选 至关于原生sql语句里面的where关键字,返回queryset对象 print(models.Books.objects.filter(pk=4)) # 3. get() 筛选 获取的数据是对象自己,数据不存在直接报错,而且查询条件必须惟一,返回数据对象自己 print(models.Books.objects.get(pk=4)) # 4. first() 获取queryset对象中第一个数据对象 返回数据对象 print(models.Books.objects.filter(pk=4).first()) # 5. last() 获取queryset对象中最后一个数据对象 返回数据对象 print(models.Books.objects.filter(pk=4).last()) # 6. count() 统计数据的条数 返回数字 print(models.Books.objects.count()) # 7. values() 获取数据对象中指定的字段的值,能够指定多个,返回queryset对象,列表套字典 print(models.Books.objects.values('title','price')) # 8. values_list() 获取数据对象中指定的字段的值,能够指定多个,返回queryset对象,列表套元组 print(models.Books.objects.values_list('title','price')) # 9. order_by() 按照指定的字段排序,默认是升序,在字段前面加 - 是降序 print(models.Books.objects.order_by('pk').values('pk')) print(models.Books.objects.order_by('-pk').values('pk')) # 10. reverse() 颠倒顺序,前提是颠倒的对象必须有顺序,排序以后的 print(models.Books.objects.order_by('pk').reverse().values('pk')) # 11. exclude() 查询条件排除什么 print(models.Books.objects.exclude(pk=4).values('pk')) # 12. exists() 判断查询结果是否有值,返回的是布尔值 print(models.Books.objects.exists()) # 13. distinct() 对查询结果去重,前提是数据彻底相同的状况下 print(models.Books.objects.values('title','price').distinct())
方法 | 描述 |
---|---|
**__gt** | 字段大于什么什么值 |
**__lt** | 字段小于什么什么值 |
**__gte** | 字段大于等于什么值 |
**__lte** | 字段小于等于什么值 |
**__in** | 字段是什么或什么的值,or的关系 |
**__range** | 字段是什么和什么之间的值,btwen..and... |
**__year** | 字段的年份是什么什么的值 |
**__month** | 字段的月份是什么什么的值 |
# 下划线查询------------------------------------------------------------------------ # 1. 查询价格大于500的书 print(models.Books.objects.filter(price__gt=500).values('title')) # 2. 查询价格小于500的书籍 print(models.Books.objects.filter(price__lt=500).values('title')) # 3. 查询价格大于等于500的书籍 print(models.Books.objects.filter(price__gte=500).values('title')) # 4. 查询价格小于等于500的书籍 print(models.Books.objects.filter(price__lte=500).values('title')) # 5. 查询价格是222 或 444 或 500 的书籍 print(models.Books.objects.filter(price__in=[222,444,500]).values('title')) # 6. 查询价格在200到800之间的书籍 顾头也顾尾 print(models.Books.objects.filter(price__range=(200,800)).values('title')) # 7. 查询出版日期是2019年的书籍 print(models.Books.objects.filter(publish_date__year='2019').values('title')) # 8. 查询出版日期是11月份的书籍 print(models.Books.objects.filter(publish_date__month='11').values('title'))
MySQL中的模糊查询,关键字like,模糊匹配的符号:%
、_
测试
方法 | 描述 |
---|---|
**__startswith** | 字段的值以什么什么开头的值 |
**__endswith** | 字段的值以什么什么结尾的值 |
**__contanins** | 字段的值包含什么什么的值,区分大小写 |
**__icontanins** | 字段的值包含什么什么的值,不区分大小写 |
# 模糊查询------------------------------------------------------------------------ # 1. 查询书籍是以三开头的书 print(models.Books.objects.filter(title__startswith='三').values('title')) # 2. 查询书籍是以三结尾的书 print(models.Books.objects.filter(title__endswith='三').values('title')) # 3. 查询书籍名称中包含游字的书籍 print(models.Books.objects.filter(title__contains='游').values('title')) # 4. 查询书籍名称中包含字母p的书籍,区分大小写 print(models.Books.objects.filter(title__contains='p').values('title')) # 5. 查询书籍名称中包含字母p的书籍,区分大小写 print(models.Books.objects.filter(title__icontains='p').values('title'))
为了方便操做,咱们建立图书管理系统相关表来演示多表操做atom
models.py: class Book(models.Model): title = models.CharField(max_length=254) price = models.DecimalField(max_digits=8,decimal_places=2) publish_date = models.DateField(auto_now_add=True) ''' auto_now :每次修改数据的时候,会自动更新时间 auto_now_add :当数据建立出来的时候,自动将建立时间记录下来 ''' publish = models.ForeignKey(to='Publish') authors = models.ManyToManyField(to='Author') class Publish(models.Model): name = models.CharField(max_length=254) addr = models.CharField(max_length=254) class Author(models.Model): name = models.CharField(max_length=254) email = models.EmailField(max_length=254) author_detail = models.OneToOneField(to='AuthorDetail') class AuthorDetail(models.Model): phone = models.BigIntegerField() add = models.CharField(max_length=254)
# 一对多字段数据的增删改查------------------------------------------------------------------------ # 一对一字段的数据的修改和一对多同样的。 # 增 # 第一种方式: publish_obj = models.Publish.objects.filter(pk=1).first() models.Book.objects.create(title='红楼梦',price=444,publish=publish_obj) # 第二种方式: models.Book.objects.create(title='三国演义',price=222,publish_id=1) # 改 # 第一种方式: models.Book.objects.filter(pk=2).update(publish_id=2) # 第二种方式: publish_obj = models.Publish.objects.filter(pk=2).first() models.Book.objects.filter(pk=2).update(publish=publish_obj) # 删 默认就是级联删除,级联更新 models.Publish.objects.filter(pk=1).delete()
方法 | 描述 |
---|---|
add | 可以在第三张表中添加数据既支持传数字,也支持传对象,能够传多个值 |
set | 修改第三张表中的数据,须要传一个可迭代对象进去例如元组,列表,容器类元素能够是数字或者对象 |
remove | 删除第三张表中的数据,既支持传数字,也支持传对象,能够传多个值 |
clear | 清空 删除书籍相关的记录,下线,括号内不须要传递参数 |
# 多对多字段数据的增删改查------------------------------------------------------------------------ # 增 book_obj = models.Book.objects.filter(pk=2).first() # print(book_obj.authors) # app01.Author.None 表示着已经跨到第三张表了 # 给当前这一本书绑定做者,能够写多个 book_obj.authors.add(1) book_obj.authors.add(1,2) # 也能够增长对象 author_obj1 = models.Author.objects.filter(pk=1).first() author_obj2 = models.Author.objects.filter(pk=2).first() book_obj.authors.add(author_obj1,author_obj2) ''' add 方法: 可以在第三张表中添加数据 既支持传数字,也支持传对象,能够传多个值 ''' # 改 # 修改id为2的书籍的做者 book_obj = models.Book.objects.filter(pk=2).first() book_obj.authors.set((1,)) book_obj.authors.set((1,2)) # 也能够修改成对象 author_obj1 = models.Author.objects.filter(pk=1).first() author_obj2 = models.Author.objects.filter(pk=2).first() book_obj.authors.set((author_obj1,author_obj2)) ''' set 方法: 修改第三张表中的数据, 须要传一个可迭代对象进去例如元组,列表, 容器类元素能够是数字或者对象 ''' # 删 # 删除id为2的书籍的做者 book_obj = models.Book.objects.filter(pk=2).first() book_obj.author.remove(1,2) # 也能够传对象 author_obj1 = models.Author.objects.filter(pk=1).first() author_obj2 = models.Author.objects.filter(pk=2).first() book_obj.author.remove(author_obj1, author_obj2) ''' remove 方法: 删除第三张表中的数据,既支持传数字,也支持传对象,能够传多个值 ''' # 清空: # 删除id为2 的书籍(包括做者信息也删除) book_obj = models.Book.objects.filter(pk=2).first() book_obj.authors.clear() ''' clear 方法: 清空 删除书籍相关的记录,下线,括号内不须要传递参数 '''
在使用跨表查询的时候,要注意一点,
要明白关系字段在哪一方?
从有关系字段的那一方查的话就是正向查询,查询按关系字段查询查。
从没有关系字段的那一方查的话就是反向查询,查询按小写的表名查。
方法 | 描述 |
---|---|
all() | 当正向查询点击外键字段数据有多个的状况下也就是多对多查询,须要加.all() |
**_set** | 反向查询的时候 表名小写而且加_set |
子查询
# 跨表查询------------------------------------------------------------------------ # 基于对象的跨表查询 # 1. 查询书籍主键为2的出版社名称 # 正向查询,先查书籍对象,一对多查询 book_obj = models.Book.objects.filter(pk=2).first() print(book_obj.publish.name) # 2. 查询书籍主键为2的做者姓名 # 正向查询,先查书籍对象,多对多查询 book_obj = models.Book.objects.filter(pk=2).first() print(book_obj.authors) # app01.Author.None print(book_obj.authors.all().values('name')) # 3. 查询做者是qinyj的手机号码 # 正向查询,先查做者对象,一对一查询 author_obj = models.Author.objects.filter(name='qinyj').first() print(author_obj.author_detail.phone) ''' 何时须要加 all: 当正向查询点击外键字段数据有多个的状况下 也就是多对多查询,须要加.all() ''' # 4. 查询出版社是东方出版社出版过的书籍 # 反向查询,先查出版社对象,一对多查询 publish_obj = models.Publish.objects.filter(name='东方出版社').first() print(publish_obj.book_set.all().values('title')) # 5. 查询做者是qinyj写过的书籍 # 反向查询,先查做者对象,多对多查询 author_obj = models.Author.objects.filter(name='qinyj').first() print(author_obj.book_set.all().values('title')) # 6. 查询手机号是111111的做者姓名 # 反向查询,先查做者详情表的对象,一对一查询 author_detail_obj = models.AuthorDetail.objects.filter(phone='111111').first() print(author_detail_obj.author.name) ''' 何时反向查询的时候表名小写而且加_set 一对多 多对多 一对一不须要加 '''
联表查询
双下划线,链式操做:__
# 基于双下划线的跨表查询 联表操做------------------------------------------------------------------------ # 1. 查询书籍id为2的出版社名称 # 正向查询: # values中写外键名就至关于跨到外键字段所在的表中了 print(models.Book.objects.filter(pk=2).values('publish__name')) # 反向查询: print(models.Publish.objects.filter(book__pk=2).values('name')) # 2. 查询书籍id为2的做者的姓名 # 正向查询: print(models.Book.objects.filter(pk=2).values('authors__name')) # 反向查询: print(models.Author.objects.filter(book__pk=2).values('name')) # 3. 查询做者是qinyj的地址 # 正向查询: print(models.Author.objects.filter(name='qinyj').values('author_detail__add')) # 反向查询: print(models.AuthorDetail.objects.filter(author__name='qinyj').values('add')) # 4. 查询出版社是东方出版社出版过的书的名字 # 正向查询: print(models.Book.objects.filter(publish__name='东方出版社').values('title')) # 反向查询: print(models.Publish.objects.filter(name='东方出版社').values('book__title')) # 5. 查询书籍id是2的做者的手机号 # 正向查询: print(models.Book.objects.filter(pk=2).values('authors__author_detail__phone')) # 反向查询: print(models.Author.objects.filter(book__pk=2).values('author_detail__phone'))
aggregate
聚合函数 | 描述 |
---|---|
Max | 最大值 |
Min | 最小值 |
Sum | 求和 |
Count | 计数 |
Avg | 平均数 |
''' 聚合函数 ''' from django.db.models import Max,Min,Count,Avg,Sum # 筛选出价格最高的书籍 print(models.Book.objects.aggregate(ct=Count('price'))) # {'ct': 2} # 筛选出价格最低的书籍 print(models.Book.objects.aggregate(mn=Min('price'))) # {'mn': Decimal('222.00')} # 求书籍价格总和 print(models.Book.objects.aggregate(sm=Sum('price'))) # {'sm': Decimal('666.00')} # 求书籍价格平均值 print(models.Book.objects.aggregate(ag=Avg('price'))) # {'ag': 333.0} # 联用 print(models.Book.objects.aggregate(Max('price')),Min('price'),Sum('price'),Count('price'))
annotate
''' 分组查询,一般和聚合函数一块儿使用 ''' # 1. 统计每一本书的 书名和对应的做者个数 print(models.Book.objects.annotate(author_num=Count('authors')).values('title','author_num')) # 2. 统计每各出版社卖的最便宜的书的价格 print(models.Publish.objects.annotate(book_num=Min('book__price')).values('name','book_num')) # 3. 按照指定的字段进行分组 print(models.Publish.objects.values('name').annotate(max_price=Max('book__price')).values('name','max_price')) # 4. 统计不止一个做者的图书 # 首先拿到书对应的做者数,再筛选出大于一的图书,做者个数 print(models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title','author_num')) # 5. 查询每一个做者出的书的总价格 print(models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price'))
''' F 查询 ''' from django.db.models import F,Q # 1. 查询库存数大于卖出数的书籍 print(models.Book.objects.filter(kucun_num__gt=F('maichu_num')).values('title')) # 2. 将全部书的价格都上涨100块 models.Book.objects.all().update(price=F('price')+100) # 3. 将全部书的名字后面都加上 后缀名 from django.db.models.functions import Concat from django.db.models import Value models.Book.objects.update(title=Concat(F('title'),Value('新款'))) ''' Q 查询 ''' # 1. 查询书籍名是三国演义或者 库存数是500的书籍 print(models.Book.objects.filter(title='三国演义',kucun_num=500)) # 默认是and的关系 print(models.Book.objects.filter(Q(title='三国演义') | Q(kucun_num=500))) # 用了 | 就是or的关系 print(models.Book.objects.filter(~Q(title='三国演义') | Q(kucun_num=500))) # 用了 | 就是or的关系 ''' Q对象高级用法 ''' q = Q() q.connector = 'or' # 默认是and,能够改为or q.children.append(('title','三国演义')) q.children.append(('kucun_num',500)) print(models.Book.objects.filter(q))
还记得在Mysql数据库中的事务操做吗?
在MySQL中只有使用Innodb数据库引擎的数据库或表才支持使用事务
事务必须知足4个条件(ACID):
from django.db import transaction with transaction.atomic(): # 在缩进的代码中书写数据库的操做 # 该缩进下的全部代码,都是一个事务 pass # 对整个view视图开启事务 @transaction.atomic def index(request): //ORM操做 return ....
1.建立一个项目,新建一个APP(基础操做,这里再也不赘述)
2.经过ORM建立生成表
from django.db import models class UserInfo(models.Model): username = models.CharField("用户",max_length=32) balance = models.CharField("余额",max_length=32)
注意啊:踩过的坑,涉及金融计算,涉及小数啊,要求特别精确的,咱们用字符串存储。
若是是金融计算的话,咱们用一个decimal来进行计算。
3.咱们给数据库加两条数据,用来模拟两个用户之间的转帐
4.配置URL
5.建立对应的视图函数
from django.shortcuts import render,HttpResponse from app01 import models from django.db import transaction from django.db.models import F def index(request): try: with transaction.atomic(): models.UserInfo.object.filter(id=1).update(balance=F("balance")-100) models.UserInfo.object.filter(id=2).update(balance=F("balance")+100) except Exception as e: return HttpResponse("出现错误<%s>"%str(e)) return HttpResponse("执行成功")
当咱们访问index的时候,会进行一次转帐操做
6.如今,咱们让他报错
from django.shortcuts import render,HttpResponse from app01 import models from django.db import transaction from django.db.models import F def index(request): try: with transaction.atomic(): models.UserInfo.object.filter(id=1).update(balance=F("balance")-100) raise 一个错误 models.UserInfo.object.filter(id=2).update(balance=F("balance")+100) except Exception as e: return HttpResponse("出现错误<%s>"%str(e)) return HttpResponse("执行成功")
咱们再次查看数据库文件,若是没有数据的原子性操做,咱们第一条sql执行完报错,那钱确定是减去了
可是,咱们进行的是原子性的操做,你会发现钱没有减诶。
完美,没毛病
范式(Normal Form),缩写NF,规范化形式,简称范式。
目的是增长数据有效性,减小数据冗余,提升存储效率
数据库中的每个字段,必须是不可拆分的最小单位。
即一个字段就表示一个意思,表中不能同时有2个字段来表示同一个意思
正例:
根据业务需求来合理使用行政区域
反例:
其中 address 能够再分为省、市、地区(县)、街道、详细地址,违反了第一范式。
表中全部字段都必须有意义,一个表只描述某一个事物
主键存在的意义就是惟一的标识表中的某一条记录,若是某一列和该行记录不要紧,也就不必存在。
反例:
此表中,天气(weather字段)和用户没啥关系,也就不存在依赖关系,所不符合 第二范式。正确的作法应)该删除此列,若有其余须要可单独存在一张表中。
表中不能有其余表中存在的、存储相同信息的字段,一般是经过外键去创建关联,外键约束
反例:
上面是一个订单表,字段从左至右以此是:订单id、买家id、买家名称、买家性别、买家年龄、订单状态。其中字段buyer_name、buyer_gender、buyer_age 是依赖于字段 buyer_info_id,违反 第二范式。
正例:
订单表
买家信息表