跳至内容

Django ORM

Django ORM(对象关系映射)允许开发者用 Python 类和对象来操作数据库,而无需直接编写 SQL 语句。本文介绍 Django 连接 MySQL 的配置方法、模型定义、数据增删改查、多表关联,以及 F/Q 查询、聚合、分组等进阶用法。

Django连接MySQL

Django默认用的数据库是sqlite3
在setting.py中找到关于SQL的配置项
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        }
    }

若要改为MySQL数据库要先在配置文件中配置
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'django_base',
            'USER': 'root',
            'PASSWORD': 'your_password',
            'HOST': '118.25.65.95',
            'PORT': '3306',
            'CHARSET': 'UTF8'
        }
    }
配置完成后需要做一个声明
	django默认用的是mysqldb模块链接MySQL但是该模块的兼容性不好需要手动改为用pymysql连接
    需要告诉django不要用默认的mysqldb 而是用pymysql
	在项目名下的init或者任意的应用名下的init文件中书写以下代码都可以
    	import pymysql
		pymysql.install_as_MySQLdb()
app配置单独的数据库
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME':'bms',           # 要连接的数据库,连接前需要创建好
        'USER':'root',        # 连接数据库的用户名
        'PASSWORD':'',        # 连接数据库的密码
        'HOST':'127.0.0.1',       # 连接主机,默认本级
        'PORT'3306            #  端口 默认3306
    }
    'app01': { #可以为每个app都配置自己的数据,并且数据库还可以指定别的,也就是不一定就是mysql,也可以指定sqlite等其他的数据库
        'ENGINE': 'django.db.backends.mysql',
        'NAME':'bms',           # 要连接的数据库,连接前需要创建好
        'USER':'root',        # 连接数据库的用户名
        'PASSWORD':'',        # 连接数据库的密码
        'HOST':'127.0.0.1',       # 连接主机,默认本级
        'PORT'3306            #  端口 默认3306
    }
}

ORM

关系映射表,Object Relational Mapping,简称ORM

作用:
    能够让一个不用sql语句的小白也能够通过python 面向对象的代码简单快捷的操作数据库
不足之处:
    封装程度太高 有时候sql语句的效率偏低 需要你自己写SQL语句
    
与MySQL对比
    									 

    对象									记录

    对象属性							   记录某个字段对应的值

如何创建表
	1先去models.py中定义一个类
    	class User(models.Model):
            id = models.AutoField(primary_key=True)
            username = models.CharField(max_length=32)
            password = models.IntegerField()
	2将数据同步到MySQL中
    	python3 manage.py makemigrations 
        	将操作记录记录到小本本上(migrations文件夹)
            相当于将ORM语句转换为SQL语句
		python3 manage.py migrate  
        	将操作真正的同步到数据库中
		只要你修改了models.py中跟数据库相关的代码 就必须重新执行上述的两条命令
 
__init__.py 配置
    import pymysql
    pymysql.install_as_MySQLdb()

settings.py 配置
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django',
        'USER': 'root',
        'PASSWORD': 'iZ!?kSxAgfBI_muydeMjR|4}t<*JlU/r',
        'HOST': '106.14.213.94',
        'PORT': '3306',
        'CHARSET': 'UTF-8'
    }
    
models.py 配置
	from django.db import models
    class User(models.Model):
        uid = models.AutoField(primary_key=True, verbose_name='主键')
        username = models.CharField(max_length=32, verbose_name='姓名')
        password = models.IntegerField(verbose_name='密码')

创建表的注意事项

由于一张表中必须要有一个主键字段,并且一般情况下都叫ID字段,所以ORM当你不定义主键字段的时候,ORM会自动帮你创建一个名为ID的主键字段,也就意味着,后续我们在创建模型表的时候如果主键字段没有额外的叫法,那么主键字段可以省略不写。

ORM常用字段

 常用字段对应表
    
    '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)',
1自增长字段
	id = models.AutoField(primary_key=True)  # 系统会默认添加此字段,无需用户自己特地添加
	id = models.BigAutoField()
    自增长字段的意思是数据表中每增加一条记录这个字段的值就会自动加1字段的类型默认为Int整型下面的BigAutoField字段可容纳比较大的数,比如说十亿但是需要注意的一点是其实我们在定义数据库表结构的时候并不需要特地定义这样一个字段因为Django会在每个表中自动添加一个id字段且这个字段的类型正是自增长型
    
2二进制字段
	Binary = models.BinaryField()
    在某些特殊情况下可以用此字段类型来插入二进制数据
    
3布尔型字段
	Boolean = models.BooleanField()
	NullBoolean = models.NullBooleanField()
    Django提供了两种布尔类型的字段上面这种不能为空下面这种的字段值可以为空
    
4整型字段
	PositiveSmallInteger = models.PositiveSmallIntegerField()  # 5个字节 正数
    SmallInteger = models.SmallIntegerField()  # 6个字节 正负数
    PositiveInteger = models.PositiveIntegerField()  # 10个字节 正数
    Integer = models.IntegerField()  # 11个字节 
    BigInteger = models.BigIntegerField()  # 20个字节
    Django提供了5种不同的整型字段可以按照两个标准来进行分类一个是按正负数来分Positive),一个是按数值大小来分SmallBig具体分类如上
    
5字符串类型
	Char = models.CharField()  # varchar
    Char = models.CharField(max_length=14verbose_name='用户名')
	Text = models.TextField()  # longtext
    字符串类型的字段分为两种上面这种对应数据库中的varchar需要在参数max_length中指定字符串的大小下面这种对应数据库中的longtext类型无需指定字符串长度想写多长就写多长
    
6时间类型
	Date = models.DateField()			# 年月日
    DateTime = models.DateTimeField()	# 年月日 时分秒
    Duration = models.DurationField()   # int, Python timedelta实现
    时间类型分为三种Date是年月日的形式DateTime是年月日时分秒的形式第三种表示一段时间在数据表中是Int类型它的底层是通过python的timedelta实现的
    
7浮点型
	Float = models.FloatField()
	Decimal = models.DecimalField()  # 11.22, 16.34
    浮点型也有两种其中第二种Decimal比较特殊需要在参数中指定整数有多少位小数有多少位
    
8其他类型
	Email = models.EmailField()  # 邮箱
    Image = models.ImageField()  # 图片
    File = models.FileField()    # 文件
    FilePath = models.FilePathField() # 文件路径
    URL = models.URLField()      # URL地址
    UUID = models.UUIDField()    # UID
    GenericIPAddress = models.GenericIPAddressField() #IP地址,IPV4或者IPV6
    
9关系型字段
OneToOneField一对一
    一对一表关系放在任意一个字段都可以但是建议放在常用的表中
    to="AuthorDetail"	关联的表
    to_field="nid"		关联哪个字段
    on_delete=models.CASCADE	级联删除
    
    
ForeignKey多对一
    建立一对多的关系,外键字段建立在多的一方
    to
        设置要关联的表

    to_field
        设置要关联的表的字段

    related_name
        反向操作时使用的字段名用于代替原反向查询时的'表名_set'
    related_query_name
        反向查询操作时使用的连接前缀用于替换表名

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

ManyToManyField多对多
    三种创建方式
        方式一自行创建第三张表
            class Book(models.Model):
                title = models.CharField(max_length=32, verbose_name="书名")


            class Author(models.Model):
                name = models.CharField(max_length=32, verbose_name="作者姓名")


            # 自己创建第三张表,分别通过外键关联书和作者
            class Author2Book(models.Model):
                author = models.ForeignKey(to="Author")
                book = models.ForeignKey(to="Book")

                class Meta:
                    unique_together = ("author", "book")
        方式二通过ManyToManyField自动创建第三张表
        	class Book(models.Model):
                title = models.CharField(max_length=32, verbose_name="书名")


            # 通过ORM自带的ManyToManyField自动创建第三张表
            class Author(models.Model):
                name = models.CharField(max_length=32, verbose_name="作者姓名")
                books = models.ManyToManyField(to="Book", related_name="authors")  #自动生成的第三张表我们是没有办法添加其他字段的
                
        方式三设置ManyTomanyField并指定自行创建的第三张表称为中介模型
        	class Book(models.Model):
                title = models.CharField(max_length=32, verbose_name="书名")


            # 自己创建第三张表,并通过ManyToManyField指定关联
            class Author(models.Model):
                name = models.CharField(max_length=32, verbose_name="作者姓名")
                books = models.ManyToManyField(to="Book", through="Author2Book", 	
				through_fields=("author", "book"))
                # through_fields接受一个2元组('field1','field2'):
                # 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。


            class Author2Book(models.Model):
                author = models.ForeignKey(to="Author")
                book = models.ForeignKey(to="Book")
                #可以扩展其他的字段了
                class Meta:
                    unique_together = ("author", "book")
                    
	创建多对多表的时候的一些参数
    	多对多的参数
            to
                设置要关联的表

            related_name
                同ForeignKey字段

            related_query_name
                同ForeignKey字段
            through
                在使用ManyToManyField字段时Django将自动生成一张表来管理多对多的关联关系

                但我们也可以手动创建第三张表来管理多对多关系此时就需要通过        
            through来指定第三张表的表名

            through_fields
                设置关联的字段

            db_table
                默认创建第三张表时数据库中表的名称            

                    
		当我们需要在第三张关系表中存储额外的字段时就要使用第三种方式第三种方式还是可以使用多对多关联关系操作的接口alladdclear等等

		当我们使用第一种方式创建多对多关联关系时就无法使用orm提供的setaddremoveclear方法来管理多对多的关系了
字段参数
在原生SQL定义数据表的时候我们常常需要给字段设定一些参数比如说是否为空默认值等等那么在Django的模型类中我们该如何设置呢首先我们要知道Django字段的参数分如下三种情况

1所有字段都具有的参数
    1.更改字段名db_colum= 
    2.设置主键primary_key=True默认为False
    3.给字段设置别名备注):verbose_name= 
    4.字段的唯一键属性unique=True设置之后这个字段的每一条记录的每个值是唯一的
    5.允许字段为空null=True数据库中字段可以为空),blank=True网页表单提交内容可以为空),切记不可以将null设置为Fasle的同时还把blank设置为True会报错的
    6.给字段建立索引db_index=True
    7.在表单中显示说明help_text= 
    8.字段值不允许更改editable=False默认是True可以更改
    9.字段的默认值default
    	可以是一个值或者可调用对象如果可调用 每有新对象被创建它都会被调用如果你的字段没有设置可以为空那么将来如果我们后添加一个字段这个字段就要给一个default值
 	10.choices
    	由二元组组成的一个可迭代对象例如列表或元组),用来给字段提供选择项 如果设置了choices 默认的表单将是一个选择框而不是标准的文本框<br>而且这个选择框的选项就是choices 中的选项
	11.auto_now_add
    	配置auto_now_add=True创建数据记录的时候会把当前时间添加到数据库
	12.auto_now
    	配置上auto_now=True每次更新数据记录的时候会更新该字段标识这条记录最后一次的修改时间
	
2个别字段才拥有的参数
    1.CharField(max_length=100)字段长度为utf8编码的100个字符串
    2.DateField(unique_for_date=True)这个字段的时间必须唯一
    3.DateField(auto_now=True)对这条记录内容更新的时间
    4.DateField(auto_now_add=True)插入这条记录的时间
    5.DecimalField(max_digits=4, decimal_places=2)前者表示整数和小数总共多少数后者表示小数点的位数

3关系型字段的参数
    on_delelte= 这个参数表示外键所关联的对象被删除的时候要进行什么操作比如说一篇文章对应一个Python的分类但是这个分类被你不小心删除了那么这篇文章的分类应该会发生什么变化呢主要有以下六种

    CASCADE模拟SQL语言中的ON DELETE CASCADE约束将定义有外键的模型对象同时删除!(该操作为当前Django版本的默认操作!)
    PROTECT阻止上面的删除操作但是弹出ProtectedError异常
    SET_NULL将外键字段设为null只有当字段设置了null=True时方可使用该值
    SET_DEFAULT:将外键字段设为默认值只有当字段设置了default参数时方可使用
    DO_NOTHING什么也不做
    SET()设置为一个传递给SET()的值或者一个回调函数的返回值注意大小写
    
4自关联字段参数
	需要在第一个参数中添加self字符串或写上它自己的表名模型类名)。

补充

(1)null
 
如果为TrueDjango 将用NULL 来在数据库中存储空值 默认值是 False.
 
(1)blank
 
如果为True该字段允许不填默认为False
要注意这与 null 不同null纯粹是数据库范畴的 blank 是数据验证范畴的
如果一个字段的blank=True表单的验证将允许该字段是空值如果字段的blank=False该字段就是必填的
 
(2)default
 
字段的默认值可以是一个值或者可调用对象如果可调用 每有新对象被创建它都会被调用如果你的字段没有设置可以为空那么将来如果我们后添加一个字段这个字段就要给一个default值
 
(3)primary_key
 
如果为True那么这个字段就是模型的主键如果你没有指定任何一个字段的primary_key=True
Django 就会自动添加一个IntegerField字段做为主键所以除非你想覆盖默认的主键行为
否则没必要设置任何一个字段的primary_key=True
 
(4)unique
 
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的
 
(5)choices
由二元组组成的一个可迭代对象例如列表或元组),用来给字段提供选择项 如果设置了choices 默认的表单将是一个选择框而不是标准的文本框<br>而且这个选择框的选项就是choices 中的选项
(6)db_index
    如果db_index=True 则代表着为此字段设置数据库索引


DatetimeFieldDateFieldTimeField这个三个时间字段都可以设置如下属性

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

(8)auto_now
    配置上auto_now=True每次更新数据记录的时候会更新该字段标识这条记录最后一次的修改时间
    
(9) choices属性
    sex = models.IntegerField(choices=((1, '男性'), (2, '女性')))

    数据库里面存的是1或者2
    通过model模型类对象.get_属性名称_display()可以获取到数字对应的文本内容
    
auto_now_add和auto_now参数
	class t1(models.Model):
    # defauit 在使用orm操作添加数据时生效.
    name = models.CharField(max_length=12, default='张三')
    sex = models.IntegerField(choices=((1, '男性'), (2, '女性')))

    d1 = models.DateTimeField(auto_now_add=True,null=True)  #自动添加创建记录的时间
    d2 = models.DateTimeField(auto_now=True,null=True) #自动添加创建记录的时间,更新记录是也能自动更新为最新时间

auto_now 自动更新时间只有在save更新时才生效,update不生效
所以如果要做更新时间的处理,那么最好手动获取当前时间并修改
redirect括号内可以直接写URL,也可以直接写别名,但是如果别名需要额外给参数的话,那么就必须使用reverse解析

字段的增删改查

字段的增加
	直接在文件中增加就可以了
字段的修改
    直接修改代码然后执行数据库同步的两条命令即可

字段的删除
    直接注释对应的字段然后执行数据库迁移的两条命令即可
     执行完毕之后字段对应的数据也都没有了
        因此在操作models.py文件时候一定要仔细执行迁移的命令之前最好先检查一下自己的代码
在增加字段的时候若没有指定默认值则会抛出异常
You are trying to add a non-nullable field 'hobby' to user without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option: 
    解决方法有三种
        1直接在终端中给出默认值
        2在增加字段的时候指定该字段可以为空
        	info = models.CharField(max_length=32,verbose_name='个人简介',null=True)
        3在增加字段时候直接设置默认值
            hobby = models.CharField(max_length=32,verbose_name='兴趣爱好',default='study')

数据的增加

方法一
	def index(request):
        # 增加
        obj = models.Book(
            bid='1',
            title='三国演义',
            price=9.99,
            pub_date=datetime.datetime.now(),
            publish='红浪漫出版社',
        )
        obj.save()
        
方法二
	    models.Book.objects.create(
        bid='2',
        title='红楼梦',
        price=19.99,
        pub_date=datetime.datetime.now(),
        publish='新华出版社',
    )
    
批量添加
    obj_list = []
    for i in range(3,10):
        obj = models.Book(
            bid=i,
            title='西游记'+ str(i),
            price=99.99,
            pub_date=datetime.datetime.now(),
            publish='夕阳红出版社',
        )
        obj_list.append(obj)
    models.Book.objects.bulk_create(obj_list)
    

数据的修改

方法一批量更新只修改被修改过的字段
    models.Book.objects.filter(bid=4).update(
            title = '水浒传',
            price = 59.99
        )
    
方法二
	obj = models.Book.objects.get(bid=6)
    obj.price = 18
    obj.save()
    
    
方式二全部更新从头到尾将数据的所有字段全部更新一边 无论该字段是否被修改
    edit_obj.username = username
    edit_obj.password= password
    edit_obj.save()

数据的删除

方式一批量删除只删除被修改过的字段
	models.Book.objects.filter(title='三国演义').delete()
    
方式二
    models.Book.objects.get(id=3).delete()   
    query类型数据和模型类对象都可以调用delete方法来进行删除
    
	在生产中删除数据内部其实并不是真正的删除 我们会给数据添加一个标识字段用来表示当前数据是否被删除了如果数据被删了仅仅只是将字段修改一个状态

数据的查询

<1> all()
	查询所有结果结果是queryset类型
  
<2> filter(**kwargs)
	它包含了与所给筛选条件相匹配的对象结果也是queryset类型 
    Book.objects.filter(title='linux',price=100) # 里面的多个条件用逗号分开,并且这几个条件必须都成立,是and的关系,or关系的我们后面再学,直接在这里写是搞不定or的
  
<3> get(**kwargs):          
    返回与所给筛选条件相匹配的对象不是queryset类型是行记录对象返回结果有且只有一个
	如果符合筛选条件的对象超过一个或者没有都会抛出错误捕获异常try  
    	Book.objects.get(id=1)
  
<4> exclude(**kwargs):      
    排除的意思它包含了与所给筛选条件不匹配的对象没有不等于的操作用这个exclude返回值是queryset类型Book.objects.exclude(id=6)返回id不等于6的所有的对象或者在queryset基础上调用Book.objects.all().exclude(id=6)
                 
<5> order_by(*field):
    queryset类型的数据来调用对查询结果排序,默认是按照id来升序排列的返回值还是queryset类型
	models.Book.objects.all().order_by('price','id') #直接写price,默认是按照price升序排列,按照字段降序排列,就写个负号就行了order_by('-price'),order_by('price','id')是多条件排序,按照price进行升序,price相同的数据,按照id进行升序
        
<6> reverse():              
    queryset类型的数据来调用对查询结果反向排序返回值还是queryset类型
  
<7> count():                
    queryset类型的数据来调用返回数据库中匹配查询(QuerySet)的对象数量
  
<8> first():                
    queryset类型的数据来调用返回第一条记录 
    Book.objects.all()[0] = Book.objects.all().first() # 得到的都是model对象,不是queryset
  
<9> last():                
    queryset类型的数据来调用返回最后一条记录
  
<10> exists():              
    queryset类型的数据来调用如果QuerySet包含数据就返回True否则返回False
	空的queryset类型数据也有布尔值True和False但是一般不用它来判断数据库里面是不是有数据如果有大量的数据你用它来判断那么就需要查询出所有的数据效率太差了用count或者exits
	all_books = models.Book.objects.all().exists() #翻译成的sql是SELECT (1) AS `a` FROM `app01_book` LIMIT 1,就是通过limit 1,取一条来看看是不是有数据

<11> values(*field):        
    用的比较多queryset类型的数据来调用返回一个ValueQuerySet——一个特殊的QuerySet运行后得到的并不是一系列model的实例化对象而是一个可迭代的字典序列,只要是返回的queryset类型就可以继续链式调用queryset类型的其他的查找方法其他方法也是一样的
    
<12> values_list(*field):   
    它与values()非常相似它返回的是一个元组序列values返回的是一个字典序列
 
<13> distinct():            
    values和values_list得到的queryset类型的数据来调用从返回结果中剔除重复纪录

基于双下划线的模糊查询

神奇的双下划线查询
# 神奇的双下划线查询
    # 1 年龄大于35岁的数据
    # res = models.User.objects.filter(age__gt=35)
    # print(res)
    # 2 年龄小于35岁的数据
    # res = models.User.objects.filter(age__lt=35)
    # print(res)
    # 大于等于 小于等于
    # res = models.User.objects.filter(age__gte=32)
    # print(res)
    # res = models.User.objects.filter(age__lte=32)
    # print(res)

    # 年龄是18 或者 32 或者40
    # res = models.User.objects.filter(age__in=[18,32,40])
    # print(res)

    # 年龄在18到40岁之间的  首尾都要
    # res = models.User.objects.filter(age__range=[18,40])
    # print(res)

    # 查询出名字里面含有s的数据  模糊查询
    # res = models.User.objects.filter(name__contains='s')
    # print(res)
    #
    # 是否区分大小写  查询出名字里面含有p的数据  区分大小写
    # res = models.User.objects.filter(name__contains='p')
    # print(res)
    # 忽略大小写
    # res = models.User.objects.filter(name__icontains='p')
    # print(res)

    # res = models.User.objects.filter(name__startswith='j')
    # res1 = models.User.objects.filter(name__endswith='j')
    #
    # print(res,res1)


    # 查询出注册时间是 2020 1月
    # res = models.User.objects.filter(register_time__month='1')
    # res = models.User.objects.filter(register_time__year='2020')

表与表关系

表与表之间的关系
	一对多
    多对多
    一对一
    没有关系
    判断表关系的方法换位思考
    
图书和出版社是一对多的关系 外键字段建在多的那一方 book	
图书和作者是多对多的关系 需要创建第三张表来专门存储
作者与作者详情表是一对一

# 在django1.X版本中外键默认都是级联更新删除的
# 多对多的表关系可以有好几种创建方式 这里暂且先介绍一种
# 针对外键字段里面的其他参数 暂时不要考虑 如果感兴趣自己可以百度试试看

单表操作

多表关系创建

from django.db import models

# Create your models here.
# 作者表
class Author(models.Model): #比较常用的信息放到这个表里面
    name=models.CharField( max_length=32)
    age=models.IntegerField()  #int
    # 与AuthorDetail建立一对一的关系,一对一的这个关系字段写在两个表的任意一个表里面都可以
    ad = models.OneToOneField(to="AuthorDetail", to_field='id', on_delete=models.CASCADE)
    # authordetail_id
    # foreign key + unique


# 作者详细信息表
class AuthorDetail(models.Model):#不常用的放到这个表里面
    birthday=models.DateField()
    telephone=models.CharField(max_length=11)
    addr=models.CharField(max_length=64)


# 出版社表
class Publish(models.Model):
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)


# 书籍表
class Book(models.Model):
    title = models.CharField( max_length=32)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)
    # publish=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE)
    publish=models.ForeignKey(to="Publish") #默认级联删除,默认关联的是另外一张表的id字段
    authors=models.ManyToManyField(to='Author',) #自动创建第三张表,id author_id book_id,不会作为本表的字段出现

一对一外键的增删改查

    models.AuthorDetail.objects.create(
        birthday='2018-01-01',
        telephone='13800000000',
        addr='北京'
    )
    ad_obj = models.AuthorDetail.objects.get(id=1)
    models.Author.objects.create(
        name='明皓',
        age=38,
        # ad=ad_obj,
        ad_id=2,
    )
    ad_obj = models.AuthorDetail.objects.get(id=4)
    obj= models.Author(
        name='杨浩',
        age=47,
        ad=ad_obj,
        # ad_id=3,
    )
    obj.save()

一对多外键的增删改查

一对多外键增删改查
# 一对多外键增删改查
    # 增
    # 1  直接写实际字段 id
    # models.Book.objects.create(title='论语',price=899.23,publish_id=1)
    # models.Book.objects.create(title='聊斋',price=444.23,publish_id=2)
    # models.Book.objects.create(title='老子',price=333.66,publish_id=1)
    # 2  虚拟字段 对象
    # publish_obj = models.Publish.objects.filter(pk=2).first()
    # models.Book.objects.create(title='红楼梦',price=666.23,publish=publish_obj)

    # 删
    # models.Publish.objects.filter(pk=1).delete()  # 级联删除

    # 修改
    # models.Book.objects.filter(pk=1).update(publish_id=2)
    # publish_obj = models.Publish.objects.filter(pk=1).first()
    # models.Book.objects.filter(pk=1).update(publish=publish_obj)
    

多对多外键的增删改查

# 如何给书籍添加作者?
    book_obj = models.Book.objects.filter(pk=1).first()
    # print(book_obj.authors)  # 就类似于你已经到了第三张关系表了
    # book_obj.authors.add(1)  # 书籍id为1的书籍绑定一个主键为1 的作者
    # book_obj.authors.add(2,3)

    # author_obj = models.Author.objects.filter(pk=1).first()
    # author_obj1 = models.Author.objects.filter(pk=2).first()
    # author_obj2 = models.Author.objects.filter(pk=3).first()
    # book_obj.authors.add(author_obj)
    # book_obj.authors.add(author_obj1,author_obj2)
    """
    add给第三张关系表添加数据
        括号内既可以传数字也可以传对象 并且都支持多个
    """

    # 删
    # book_obj.authors.remove(2)
    # book_obj.authors.remove(1,3)

    # author_obj = models.Author.objects.filter(pk=2).first()
    # author_obj1 = models.Author.objects.filter(pk=3).first()
    # book_obj.authors.remove(author_obj,author_obj1)
    """
    remove
        括号内既可以传数字也可以传对象 并且都支持多个
    """


    # 修改
    # book_obj.authors.set([1,2])  # 括号内必须给一个可迭代对象
    # book_obj.authors.set([3])  # 括号内必须给一个可迭代对象

    # author_obj = models.Author.objects.filter(pk=2).first()
    # author_obj1 = models.Author.objects.filter(pk=3).first()
    # book_obj.authors.set([author_obj,author_obj1])  # 括号内必须给一个可迭代对象

    """
    set
        括号内必须传一个可迭代对象,该对象内既可以数字也可以对象 并且都支持多个
    """


    # 清空
    # 在第三张关系表中清空某个书籍与作者的绑定关系
    book_obj.authors.clear()
    """
    clear
        括号内不要加任何参数
    
    """

批量插入数据

def ab_pl(request):
    # 先给Book插入一万条数据
    # for i in range(10000):
    #     models.Book.objects.create(title='第%s本书'%i)
    # # 再将所有的数据查询并展示到前端页面
    book_queryset = models.Book.objects.all()

    # 批量插入
    # book_list = []
    # for i in range(100000):
    #     book_obj = models.Book(title='第%s本书'%i)
    #     book_list.append(book_obj)
    # models.Book.objects.bulk_create(book_list)
    """
    当你想要批量插入数据的时候 使用orm给你提供的bulk_create能够大大的减少操作时间
    :param request: 
    :return: 
    """
    return render(request,'ab_pl.html',locals())

自定义分页器

级联更新删除

    一对一和一对多 ,基本和单表一样(级联删除)
    models.Author.objects.get(id=1).delete()
    models.AuthorDetail.objects.get(id=2).delete()
    models.Book.objects.get(id=1).delete()

Django测试脚本


"""
当你只是想测试django中的某一个py文件内容 那么你可以不用书写前后端交互的形式
而是直接写一个测试脚本即可

脚本代码无论是写在应用下的tests.py还是自己单独开设py文件都可以
"""
# 测试环境的准备 去manage.py中拷贝前四行代码 然后自己写两行
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
    import django
    django.setup()
    # 在这个代码块的下面就可以测试django里面的单个py文件了
    

查看内部SQL语句的方式

# 方式1
    res = models.User.objects.values_list('name','age')  # <QuerySet [('jason', 18), ('egonPPP', 84)]>
    print(res.query)
    queryset对象才能够点击query查看内部的sql语句

# 方式2:所有的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',
            },
        }
    }

正向反向概念

正反向的概念
# 正向
# 反向
	外键字段在我手上那么我查你就是正向
  外键字段如果不在手上我查你就是反向
  
  book >>>外键字段在书那儿(正向)>>> publish
  publish	>>>外键字段在书那儿(反向)>>>book
  
  一对一和多对多正反向的判断也是如此
  
"""
正向查询按字段
反向查询按表名小写
				_set
				...
"""
db_constraint=False

多表查询

多表查询
	子查询(基于对象的跨表查询)
# 1.查询书籍主键为1的出版社
    # book_obj = models.Book.objects.filter(pk=1).first()
    # # 书查出版社 正向
    # res = book_obj.publish
    # print(res)
    # print(res.name)
    # print(res.addr)

    # 2.查询书籍主键为2的作者
    # book_obj = models.Book.objects.filter(pk=2).first()
    # # 书查作者 正向
    # # res = book_obj.authors  # app01.Author.None
    # res = book_obj.authors.all()  # <QuerySet [<Author: Author object>, <Author: Author object>]>
    #
    # print(res)

    # 3.查询作者jason的电话号码
    # author_obj = models.Author.objects.filter(name='jason').first()
    # res = author_obj.author_detail
    # print(res)
    # print(res.phone)
    # print(res.addr)

    """
    在书写orm语句的时候跟写sql语句一样的
    不要企图一次性将orm语句写完 如果比较复杂 就写一点看一点
    
    正向什么时候需要加.all()
        当你的结果可能有多个的时候就需要加.all()
        如果是一个则直接拿到数据对象
            book_obj.publish
            book_obj.authors.all()
            author_obj.author_detail
    """
    # 4.查询出版社是东方出版社出版的书
    # publish_obj = models.Publish.objects.filter(name='东方出版社').first()
    # 出版社查书  反向
    # res = publish_obj.book_set  # app01.Book.None
    # res = publish_obj.book_set.all()
    # print(res)

    # 5.查询作者是jason写过的书
    # author_obj = models.Author.objects.filter(name='jason').first()
    # 作者查书      反向
    # res = author_obj.book_set  # app01.Book.None
    # res = author_obj.book_set.all()
    # print(res)

    # 6.查询手机号是110的作者姓名
    # author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
    # res = author_detail_obj.author
    # print(res.name)
    """
    基于对象 
        反向查询的时候
            当你的查询结果可以有多个的时候 就必须加_set.all()
            当你的结果只有一个的时候 不需要加_set.all()
        自己总结出 自己方便记忆的即可 每个人都可以不一样
    """

联表查询

联表查询(基于双下划线的跨表查询)
# 基于双下划线的跨表查询


    # 1.查询jason的手机号和作者姓名
    # res = models.Author.objects.filter(name='jason').values('author_detail__phone','name')
    # print(res)
    # 反向
    # res = models.AuthorDetail.objects.filter(author__name='jason')  # 拿作者姓名是jason的作者详情
    # res = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__name')
    # print(res)


    # 2.查询书籍主键为1的出版社名称和书的名称
    # res = models.Book.objects.filter(pk=1).values('title','publish__name')
    # print(res)
    # 反向
    # res = models.Publish.objects.filter(book__id=1).values('name','book__title')
    # print(res)

    # 3.查询书籍主键为1的作者姓名
    # res = models.Book.objects.filter(pk=1).values('authors__name')
    # print(res)
    # 反向
    # res = models.Author.objects.filter(book__id=1).values('name')
    # print(res)


    # 查询书籍主键是1的作者的手机号
    # book author authordetail
    # res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
    # print(res)
    """
    你只要掌握了正反向的概念
    以及双下划线
    那么你就可以无限制的跨表

聚合查询

聚合查询
# 聚合查询      aggregate
    """
    聚合查询通常情况下都是配合分组一起使用的
    只要是跟数据库相关的模块 
        基本上都在django.db.models里面
        如果上述没有那么应该在django.db里面
    """
    from app01 import models
    from django.db.models import Max,Min,Sum,Count,Avg
    # 1 所有书的平均价格
    # res = models.Book.objects.aggregate(Avg('price'))
    # print(res)
    # 2.上述方法一次性使用
    res = models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Count('pk'),Avg('price'))
    print(res)

分组查询

分组查询
# 分组查询  annotate
    """
    MySQL分组查询都有哪些特点
        分组之后默认只能获取到分组的依据 组内其他字段都无法直接获取了
            严格模式
                ONLY_FULL_GROUP_BY
                
    """
    from django.db.models import Max, Min, Sum, Count, Avg
    # 1.统计每一本书的作者个数
    # res = models.Book.objects.annotate()  # models后面点什么 就是按什么分组
    # res = models.Book.objects.annotate(author_num=Count('authors')).values('title','author_num')
    """
    author_num是我们自己定义的字段 用来存储统计出来的每本书对应的作者个数
    """
    # res1 = models.Book.objects.annotate(author_num=Count('authors__id')).values('title','author_num')
    # print(res,res1)
    """
    代码没有补全 不要怕 正常写
    补全给你是pycharm给你的 到后面在服务器上直接书写代码 什么补全都没有 颜色提示也没有
    
    """

    # 2.统计每个出版社卖的最便宜的书的价格(作业:复习原生SQL语句 写出来)
    # res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
    # print(res)

    # 3.统计不止一个作者的图书
        # 1.先按照图书分组 求每一本书对应的作者个数
        # 2.过滤出不止一个作者的图书
    # res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title','author_num')
    # """
    # 只要你的orm语句得出的结果还是一个queryset对象
    # 那么它就可以继续无限制的点queryset对象封装的方法
    #
    # """
    # print(res)

    # 4.查询每个作者出的书的总价格
    # res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
    # print(res)

    """
    如果我想按照指定的字段分组该如何处理呢?
        models.Book.objects.values('price').annotate()
    后续BBS作业会使用
    
    
    你们的机器上如果出现分组查询报错的情况
        你需要修改数据库严格模式
    """

F与Q查询

# F查询
    # 1.查询卖出数大于库存数的书籍
    # F查询
    """
    能够帮助你直接获取到表中某个字段对应的数据
    """
    from django.db.models import F
    # res = models.Book.objects.filter(maichu__gt=F('kucun'))
    # print(res)


    # 2.将所有书籍的价格提升500块
    # models.Book.objects.update(price=F('price') + 500)


    # 3.将所有书的名称后面加上爆款两个字
    """
    在操作字符类型的数据的时候 F不能够直接做到字符串的拼接
    """
    from django.db.models.functions import Concat
    from django.db.models import Value
    models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
    # models.Book.objects.update(title=F('title') + '爆款')  # 所有的名称会全部变成空白

# Q查询
    # 1.查询卖出数大于100或者价格小于600的书籍
    # res = models.Book.objects.filter(maichu__gt=100,price__lt=600)
    """filter括号内多个参数是and关系"""
    from django.db.models import Q
    # res = models.Book.objects.filter(Q(maichu__gt=100),Q(price__lt=600))  # Q包裹逗号分割 还是and关系
    # res = models.Book.objects.filter(Q(maichu__gt=100)|Q(price__lt=600))  # | or关系
    # res = models.Book.objects.filter(~Q(maichu__gt=100)|Q(price__lt=600))  # ~ not关系
    # print(res)  # <QuerySet []>

    # Q的高阶用法  能够将查询条件的左边也变成字符串的形式
    q = Q()
    q.connector = 'or'
    q.children.append(('maichu__gt',100))
    q.children.append(('price__lt',600))
    res = models.Book.objects.filter(q)  # 默认还是and关系
    print(res)

事务


"""
事务
	ACID
		原子性
			不可分割的最小单位
		一致性
			跟原子性是相辅相成
		隔离性
			事务之间互相不干扰
		持久性
			事务一旦确认永久生效
	
	事务的回滚 
		rollback
	事务的确认
		commit
"""
# 目前你只需要掌握Django中如何简单的开启事务
# 事务
    from django.db import transaction
    try:
        with transaction.atomic():
            # sql1
            # sql2
            ...
            # 在with代码快内书写的所有orm操作都是属于同一个事务
    except Exception as e:
        print(e)
    print('执行其他操作')
最后更新于