一。常用字段。
在orm中有一些字段是常用字段:
1.AutoField
这个字段是自增的,必须填入参数primary_key=True,也就是说这个字段是表的主键,如果表类中没有自增列,就会自动创建名为id的自增列。
2.IntegerField
是一个整数类型,范围在 -2147483648到 2147483647。但是一般不用它来存储手机号,位数不够。
3.DateField。
这是日期字段,也就是YYYY-MM-DD,相当于Python中的datetime。date()实例。
4.DateTimeField
与3字段不同的是,这个字段会添加时分秒参数,像YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例。(auto_now,auto_now_add)
5.BooleanField
布尔类型参数,给该字段传值的时候,你只需要传递布尔值即可,但是对应到数据库就会传递0或者1.
6.TextField
文本类型字段,用来传递大型的文本。
7.FileField
文件字段。字符串类型,路径保存在数据库,文件上传到指定的目录。
他的参数有upload_to=''file/'',用户上传的文件会自动放到等号后面指定字段的文件路径中(在项目文件下。)
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
8.CharField
字符类型,这个类型必须提供max_length参数,代表这个字符的长度
9.自定义char字段,
在django中,字符串型式的都是varchar类型的文件,我们可以通过自定义的方式来定义一个。
class MyChar(models.Field): 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): return 'char(%s)'%self.max_length #返回char类型。
其他字段:
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) - 二进制类型 字段合集
字段与sql语句对应:
对应关系: 'AutoField': 'integer AUTO_INCREMENT', 'BigAutoField': 'bigint AUTO_INCREMENT', 'BinaryField': 'longblob', 'BooleanField': 'bool', 'CharField': 'varchar(%(max_length)s)', 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', 'DateField': 'date', 'DateTimeField': 'datetime', 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'DurationField': 'bigint', 'FileField': 'varchar(%(max_length)s)', 'FilePathField': 'varchar(%(max_length)s)', 'FloatField': 'double precision', 'IntegerField': 'integer', 'BigIntegerField': 'bigint', 'IPAddressField': 'char(15)', 'GenericIPAddressField': 'char(39)', 'NullBooleanField': 'bool', 'OneToOneField': 'integer', 'PositiveIntegerField': 'integer UNSIGNED', 'PositiveSmallIntegerField': 'smallint UNSIGNED', 'SlugField': 'varchar(%(max_length)s)', 'SmallIntegerField': 'smallint', 'TextField': 'longtext', 'TimeField': 'time', 'UUIDField': 'char(32)', ORM字段与MySQL字段对应关系
字段对应的参数
字段中也需要一些参数设置。
1.null
表示某个字段可以为空。添加字段时使用。
2.unique
可以设置其是唯一值。unique=True,设置1对一时可以使用foreign+unique
3.db_index
如果db_index=True时,代表这为字段设置索引。注意查询效率。
4.default。
为该字段设置默认值。
5.时间字段专用。
auto_now_add:
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库
auto_now:
配置auto_now=True,每次更新数据记录的时候会更新该字段。
二。关系字段:
1.foreignkey
外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 '一对多'中'多'的一方。
ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。
字段参数
1.to
设置其关联的表
2.to_field
设置要关联的表的字段,如果不设置则会自动关联外键。
3.on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。(级联删除)
参数值:models.CASCADE
4.db_constraint
是否在数据库中创建外键约束。默认为True.也就是级联操作。
其他的关联动作:
models.DO_NOTHING
删除关联数据,引发错误IntegrityError
models.PROTECT
删除关联数据,引发错误ProtectedError
models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
models.SET
删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
def func(): return 10 class MyModel(models.Model): user = models.ForeignKey( to="User", to_field="id", on_delete=models.SET(func) )
Django2中所需要添加的参数:
当你在使用django2.X版本的时候 在建立外键关系时(*****),需要你手动添加几个关键点参数。
models.cascade
db_constraints
2.onetoonefield
可以通过foreignField(unique=Ture)来设置。
字段参数:to,to_field,on_delete。
三。总结13个查询操作。
可与上上篇博客参考:
(1) all(): 查询所有结果
(2)filter(**kwargs): 它包含了与所给筛选条件相匹配的对象
(3)get(**kwargs):返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。(源码就去搂一眼~诠释为何只能是一个对象)
(4)exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象
(5) order_by(*field): 对查询结果排序('-id') / ('price')
(6) reverse(): 对查询结果反向排序 >> > 前面要先有排序才能反向
(7) count(): 返回数据库中匹配查询(QuerySet)
(8) first(): 返回第一条记录print(models.Book.objects.filter(pk=1).first())
(9) last(): 返回最后一条记录
(10) exists(): 如果QuerySet包含数据,就返回True,否则返回False
(11)values(*field):返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列.
(12)values_list(*field):它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列.
(13)distinct(): 从返回结果中剔除重复纪录(如果你查询跨越多个表,可能在计算QuerySet时得到重复的结果。此时可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。)
其中,返回的是QuerySet对象的方法有:
all()
filter()
exclude()
order_by()
reverse()
distinct()
返回特殊的QuerySet对象的是(里面是字典或元组):
values() 返回一个可 迭代的字典序列
values_list() 返回一个可迭代的元祖序列
返回的是具体的对象的(外面不再有QuerySet)
get()
first()
last()
返回布尔值的是:
exists()
返回的是数字的方法:
count()
四。Django中的查询优化。
在django中,所有的查询都是惰性查询。也就是说,只有在真正需要数据的时候才会执行数据库语句。
这样的设计在于减轻数据库的压力。如,以下代码不会真正的执行sql语句:
res = models.Book.objects.all()
只有print或者真正需要这个数据时才会访问数据库。
当我们需要查询一个书籍中的数目时有两种方法。
1.将数据通过value()筛选,得出特殊set对象:
res = models.Book.objects.values('title') print(res)
这种操作数据库是一次性的,这样的结果是不能通过点操作进行的,其原理是搜索所有的title,生成字典添加到返回对象中
2.通过all方法取出全部对象,通过点方法取出title,返回qureyset对象。
res = models.Book.objects.all()
for i in res:
print(i.title)
这种操作也是一次性的,一次将所有对象全部获取,放入列表中。
但是取出的数据太过庞大,如何将这个操作进行优化呢?
(1).only。
only的使用和value差不多,但是only获取的不是字典形式的数据,而是一个对象。它会将它所搜索的字段生成一个对象放入返回队列中。
res = models.Book.objects.only('title') for i in res: print(i.title)
可以看到它只对数据库进行了一次操作,之后的点操作都不再访问数据库:
但是当它点表中的其他数据是,它没有报错,却会一条条的访问数据库。:
res = models.Book.objects.only('title') for i in res: print(i.price)
而且是在访问过所有数据之后又分别访问了一次:
(2)defer
defer提取出来的也是一个对象,和only是相反的操作,它会将该字段以外的对象一次性的提取出来,生成对象返回:
res = models.Book.objects.defer('title') print(res)
它的sql语句(此处先不使用str方法):
当它点其他字段时,不会访问数据库,而点该字段时,会频繁访问数据库。
场景二。外键查询优化。
当需要查询一个外键的时候,首先需要获取该表对象,然后再获取外键中的某些字段:
res = models.Book.objects.all() for i in res: print(i.publish.name)
可以看到,每查询一次数据就会访问一次数据库,访问的是外键的那个数据库。
为了减少对数据库的操作次数,可以使用两种查询方法():
(1)select_related
res =models.Book.objects.select_related('publish') for i in res: print(i.publish.name)
它的原生sql语句就是把它外键的表进行连接,然后查询所有需要的字段:
随后再进行点操作就不需要走数据库了。
注意:select_related括号中只能放外键字段,而且只能是一对一或一对多的字段,但是可以持续的__进行拼接如:(外键字段1__外键字段2__外键字段3__外键字段4).它会把四张表全部拼接起来。
(2)prefetch_related
prefetch_related这个操作会执行多次操作,先将这个表关联的字段的主键查询到手,再使用主键去找第二张表,最后将两张表全部返回。
res = models.Book.objects.prefetch_related('publish') for i in res: print(i.publish.name)
可以看到查询了两次数据库。
对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。prefetch_related()的优化方式是分别查询每个表,然后用Python处理他们之间的关系。支持连多个外键。
总结:
only()将括号内的字段数据生成一个对象列表返回。访问其他数据会再次寻找数据库
defer()除了括号中的字段,其他数据生成一个对象列表返回。访问该数据会再次寻找数据库。
select_related()。括号中只能放外键,且只支持1对1,1对多的外键,而且支持多__外键,将所有外键对应的表连接起来,返回对象列表。
prefetch_related()。括号中只能放外键,且只支持1对多,多对多的外键,支持多外键一起,将这个外键的id获取后,再去下一张表中寻找数据,获取外键。最后的拼接又python操作。
五。orm实现事务。
事务的四大特性
1.原子性。
2.一致性。
3.隔离性。
4.持久性。
操作事务需要导入模块:
from django.db import transaction with transaction.atomic(): """数据库操作 在该代码块中书写的操作 同属于一个事务 """ models.Book.objects.create() models.Publish.objects.create() # 添加书籍和出版社 就是同一个事务 要么一起成功要么一起失败 print('出了 代码块 事务就结束')
其原生sql语句是:
可以看到没有出现事务相关的代码,其逻辑应该是再python中操作。
1,装饰器用法
from django.db import transaction @transaction.atomic def viewfunc(request): # 这些代码会在一个事务中执行
2.with语句用法:
from django.db import transaction def viewfunc(request): # 这部分代码不在事务中,会被 Django 自动提交 ...... with transaction.atomic(): # 这部分代码会在事务中执行 ......
3.三句话使用.
from django.db import transaction # 创建保存点 save_id = transaction.savepoint() # 回滚到保存点 transaction.savepoint_rollback(save_id) # 提交从保存点到当前状态的所有数据库事务操作 transaction.savepoint_commit(save_id)
使用例子:
class OrderCommitView(LoginRequiredJSONMixin, View): """订单提交""" def post(self, request): """保存订单信息和订单商品信息""" # 获取当前保存订单时需要的信息 ...... # 显式的开启一个事务 with transaction.atomic(): # 创建事务保存点 save_id = transaction.savepoint() # 暴力回滚 try: # 保存订单基本信息 OrderInfo(一) order = OrderInfo.objects.create( order_id=order_id, user=user, address=address, total_count=0, total_amount=Decimal('0'), freight=Decimal('10.00'), pay_method=pay_method, status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'] if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY'] else OrderInfo.ORDER_STATUS_ENUM['UNSEND'] ) # 从redis读取购物车中被勾选的商品信息 redis_conn = get_redis_connection('carts') redis_cart = redis_conn.hgetall('carts_%s' % user.id) selected = redis_conn.smembers('selected_%s' % user.id) carts = {} for sku_id in selected: carts[int(sku_id)] = int(redis_cart[sku_id]) sku_ids = carts.keys() # 遍历购物车中被勾选的商品信息 for sku_id in sku_ids: # 查询SKU信息 sku = SKU.objects.get(id=sku_id) # 判断SKU库存 sku_count = carts[sku.id] if sku_count > sku.stock: # 出错就回滚 transaction.savepoint_rollback(save_id) return http.JsonResponse({ 'code': RETCODE.STOCKERR, 'errmsg': '库存不足'}) # SKU减少库存,增加销量 sku.stock -= sku_count sku.sales += sku_count sku.save() # 修改SPU销量 sku.goods.sales += sku_count sku.goods.save() # 保存订单商品信息 OrderGoods(多) OrderGoods.objects.create( order=order, sku=sku, count=sku_count, price=sku.price, ) # 保存商品订单中总价和总数量 order.total_count += sku_count order.total_amount += (sku_count * sku.price) # 添加邮费和保存订单信息 order.total_amount += order.freight order.save() except Exception as e: logger.error(e) transaction.savepoint_rollback(save_id) return http.JsonResponse({ 'code': RETCODE.DBERR, 'errmsg': '下单失败'}) # 提交订单成功,显式的提交一次事务 transaction.savepoint_commit(save_id) # 清除购物车中已结算的商品 pl = redis_conn.pipeline() pl.hdel('carts_%s' % user.id, *selected) pl.srem('selected_%s' % user.id, *selected) pl.execute() # 响应提交订单结果 return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '下单成功', 'order_id': order.order_id})
补充:
除了昨天在设置中添加,还可以查询sql语句的方法是:
通过对象点query即可查询。