9_20orm中的关键字段,orm查询13方法整合,查询优化和事务。

一。常用字段。

  在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字段对应关系
字段与sql语句对应

  字段对应的参数

  字段中也需要一些参数设置。

  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即可查询。

 

posted on 2019-09-20 20:14  一只萌萌哒的提莫  阅读(248)  评论(0编辑  收藏  举报