django之模型层

目录

模型层

一.模型层前期准备

1.django自带一个sqlite3小型数据库

该数据库对时间字段不敏感,有时候会展示错乱,所以我们习惯切换成常见的数据库,比如MySQL,django orm并不会自动帮你创建库,所以要提前准备好

2.django切换MySQL数据库

"""DATABASES配置:"""
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  # 指定数据库软件名称
        'NAME': 'django01',  # 指定库名
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'CHARSET': 'utf8'
    }
}

3.定义模型类

class User(models.Model):
	uid = models.AutoField(primary_key=True, verbose_name='编号')
	name = models.CharField(max_length=32, verbose_name='姓名')
	age = models.IntegerField(verbose_name='年龄')
	join_time = models.DateField(auto_now_add=True)
	"""
	日期字段
		DateTimeField:年月日时分秒
		DateField:年月日

	日期字段重要参数
		auto_now:每次操作数据并保存都会自动更新当前时间
		auto_now_add:只在创建数据的那一刻自动获取当前时间 之后如果不人为更改则不变
	"""

4.执行数据库迁移命令(模型类>>>表)

python manage.py makemigrations

python manage.py migrate

image

5.模型层测试准备

默认是不允许单独测试某个py文件,如果想要测试某个py文件(主要models.py)

  • 方式一:pycharm提供的测试环境

    python console命令行测试环境

    终端的形式,代码没法保存下来,关掉以后代码就没有了,不会保存下来,只是临时的

    image

  • 方式二:在任意空的py文件或者tests.py文件中

    '''复制mange.py文件的前四行代码 放入一个空白的py文件中 但是不要放入已有的文件中'''
    import os
    
    def main():
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djang04.settings')
        import django  # 这个导入文件只能放在这里,放在外面会报错
        django.setup()
    
        from app01 import models  # 这个导入文件也只能放在这里,放在外面会报错
        print(models.User.objects.filter())
    
    
    if __name__ == '__main__':
        main()
    
    

    image

6.ORM底层SQL语句

​ django orm底层还是SQL语句,我们是可以查看的

如果我们手上是一个QuerySet对象,那么可以直接点query查看SQL语句

image

如果想查看所有底层的SQL语句也可以在配置文件中添加日志记录

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

image

二.ORM常见查询关键字

  • create()

    创建数据,返回值就是当前创建的数据对象,可以通过点的方式去获取数据对象里面具体的某个字段的值

    1.当需要查询数据主键字段值的时候 可以使用pk忽略掉数据字段真正的名字
    2.在模型类中可以定义一个__str__方法 便于后续数据对象被打印展示的是查看方便
    3.Queryset中如果是列表套对象那么直接for循环和索引取值但是索引不支持负数
    4.虽然QuerySet支持索引但是当QuerySet没有数据的时候索引会报错 推荐使用first
    	1.create()
            创建数据 返回值就是当前创建的数据对象
            ps:还可以利用类实例化对象然后调用save方法创建
      	2.update()
      	 	 更新数据
    	3.delete()
    		 删除数据
    
    import os
    
    
    def main():
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djang04.settings')
        import django
        django.setup()
    
        from app01 import models
        print(models.User.objects.filter())
    
        res = models.User.objects.create(name='kevin', age=38)
        print(res)
        user_obj = models.User(name='oscar', age=20)
        user_obj.save()
        # 对象可以通过点的方式取值
        print(res.name)
        print(res.age)
        print(res.join_time)
        # 获取主键
        print(res.uid)
        print(res.pk)
    
    main()
    
    """
    查找主键的时候有俩中方式:
        1.我们可以通过主键名去使用点的方式去获取
        2.也可以通过点pk得方式去获取
            查找数据对象的主键以后我们创建表的时候可能会创建好多表,那么每个表中的主键名可能不一样,我们在要获取表对象的主键得时候,需要知道表的主键名,使用pk的话它会自动帮我们去找
    """
    
  • filter()

    筛选数据,返回值是个QuerySet对象(可以看成是列表套数据对象)

    1.括号内不写查询条件,默认就是查询所有
    2.括号内可以填写条件,并且支持多个,逗号隔开,默认是and关系
        res = models.User.objects.fileter(name='jason', age=18)
        print(res)  # <QuerySet [<User: 对象jason>]>
    
  • all()

    查询所有数据,返回值是一个QuerySet对象(可以看成是列表套数据对象)

    res = models.user.objects.all()
    print(res) # <QuerySet [<User: 对象jason>, <User: 对象kevin>, <User: 对象tony>]>
    
  • first()

    获取QuerySet对象中的第一个数据对象,如果空则返回None

    res = models.User.objects.all().first()
    print(res)  # <QuerySet [<User: 对象jason>]>
    
  • last()

    获取QuerySet对象中的最后一个数据对象,如果空则返回None

    res = models.User.objects.all().last()
    print(res)  # <QuerySet [<User: 对象tony>]>
    
  • get()

    直接根据条件查询具体的数据对象,但是条件不存在直接报错,不推荐使用

    直接根据条件查询具体的数据对象 但是条件不存在直接报错 不推荐使用
    
        res = models.User.objects.get(pk=2)
        print(res)  # 对象:kevin
    这个方法与filter方法对比:
        res = models.User.objects.get(pk=2)
        print(res)  # 对象:kevin
        res = models.User.objects.filter(pk=2).first()
        print(res)  # 对象:kevin
        虽然看起来get方法可能更简单一点,但是不推荐使用get方法
            当查询查询条件不存在的时候:
                res = models.User.objects.get(pk=21)
                print(res)  # 直接报错
                res = models.User.objects.filter(pk=21).first()
                print(res)  # None
    
  • values()

    指定查询字段,结果是QuerySet对象(可以看成是列表套字典数据)

    res = models.User.objects.values()
        print(res)   # [{},{}]
    
  • value_list()

    指定查询字段,结果是QuerySet对象(可以看成是列表套元组数据)

    res = models.User.objects.all().values_list('name','age')  # [(),(),(),()]
    
  • order_by()

    根据指定条件排序,默认是升序,字段前面加负号就是降序

    res = models.User.objects.all().order_by('age')
    models.User.objects.all().order_by('-age')
    print(res)
    
  • count()

    统计orm查询之后结果集中的数据格式

    res = models.User.objects.all().count()
    print(res)  # 3
    
  • distinct()

    针对重复的数据进行去重,一定要注意数据对象中的主键

  • exclude()

    针对括号内的条件取反进行数据查询QuerySet(可以看成是列表套数据对象)

    res = models.User.objects.exclude(pk=2)
        print(res)  # <QuerySet [<User: 对象:jason>, <User: 对象:tony>]>
    
  • reverse()

    针对已经排了序的结果做颠倒

  • exists()

    判断查询结果集是否有数据,返回布尔值,但是几乎不用因为所有数据自带布尔值

  • raw()

    执行SQL语句,还可以借助于模块

    from django.db import connection
    cursor = connection.cursor()
    cursor.execute("insert into user(name) VALUES ('lisa')")
    cursor.execute("update user set name='lisa' WHERE name='jennie'")
    cursor.execute("delete from user where name='lisa'")
    cursor.execute("select * from user")
    cursor.fetchone()
    cursor.fetchall()
    

三.ORM执行SQL语句

1.ORM编写SQL语句

有时候ORM的操作效率可能偏低,我们是可以自己编写SQL的

方式1:
	models.User.objects.raw('select * from app01_user;')
    
-----------------------------------------------------
方式2:
	from django.db import connection
    cursor.execute('select name from app01_user;')
    print(cursor.fetchall)

2.神奇的双下划线查询

queryset对象可以无限制的点queryset对象的方法

  • <1>.比较运算符

    类型 属性
    字段__gt 大于
    字段__lt 小于
    字段__gte 大于等于
    字段__lte 小于等于
    res = models.User.objects.filter(age__lte=18)
    print(res)  # # <QuerySet [<User: 对象:jason>]>
    
  • <2>.成员运算符

    字段__in
    
    res = models.User.objects.filter(age__in=(18, 28, 38))
    print(res)
    
  • <3>.范围查询(数字)

    字段名__range
    
    res = models.User.objects.filter(age__range=(18, 38))
    print(res)
    
  • <4>.模糊查询

    字段名__contains		不忽略大小写
    字段名__icontains		忽略大小写
    
    res = models.User.objects.filter(name__contains='j')
    print(res)  # <QuerySet [<User: 对象:jason>]>
    ---------------------------------------------------
    res = models.User.object.filter(name__icontains='j')
    print(res)  # <QuerySet [<User: 对象:jason>],  <User: 对象:Jerry>]>
    
  • <5>.日期处理

    字段__year
    字段__month
    字段__day
    

3.ORM外键字段的创建

  • MySQL外键关系复习

    一对多
    	外键字段建在多的一方
    多对多
    	外键字段统一建在第三张关系表
    一对一
    	建在任何一方都可以,但是建议建在查询频率较高的表中
    """
    ps:
    	关系的判断可以采用换位思考原则,熟练之后可以瞬间判断
    """
    
  • <1>.数据准备

    创建基础表(书籍表、出版社表、作者表、作者详情)

  • <2>.一对多

    ORM中外键字段建在多的一方
    	models.ForeignKey()
    会自动添加_id后缀
    
  • <3>.多对多

    ORM中有三种创建多对多字段的方式
    	models.ManyToManyField()
        
        方式1:直接在查询频率较高的表中填写字段即可,自动创建第三张关系表
        方式2:自己创建第三张关系表
        方式3:自己创建第三张关系表,但是还是要orm多对多字段做关联
    
  • <4>.一对一

    ORM中外键字段建在查询频率较高的表中
    	models.OneToOneField()
        会自动添加_id后缀
        publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
    	author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
    """
    django1.X针对models.ForeignKey()/models.OneToOneField()内不需要添加on_delete=models.CASCADE
    django2X及以上则需要添加on_delete=models.CASCADE
    """
    

4.外键字段数据操作

  • <1>.一对多

    针对一对多,插入数据可以直接填写表中的实际字段

    models.Book.objects.create(title='三国演义', price=888.88, publish_id=1)
    models.Book.objects.create(title='人性的弱点', price=777.55, publish_id=1)
    

    针对一对多,插入数据也可以填写表中的类中字段名

    publish_obj = models.Publish.objects.filter(pk=1).first()
    models.Publish.objects.create(title='水浒传', price=555.66, publish=publish_obj)
    
  • <2>.一对一

    一对一与一对多外键字段的操作是一致的

    既可以传数字也可以传对象

  • <3>.多对多

    models.ManToManyField(to='Author')
    	add()
        	添加数据,括号内即可以填写数字也可以填写数据对象,支持多个
            方法一:
            	book_obj = models.Book.object.filter(pk=3).first()
                book_obj.author.add(1)
                book_obj.author.add(2, 3)
    		方法二:
            	book_obj = models.Book.objects.filter(pk=3).first()
                author_obj1 = models.Author.objects.filter(pk=3).first()
                author_obj2 = models.Author.objects.filter(pk=4).first()
                book_obj.author.add(author_obj1, author_obj2)
    ---------------------------------------------------
    	remove()
        	删除数据,括号内既可以填写数字,也可以填写数据对象,支持多个
            	book_obj = models.Book.objects.filter(pk=3).first()
                author_obj1 = models.Author.objects.filter(pk=3).first()
                author_obj2 = models.Author.objects.filter(pk=4).first()
                book_obj.author.remove(1)
                book_obj.author.remove(2, 3)
            book_obj.author.remove(author_obj1)
            book_obj.author.remove(author_obj1, author_obj2)
    ---------------------------------------------------  	set()
        	修改数据,括号内必须是可迭代对象
            	book_obj.author.set([2, ])
    			book_obj.author.set([2, 3])
                book_obj.author.set([author_obj1, ])
                book_obj.author.set([author_obj1, author_obj2])
    --------------------------------------------------- 
    	clear()
        	清空指定数据,括号内不需要任何参数
            	book_obj.author.clear()
                
    

四.ORM跨表查询

1.正反向查询概念☆

  • 正向查询

    由外键字段所在的表数据查询关联的表数据 正向

  • 反向查询

    没有外键字段的表数据查询关联的表数据 反向

  • ORM跨表查询口诀

    正向查询按外键字段

    反向查询按表名小写

2.基于对象的跨表查询

正向查询

  • <1>.查询主键为1的书籍对应的出版社名称

    '''先根据条件获取数据对象'''
    	book_obj = models.Book.objects.filter(pk=1).first
    '''再判断正反向的概念,由书查出版社外键字段在书所在的表中,所有是正向查询'''
    	print(book_obj.publish.name)  # 出版社: 北方出版社
    
  • <2>.查询主键为4的书籍对应的作者姓名

    '''先根据条件获取数据'''
    	book_obj = models.Book.objects.filter(pk=4).first()
    '''再判断正反向的概念,由书查作者外键字段在书所在的表中,所有是正向查询''' 
    	print(book_obj.authors) # app01.Author.None
    '''因为一本书可能是多个作者联合出版,此时直接点authors返回的就是None,因此要再点all()才能查的出来'''
    	print(book_obj.authors.all())
    '''如果指向获取作者名也可以在all后面继续点values('name')'''
    	print(book_obj.authors.all().values('name'))
    
  • <3>.查询jason的电话号码

    '''先根据条件获取数据'''
    	author_obj = models.Author.objects.filter(name='jason').first()
    '''再判断正反向的概念,由作者查作者信息外键字段在作者所在的表中,所有是正向查询'''   
    	print(author_obj.author_detail.phone)
    

反向查询

  • <1>.查询北方出版社出版过的书籍

    '''先根据条件获取数据'''
    	publish_obj = models.Publish.objects.filter(name='北方出版社').first()
    '''再判断正反向的概念,由出版社查书籍外键字段在出版社所在的表中,所有是反向查询'''
    	print(publish_obj.book_set.all())  # 反向查询固定格式: 表名_set
    """
    注意:但凡所查的数据不止一个都需要在查询的后面加all(),否则返回的就是None
    """
    
  • <2>.查询jason写过的书籍

    查询思路与上一题一致
    
    author_obj = models.Author.objects.filter(name='jason').first()
    print(author_obj.book_set.all())
    
  • <3>.查询电话号码是110的作者姓名

    author_obj = models.AuthorDetail.objects.filter(phone=110).first()
    print(author_detail_obj.author)
    print(author_detail_obj.author.name)
    

3.基于双下划线的跨表查询

正向查询

  • <1>.查询主键为1的书籍对应的出版社名称

    res = models.Book.object.filter(pk=1).values('publish__name','title')
    print(res)
    
  • <2>.查询主键为4的书籍对应的作者姓名

    res = models.Book.object.filter(pk=4).values('author__name','title')
    print(res)
    
  • <3>.查询jason的电话号码

    res = models.Author.objects.filter(name='jason').values('author_detail__phone')
    print(res)
    

反向查询

  • <1>.查询北方出版社出版过的书籍名称和价格

    res = models.Publish.objects.filter(name='北方出版社').values('book__title','book__price','name')
    print(res)
    
  • <2>.查询jason写过的书籍名称

    res = models.Author.object.filter(name='jason').vblues('book__title', 'name')
    print(res)
    
  • <3>.查询电话号码110的作者姓名

    res = models.Author.object.filter(phone=110).values('author__name', 'phone')
    print(res)
    

4.跨表查询的进阶操作

正向查询

  • <1>.查询主键为1的书籍对应的出版社名称

    res = models.Publish.objects.filter(book__pk=1).values('name')
    print(res)
    
  • <2>.查询主键为4的书籍对应的作者姓名

    res = models.Author.objects.filter(book__pk=4).values('name','book__title')
    print(res)
    
  • <3>.3.查询jason的电话号码

    res = models.AuthorDetail.objects.filter(author__name='jason').values('phone')
    print(res)
    

反向查询

  • <4>.查询北方出版社出版过的书籍名称和价格

    res = models.Book.objects.filter(publish__name='北方出版社').values('title','price')
    print(res)
    
  • <5>.查询jason写过的书籍名称

    res = models.Book.objects.filter(authors__name='jason').values('title')
    print(res)
    
  • <6>.查询电话号码是110的作者姓名

    res = models.Author.objects.filter(author_detail__phone=110).values('name')
    print(res)
    

5.双下划线补充操作

当我们想要同时跨多张相关联的表去查询获取数据,可以使用多双下划线实现:表名__表名__字段名

查询主键为4的书籍对应的作者的电话号码
res = models.Book.objects.filter(pk=4).values('authors__author_detail__phone')
print(res)

res = models.AuthorDetail.objects.filter(author__book__pk=4).values('phone')
print(res)

res = models.Author.objects.filter(book__pk=4).values('author_detail__phone')
print(res)

五.聚合查询

1.聚合函数

我们常用的聚合函数有max、min、sum、avg、count

  • <1>.使用聚合函数之前需要导入模块

    from diango.db.models import Max, Min, Sum, Avg, Count
    
  • <2>.聚合函数的使用

    聚合函数通常情况下是配合分组一起使用的

  • <3>.关键字aggregate

    没有分组之前如果单纯的使用聚合函数,需要关键字aggregate

    查询所有书的平均价格

    res = models.Book.object.aggregate(Avg('price'))
    print(res)  # # {'price__avg': Decimal('183.117778')}
    

    image

    res = models.Book.object.aggregate(Max('pricr'), Min('price'), Sum('price'), Avg('price'), Count('pk'))
    print(res)
    

    image

六.分组查询

image

如果我们执行orm分组查询报错,并且有关键字 sql_mode strict mode

image

打开MySQL
输入>>>:show variables like '%mode%';

分组有一个特性 默认只能够直接获取分组的字段 其他字段需要使用方法
我们也可以忽略掉该特性 将sql_mode中only_full_group_by配置移除即可

示例1:

统计每一本书的作者个数
	res = models.Book.object.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
    print(res)
    
"""
	1.按照整条数据分组
		models.Book.objects.annotate()	按照一条条书籍记录分组
	2.按照表中某个字段分组
		models.Book.objects.values('title').annotate()	按照annotate之前values括号中指定的字段分组
"""

1.分组查询

annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数,所以使用前从django.db.models引入Avg, Max, Count, Sum(首字母大写))

2.返回值

分组后,用values取值,则返回值是QuerySet书籍类型里面为一个个字典
分组后,用values_list取值,则返回值是QuerySet数据类型里面为一个个元组

MySQL中的limit相当于ORM中的QuerySet数据类型的切片

3.分组查询关键字

annotate()  里面放聚合函数

1.values 或者 values_list 放在 annotate 前面:values 或者 values_list 是声明以什么字段分组,annotate 执行分组。
2.values 或者 values_list 放在annotate后面: annotate 表示直接以当前表的pk执行分组,values 或者 values_list 表示查询哪些字段, 并且要将 annotate 里的聚合函数起别名,在 values 或者 values_list 里写其别名。
3.filter放在 annotate 前面:表示where条件
4.filter放在annotate后面:表示having

跨表分组查询本质就是将关联表join成一张表,再按单表得到思路进行分组查询

七.F与Q查询

导入模块

from django.db.models import F,Q

F查询

能够帮助你直接获取到列表中某个字段对应的数据

注意:
在操作字符串类型的数据的时候, F不能够直接做到字符串的拼接
    # 1.查询库存数大于卖出数的书籍
    '''当查询条件不是明确的 也需要从数据库中获取 就需要使用F查询'''
    from django.db.models import F
    res = models.Book.objects.filter(kucun__gt=F('maichu'))
    print(res)
    # 2.将所有书的价格涨800
    models.Book.objects.update(price=F('price') + 800)
    # 3.将所有书的名称后面追加爆款
    from django.db.models.functions import Concat
    from django.db.models import Value
    models.Book.objects.update(title=Concat(F('title'), Value('新款')))

在用ORM进行查询的时候,我们会发现当查询条件不明确的时候不能执行语句,因此需要用到F查询

img

Q查询

    # 查询主键是1或者价格大于2000的书籍
    res = models.Book.objects.filter(pk=1, price__gt=2000)  # 逗号默认是and关系
    from django.db.models import Q
    res = models.Book.objects.filter(Q(pk=1), Q(price__gt=2000))  # 逗号是and
    res = models.Book.objects.filter(Q(pk=1) | Q(price__gt=2000))  # |是or
    res = models.Book.objects.filter(~Q(pk=1) | Q(price__gt=2000))  # ~是not
    print(res.query)

img

Q查询进阶操作

可以将查询条件的字段改为字符串形式

from django.db.models import Q
q_obj = Q()  # 1.先产生q对象
q_obj.cennector = 'or'  # 默认多个条件的连接是and可以修改成or
q_obj.children.append(('pk', 1))  # 2.添加查询条件
q_obj.children.append(('price__ge', 2000))  # 支持添加多个
res = models.Book.object.filter(q_obj)  # 查询支持直接填写q对象
print(res)

image

八.ORM查询优化

django orm默认都是惰性查询
	当orm的语句在后续的代码中真正需要使用的时候才会执行
django orm自带limit分页
	减轻数据库端以及服务端的压力

1.ORM查询优化之only(单表)

res =models.Book.objects.only('title', 'price')
print(res)  # queryset [数据对象、数据对象]
for obj in res:
    print(obj.title)
    print(obj.price)  # 点击括号内填写的字段不走SQL查询
    print(publish_time)  # 可以点击括号内没有得到字段获取数据,但是会走SQL查询比较浪费资源

点击括号内填写的字段不走SQL查询
image
可以点击括号内没有得到字段获取数据,但是会走SQL查询比较浪费资源
image

2.ORM查询优化之defer

与only刚好相反,点击括号内的会走SQL查询,但是点击括号内没有得到的字段不走SQL语句

res =models.Book.objects.defer('title', 'price')
print(res)  # queryset [数据对象、数据对象]
for obj in res:
    print(obj.title)
    print(obj.price)  # 点击括号内填写的字段,走SQL查询比较浪费资源
    print(publish_time)  # 点击括号内没有得到字段获取数据,不会走SQL查询

点击括号内填写的字段
image
点击括号内没有的字段
image

3.ORM查询优化之select_related(多表)

select_related括号内只能接收外键字段(一对多 一对一) 它会在内部自动拼表 得出的数据对象在点击表中数据的时候都不会再走数据库查询

res = models.Book.objects.all()
for obj in res:
    print(obj.publish.name)  # 每次查询都需要走SQL

image

res = models.Book.objects.select_related('publish')  # 先连表后查询封装
print(res)
for obj in res:
	print(obj.publish.name)  # 不在走SQL查询

image

res1 = models.Authors.objects.select_related('author')  # 括号内不支持多对多字段,其他两个都可以
print(res1)

image

4.ORM查询优化之prefetch_related(多表)

prefetch_related底层其实是子查询 将查询之后的结果也一次性封装到数据对象中 用户在使用的时候是感觉不出来的

res = models.Book.object.prefetch_related('publish')  # 子查询
for obj in res:
	print(obj.publish.name)

image
额外补充个知识:当表中已经有数据的情况下 添加新的字段需要指定一些参数

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish_time = models.DateTimeField(auto_now=True)

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    author = models.ManyToManyField(to='Author')
    '''
    当表中已经有数据的情况下 添加新的字段需要指定一些参数
        1.设置字段值存于为空    null=True
        2.设置字段默认值    default=1000
        3.在终端直接给出默认值
    '''
    storage_num = models.IntegerField(verbose_name='库存数', null=True)
    sale_num = models.IntegerField(verbose_name='卖出数目', default=1000)

    def __str__(self):
        return f'对象:{self.title}'

九.ORM事务操作

django orm中至少提供里三种开启事务的方式

方式一

在settings.py配置文件数据库相关添加键值对>>>:全局有效

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'day07',
        'USER': 'root',
        'PASSWORD': '123',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'CHARSET': 'utf8',
        "ATOMIC_REQUESTS": True,
    }
}
'''每次请求所涉及到的orm操作同属于一个事务'''

方式二

使用orm提供的装饰器>>>:局部有效

先导入装饰器所需要的模块

from django.db import transaction

然后使用装饰器开启事务

@transaction.atomic
def index():pass

方式三

with上下文管理>>>:局部有效

from django.db import transaction

def reg():
    with transaction.atomic():
        pass

十.ORM常用字段类型

AutoField(int自增列)

int自增列,必须填入参数primary_key=True。当model中如果没有自增列,则会自动创建一个列名为id的列

CharField(varchar)

verbose_name:字段的注释

max_length:字符长度

IntegerField(int)

一个整数类型,-2147483648 to 2147483647。(一般不用它来存手机号(位数也不够),直接用字符串存,)

BigIntergerField(bigint)

长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

DecimalField(decimal)

max_digits=8:总共位数

decimal_places=2:小数位

EmailField(vachar(254))

邮箱字段类型,本质还是vachar

DateField(date)

日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()

DateTimeField(datetime)

日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例。 auto_now:每次修改数据的时候都会自动更新当前时间 auto_now_add:只在创建数据的时候记录创建时间后续不会自动修改了

BooleanField(Field)(布尔值类型)

该字段传布尔值(False/Ture)数据库里面存0/1

TextField(Field)(文本类型)

该字段可以用来存大段内容(文章)没有字数限制

FileField(Field)(字符类型)

upload_to = “/data”

给该字段传一个文件对象,会自动将文件保存到/data目录下然后将文件路径保存到数据库中

/data/a.txt

ForeignKey()

外键

OneToOneField

一对一关系

ManyToManyField

多对多关系

ORM还支持自定义字段

django orm除了给你提供了很多字段类型之外,还支持你自定义字段

# 定义char类型

# 字段类内部都继承Field
class MyCharField(models.Field):
    # 字符类型都必须有max_length表示字符长度
    def __init__(self,max_length, *args, **kwargs):
        self.max_length = max_length
        # 调用父类的init方法
        super().__init__(max_length=max_length, *args, **kwargs)  # 一定要是关键字的形式传入

    def db_type(self, connection):
        # 返回真真正的数据类型及各种约束条件
        return 'char(%s)' % self.max_length

# 自定义字段使用
myfield = MyCharField(max_length=16,null=True)  # 可以为空

image

十一.ORM常用参数

1.字段参数

primary_key

主键

verbose_name

字段注释

max_length

字段长度

max_digits

小数总共多少位

decimal_places

小数点后面的位数

null

允许字段为空

default

字段默认值

unique

唯一值

db_index

给字段添加索引

2.DateField与DateTimeField参数

auto_now_add

配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。

auto_now

配置上auto_now=True,每次更新数据记录的时候会更新该字段

3.choices

当某个字段的可能性能够被列举完全的情况下使用(性别、学历、工作状态。。。)

class User(models.Model):
    name = models.CharField(max_length=32)
    info = MyCharField(max_length=64)
    # 提前列举好对应关系
    gender_choice = (
    	(1, '男性'),
        (2, '女性'),
        (3, '其他').
    )
    gender = models.IntegerField(choices=gender_choice,null=True)
user_obj = User.objects.filter(pk=1).first()
user_obj.gender 
user_obj.get_gender_display()

4.字段参数

to

设置关联的表

to_field

设置要关联的字段

on_delete

当删除关联表中的数据时,当前表与其关联的行的行为

1.models.CASCADE
	级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除
2.models.SET_NULL
	当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空
3.models.PORTECT
	当主表中的一行数据删除时,由于表中相关字段是手保护的外键,所以都不允许删除
4.models.SET_DEFAULT
	当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值
5.models.SET()
	当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数
6.models.DO_NOTHING
	什么都不做,一切都看数据库级别的约束,注意数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似

十二.多对多三种创建方式

1.自动创建

authors = models.ManyToManyField(to='Author')
"""
优势:第三张表自动创建,并且提供了add、remove、set、clear四种操作
劣势:第三张表无法创建更多的字段,扩展性较差
"""

2.手动创建

class Book(models.Model):
	title = models.CharField(max_length=32)
class Author(models.Model):
	name = models.CharField(max_length=32)
class Book2Author(models.Model):
	book = models.ForeignKey(to='Book')
	author = models.ForeignKey('Author')
	others = models.CharField(max_length=32)
	join_time = models.DateField(auto_now_add=True)
"""
优势:第三张表完全由自己创建,扩展性强
劣势:编写繁琐,并且不在支持add、remove、set、clear以及正反向概念
"""

3.半自动创建

class Book(models.Model):
	title = models.CharField(max_length=32)
	authors = models.ManyToManyField(to='Author',
					through='Book2Author',
					through_fields=('book','author')
					)
class Author(models.Model):
	name = models.CharField(max_length=32)
class Book2Author(models.Model):
	book = models.ForeignKey(to='Book', on_delete=models.CASCADE)
	other = models.CharField(max_length=32)
	join_time = models.DateField(auto_now_add=True)
"""
优势:第三张表完全由自己创建,扩展性强,正反向概念依然清晰可用
劣势:编写繁琐不再支持add、remove、set、clear
"""
posted @ 2022-12-14 20:23  dear丹  阅读(48)  评论(0编辑  收藏  举报