Django中的ORM及操作

1 ORM

  1. 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术
  2. 简单说,ORM通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中
  3. ORM在业务逻辑层和数据库层之间充当了桥梁的作用

2 Django中的ORM

2.1 model

​ 在Django中model是你数据的单一、明确的信息来源。它包含了你存储的数据的重要字段和行为。通常,一个模型(model)映射到一个数据库表,基本情况:

  • 每个模型都是一个Python类,它是django.db.models.Model的子类。

  • 模型的每个属性都代表一个数据库字段。

  • 综上所述,Django为您提供了一个自动生成的数据库访问API,详询>>官方文档链接

    ORMDB
    <—>数据表
    对象<—>数据行
    属性<—>字段
2.2 配置数据库
2.2.1 配置文件

​ 在Django项目的settings.py文件中,配置数据库连接信息

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": "database",  
        "USER": "user",
        "PASSWORD": "pwd",
        "HOST": "host",
        "POST": 3306
    }
}
2.2.2 安装依赖

​ 在Django项目的init.py文件中写如下代码,告诉Django使用pymysql模块连接MySQL数据库

import pymysql

pymysql.install_as_MySQLdb()

# 需要 pip install pymysql
2.2.3 创建表
  1. 定义模型类

    定义了一个 Person 模型,包含 first_namelast_name

    from django.db import models
    
    class Person(models.Model):
        first_name = models.CharField(max_length=30)
        last_name = models.CharField(max_length=30)
    

    first_namelast_name 是模型的字段。每个字段被指定为一个类属性,每个属性映射到一个数据库列。

    上面的 Person 模型将会像这样创建一个数据库表:

    CREATE TABLE myapp_person (
        "id" serial NOT NULL PRIMARY KEY,
        "first_name" varchar(30) NOT NULL,
        "last_name" varchar(30) NOT NULL
    );
    

    说明:

    • 表myapp_person的名称是自动生成的,如果你要自定义表名,需要在model的Meta类中指定 db_table 参数,强烈建议使用小写表名,特别是使用MySQL作为后端数据库时。
    • id字段是自动添加的,如果你想要指定自定义主键,只需在其中一个字段中指定 primary_key=True 即可。如果Django发现你已经明确地设置了Field.primary_key,它将不会添加自动ID列。
    • 本示例中的CREATE TABLE SQL使用PostgreSQL语法进行格式化,但值得注意的是,Django会根据配置文件中指定的数据库后端类型来生成相应的SQL语句。
2.3 class Meta 参数详解
class Foo(models.Model): 
    bar = models.CharField(maxlength=30)

    class Meta:
        # 下面是所有可能用到的 Meta 选项. 没有一个选项是必需的. 
        # 是否添加 class Meta 到你的 model 完全是可选的.
        pass
2.3.1 app_lable
# app_label这个选项只在一种情况下使用,就是你的模型类不在默认的应用程序包下的models.py文件中,这时候你需要指定你这个模型类是那个应用程序的。比如你在其他地方写了一个模型类,而这个模型类是属于myapp的,那么你这是需要指定为:

app_label='myapp'
2.3.2 db_table
# db_table是用于指定自定义数据库表名的。Django有一套默认的按照一定规则生成数据模型对应的数据库表名,如果你想使用自定义的表名,就通过这个属性指定,比如:

table_name='my_owner_table'  

# 若不提供该参数, Django 会使用 app_label + '_' + module_name 作为表的名字.

# 若你的表的名字是一个 SQL 保留字, 或包含 Python 变量名不允许的字符--特别是连字符 --没关系. Django 会自动在幕后替你将列名字和表名字用引号引起来.
2.3.3 db_tablespace
# 有些数据库有数据库表空间,比如Oracle。你可以通过db_tablespace来指定这个模型对应的数据库表放在哪个数据库表空间
2.3.4 get_last_by
# 由于Django的管理方法中有个lastest()方法,就是得到最近一行记录。如果你的数据模型中有 DateField 或 DateTimeField 类型的字段,你可以通过这个选项来指定lastest()是按照哪个字段进行选取的。

# 一个 DateField 或 DateTimeField 字段的名字. 若提供该选项, 该模块将拥有一个 get_latest() 函数以得到 "最新的" 对象(依据那个字段):

get_latest_by = "order_date"
2.3.5 order_with_respect_to
# 这个选项一般用于多对多的关系中,它指向一个关联对象。就是说关联对象找到这个对象后它是经过排序的。指定这个属性后你会得到一个get_XXX_order()和set_XXX_order()的方法,通过它们你可以设置或者回去排序的对象。

# 举例来说, 如果一个 PizzaToppping 关联到一个 Pizza 对象, 这样做:

order_with_respect_to = 'pizza'

# 就允许 toppings 依照相关的 pizza 来排序
2.3.6 ordering
# 这个字段是告诉Django模型对象返回的记录结果集是按照哪个字段排序的。比如下面的代码:

ordering=['order_date'] 
# 按订单升序排列
ordering=['-order_date'] 
# 按订单降序排列,-表示降序
ordering=['?order_date'] 
# 随机排序,?表示随机
ordering = ['-pub_date', 'author']
# 对 pub_date 降序,然后对 author 升序

# 需要注意的是:不论你使用了多少个字段排序, admin 只使用第一个字段
2.3.7 unique_together
# unique_together这个选项用于:当你需要通过两个字段保持唯一性时使用。这会在 Django admin 层和数据库层同时做出限制(也就是相关的 UNIQUE 语句会被包括在 CREATE TABLE 语句中)。比如:一个Person的FirstName和LastName两者的组合必须是唯一的,那么需要这样设置:

unique_together = (("first_name", "last_name"),)
2.3.8 verbose_name
# verbose_name的意思很简单,就是给你的模型类起一个更可读的名字:

verbose_name = "pizza"
2.3.9 verbose_name_plural
# 这个选项是指定,模型的复数形式是什么,比如

verbose_name_plural = "stories"

# 若未提供该选项, Django 会使用 verbose_name + "s".
2.4 Django ORM参数及详解
2.4.1 常用字段
  1. AutoField:int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列

  2. IntegerField:一个整数类型,范围在 -2147483648 to 2147483647

  3. CharField:字符类型,必须提供max_length参数, max_length表示字符长度

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

  5. DateTimeField:日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例

    # DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。
    #auto_now_add:配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
    # auto_now:配置上auto_now=True,每次更新数据记录的时候会更新该字段。
    
2.4.2 字段合集
# int自增列,必须填入参数 primary_key=True
AutoField(Field)

# bigint自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
	# 注:当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)

# 小整数 -32768 ~ 32767
SmallIntegerField(IntegerField):
	
# 正小整数 0 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)

# 整数列(有符号的) -2147483648 ~ 2147483647
IntegerField(Field)
	
# 正整数 0 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
	
# 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
BigIntegerField(IntegerField):
	
# 布尔值类型
BooleanField(Field)
	
# 可以为空的布尔值
NullBooleanField(Field):
	
# 字符类型
# 必须提供max_length参数, max_length表示字符长度
CharField(Field)

# 文本类型
TextField(Field)
	
# 字符串类型,Django Admin以及ModelForm中提供验证机制
EmailField(CharField)# 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
IPAddressField(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"
GenericIPAddressField(Field)
		
# 字符串类型,Django Admin以及ModelForm中提供验证 URL
URLField(CharField)
	
# 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
SlugField(CharField)
	
# 字符串类型,格式必须为逗号分割的数字
CommaSeparatedIntegerField(CharField)
	
# 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
UUIDField(Field)
	
# 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
# 参数:
	# path,                      文件夹路径
	# match=None,                正则匹配
	# recursive=False,           递归下面的文件夹
	# allow_files=True,          允许文件
	# allow_folders=False,       允许文件夹
FilePathField(Field)
	
# 字符串,路径保存在数据库,文件上传到指定目录
	# 参数:
		# upload_to = ""      上传文件的保存路径
		# storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
FileField(Field)
	
ImageField(FileField)
	# 字符串,路径保存在数据库,文件上传到指定目录
	# 参数:
		upload_to = ""      上传文件的保存路径
		storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
		width_field=None,   上传图片的高度保存的数据库字段名(字符串)
		height_field=None   上传图片的宽度保存的数据库字段名(字符串)

# 日期+时间格式 YYYY#MM#DD HH:MM[:ss[.uuuuuu]][TZ]
DateTimeField(DateField)
	
# 日期格式      YYYY#MM#DD
DateField(DateTimeCheckMixin, Field)

# 时间格式      HH:MM[:ss[.uuuuuu]]
TimeField(DateTimeCheckMixin, Field)
	
# 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
DurationField(Field)
	
# 浮点型
FloatField(Field)
	
DecimalField(Field)
	# 10进制小数
	# 参数:
		max_digits,小数总长度
		decimal_places,小数位长度

        # 二进制类型
BinaryField(Field)

2.4.3 字段参数
Fielddescription
null数据库中字段是否可以为空
db_column数据库中字段的列名
db_tablespace数据库中字段的存储表空间
default数据库中字段的默认值
primary_key数据库中字段是否为主键
db_index数据库中字段是否可以建立索引
unique数据库中字段是否可以建立唯一索引
unique_for_date数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month数据库中字段【月】部分是否可以建立唯一索引
unique_for_year数据库中字段【年】部分是否可以建立唯一索引
verbose_nameAdmin中显示的字段名称
blankAdmin中是否允许用户输入为空
editableAdmin中是否可以编辑
help_textAdmin中该字段的提示信息
choicesAdmin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作,如:gf = models.IntegerField(choices=[(0, ‘何穗’),(1, ‘大表姐’),],default=1)
error_messages自定义错误信息(字典类型),从而定制想要显示的错误信息; 字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date, 如:{‘null’: “不能为空.”, ‘invalid’: ‘格式错误’}
validators自定义错误验证(列表类型),从而定制想要的验证规则
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,<br/> MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
test = models.CharField(
max_length=32,
error_messages={
‘c1’: ‘优先错信息1’,
‘c2’: ‘优先错信息2’,
‘c3’: ‘优先错信息3’,
},
validators=[
RegexValidator(regex=‘root_\d+’, message=‘错误了’, code=‘c1’),
RegexValidator(regex=‘root_112233\d+’, message=‘又错误了’, code=‘c2’),
EmailValidator(message=‘又错误了’, code=‘c3’), ]
)
2.4.4 ForeignKey
# 外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 '一对多'中'多'的一方。
# ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。

models.ForeignKey(to="关联表")

字段参数

  1. to:设置要关联的表

  2. to_field:设置要关联的表的字段

  3. related_name:反向操作时,使用的字段名,用于代替原反向查询时的’表名_set’

    class Classes(models.Model):
        name = models.CharField(max_length=32)
    
    class Student(models.Model):
        name = models.CharField(max_length=32)
        theclass = models.ForeignKey(to="Classes")
        
    # 当我们要查询某个班级关联的所有学生(反向查询)时,写法如下:
    models.Classes.objects.first().student_set.all() 		# 一类的对象.多类名小写_set.all()
    
    # 当我们在ForeignKey字段中添加了参数 related_name 后
    class Student(models.Model):
        name = models.CharField(max_length=32)
        theclass = models.ForeignKey(to="Classes", related_name="students")
        
    # 当我们要查询某个班级关联的所有学生(反向查询)时,写法如下:
    models.Classes.objects.first().students.all() 	# 一类的对象.related_name内容_set.all()
    
  4. related_query_name:反向查询操作时,使用的连接前缀,用于替换表名

  5. db_constraint:是否在数据库中创建外键约束,默认为True

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

    类型描述
    None删除关联表中的数据时,当前表与其关联的field的行为
    models.CASCADE删除关联数据,与之关联也删除
    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)
    )
2.4.5 OneToOneField
# 一对一字段,通常一对一字段用来扩展已有字段

models.OneToOneField(to='关联表')

# 一对一的关联关系多用在当一张表的不同字段查询频次差距过大的情况下,将本可以存储在一张表的字段拆开放置在两张表中,然后将两张表建立一对一的关联关系

class Author(models.Model):
    name = models.CharField(max_length=32)
    info = models.OneToOneField(to='AuthorInfo')
    

class AuthorInfo(models.Model):
    phone = models.CharField(max_length=11)
    email = models.EmailField()

字段参数

  1. to:设置要关联的表
  2. to_field:设置要关联的表的字段
  3. on_delete:当删除关联表中的数据时,当前表与其关联的行的行为(和ForeignKey中一致)
2.4.6 ManyToManyField
# 用于表示多对多的关联关系。在数据库中通过第三张表来建立关联关系

models.ManyToManyField(to="关联表")

字段参数

  1. to:设置要关联的表

  2. related_name:反向操作时,使用的字段名,用于代替原反向查询时的’表名_set’(和ForeignKey中一致)

  3. related_query_name:反向查询操作时,使用的连接前缀,用于替换表名

  4. symmetrical:仅用于多对多自关联时,指定内部是否创建反向操作的字段。默认为True

    class Person(models.Model):
        name = models.CharField(max_length=16)
        friends = models.ManyToManyField("self")
    # 此时,person对象就没有person_set属性
    
    class Person(models.Model):
        name = models.CharField(max_length=16)
        friends = models.ManyToManyField("self", symmetrical=False)
    # 此时,person对象现在就可以使用person_set属性进行反向查询
    
  5. through:在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。

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

  6. through_fileds:设置关联字段

  7. db_name:默认创建第三张表时,数据库中表的名称

创建第三张表的方式

  1. 自己创建第三张表

    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")
    
  2. 通过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")
    
  3. 通过设置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")
    
  4. 注意

    • 当我们需要在第三张关系表中存储额外的字段时,就要使用第三种方式。
    • 但是当我们使用第三种方式创建多对多关联关系时,就无法使用set、add、remove、clear方法来管理多对多的关系了,需要通过第三张表的model来管理多对多关系

3 Django关联查询及数据处理

3.1 建表准备工作
class Company(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=100)
    
    groups = models.ManyToManyField('Group', related_name='company')
    
    create_time = models.DateTimeField(auto_now_add=True)
    update_time = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'tb_company'
        verbose_name = 'company'
        verbose_name_plural = 'company'
        
class Role(models.Model):
    function_role_name = 'function_role'
    ROLE_TYPE = {(function_role_name, function_role_name)}
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=100)
    type = models.CharField(max_length=30, choices=ROLE_TYPE, default=function_role_name)
    
    # 描述角色所属公司
    company = models.ForeignKey(Company, related_name='roles', null=True, blank=True)
    
    create_time = models.DateTimeField(auto_now_add=True)
    update_time = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'tb_role'
        verbose_name = 'role'
        verbose_name_plural = 'roles'
class Group(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=100)

    roles = models.ManyToManyField('Role', related_name='groups')

    create_time = models.DateTimeField(auto_now_add=True)
    update_time = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'tb_group'
        verbose_name = 'group'
        verbose_name_plural = 'groups'
class User(models.Model):
    create_time = models.DateTimeField(auto_now_add=True)
    update_time = models.DateTimeField(auto_now=True)

    user_detail = models.OneToOneField('UserDetail', related_name='user')

    class Meta:
        db_table = 'tb_user'
        verbose_name = 'user'
        verbose_name_plural = 'users'
class UserDetail(models.Model):
    name = models.CharField(max_length=128)
    gender = models.IntegerField(default=1)
    create_time = models.DateTimeField(auto_now_add=True)
    update_time = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'tb_user_detail'
        verbose_name = 'user_detail'

以上5个模型类会生成7张表(5张基础表,2张多对多产生的第三张表)

# 公司表
CREATE TABLE `tb_company` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `create_time` datetime(6) NOT NULL,
  `update_time` datetime(6) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

# 公司组关联表
CREATE TABLE `tb_company_groups` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `company_id` int(11) NOT NULL,
  `group_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `tb_company_groups_company_id_group_id_ba7436ed_uniq` (`company_id`,`group_id`),
  KEY `tb_company_groups_group_id_3f468bc4_fk_tb_group_id` (`group_id`),
  CONSTRAINT `tb_company_groups_company_id_e23f47b0_fk_tb_company_id` FOREIGN KEY (`company_id`) REFERENCES `tb_company` (`id`),
  CONSTRAINT `tb_company_groups_group_id_3f468bc4_fk_tb_group_id` FOREIGN KEY (`group_id`) REFERENCES `tb_group` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

# 组表
CREATE TABLE `tb_group` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `create_time` datetime(6) NOT NULL,
  `update_time` datetime(6) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

# 组角色表
CREATE TABLE `tb_group_roles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `group_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `tb_group_roles_group_id_role_id_58755f0b_uniq` (`group_id`,`role_id`),
  KEY `tb_group_roles_role_id_2dd5105b_fk_tb_role_id` (`role_id`),
  CONSTRAINT `tb_group_roles_group_id_dbeceaa3_fk_tb_group_id` FOREIGN KEY (`group_id`) REFERENCES `tb_group` (`id`),
  CONSTRAINT `tb_group_roles_role_id_2dd5105b_fk_tb_role_id` FOREIGN KEY (`role_id`) REFERENCES `tb_role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

# 角色表
CREATE TABLE `tb_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `type` varchar(30) NOT NULL,
  `create_time` datetime(6) NOT NULL,
  `update_time` datetime(6) NOT NULL,
  `company_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `tb_role_company_id_5ae6eab3_fk_tb_company_id` (`company_id`),
  CONSTRAINT `tb_role_company_id_5ae6eab3_fk_tb_company_id` FOREIGN KEY (`company_id`) REFERENCES `tb_company` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

# 用户表
CREATE TABLE `tb_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `create_time` datetime(6) NOT NULL,
  `update_time` datetime(6) NOT NULL,
  `user_detail_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_detail_id` (`user_detail_id`),
  CONSTRAINT `tb_user_ibfk_1` FOREIGN KEY (`user_detail_id`) REFERENCES `tb_user_detail` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8

# 用户详细信息表
CREATE TABLE `tb_user_detail` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  `gender` int(11) NOT NULL DEFAULT '1',
  `create_time` datetime(6) NOT NULL,
  `update_time` datetime(6) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
3.2 新增数据
3.2.1 单表操作
# 方法一:通过模型类
company = Company.objects.create(name="测试公司")

# 方法二:通过对象
company = Company()
company.name = "测试公司"
company.save()
3.2.2 一对一
# 方法一:通过模型类
user = User.objects.create(
    name="测试用户", gender=1, user_detail=UserDetail.objects.get(id=1))
user = User.objects.create(name="测试用户", gender=1, user_detail_id=1)

# 方法二:通过对象
user = User()
user.name = "测试用户"
user.user_detail = UserDetail.objects.get(id=2)   # 或者 user.user_detail_id = 2

user.save()
3.2.2 一对多
# 方法一:通过模型类
role = Role.objects.create(name="测试角色", company=Company.objects.filter().first())
role = Role.objects.create(name="测试角色", company_id=1)

# 方法二:通过对象
role = Role()
role.name = "测试角色"
role.company = Company.objects.get(id=1)    # 或者 role.company_id = 1
role.save()

3.2.2 多对多
# 方法一:通过对象
company = Company.objects.create(name="测试公司")
group = Group.objects.create(name="测试组")
company.groups.add(group)   # 或者 group.company.add(company)


company.groups.add(Group.objects.get(id=4), Group.objects.get(id=5))
company.groups.add(*[Group.objects.get(id=4), Group.objects.get(id=5)])
company.groups.add(*[4, 5])
company.groups.add(4, 5)

3.3 修改数据
3.4 删除数据
3.5 查询数据
3.5.1 常见方法结果返回
方法返回值
all()查询所有结果
filter(**kwargs)它包含了与所给筛选条件相匹配的QuerySet集合
get(**kwargs)返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
exclude(**kwargs)它包含了与所给筛选条件不匹配的对象
values(*field)返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
values_list(*field)它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
order_by(*field)对查询结果排序
reverse()对查询结果反向排序,请注意reverse()通常只能在具有已定义顺序的QuerySet上调用(在model类的Meta中指定ordering或调用order_by()方法)。
distinct()从返回结果中剔除重复纪录(如果你查询跨越多个表,可能在计算QuerySet时得到重复的结果。此时可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。)
count()返回数据库中匹配查询(QuerySet)的对象数量
first()返回第一条记录
last()返回最后一条记录
exists()如果QuerySet包含数据,就返回True,否则返回False
3.5.2 结果返回具体对象
  • get()
  • first()
  • last()
3.5.3 结果返回QuerySet
  • all()
  • filter()
  • exclude()
  • order_by()
  • reverse()
  • distinct()
  • values():返回一个可迭代的字典序列
  • values_list():返回一个可迭代的元祖序列
3.5.4 filter条件查询

用法: 模型类.objects.filter(模型类属性名__查询操作符 = 值)

用法: 模型类.objects.filter(模型类属性名__查询操作符 = 值)

  • 判等: exact

    select * from company where id=1;
    Company.objects.filter(id__exact=1)
    Company.objects.filter(id=1)
    
  • 模糊查询: contains / endswith / startswith / icontains / istartswith / iendswith

    Company.objects.filter(name__contains="ven")  # 获取name字段包含"ven"的
    Company.objects.filter(name__icontains="ven") # icontains大小写不敏感
    
  • 空查询: isnull

    Company.objects.filter(comment__isnull=False)
    
  • 范围查询: in、range

    Company.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
    Company.objects.exclude(id__in=[11, 22, 33])  # not in,与exclude结合使用
    
    Company.objects.filter(id__range=[1, 3])      # id范围是1到3的,等价于SQL的bettwen and
    
  • 比较查询: gt(大于)、 lt(小于) 、gte(大于或等于)、lte(小于或等于)

    Company.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
    
  • 日期查询: year、month、day、week_day、hour、minute、second

    Company.objects.filter(create_time__year=2015)
    
    Company.objects.filter(create_time__gt='2014-1-1')
    
3.5.5 F对象与Q对象
  • F对象

    1. 可以使用模型的字段A与字段B进行比较,如果A写在了等号的左边,则B出现在等号的右边,需要通过F对象构造;

      # 查询创建时间比更新时间小的公司
      Company.objects.filter(create_time__lt=F('update_time'))
      
    2. F对象使用算数运算

      Company.objects.filter(age__gt=F('id')*4)
      
    3. F对象中还可以写作”模型类__列名”进行关联查询

      Company.objects.filter(isDelete=F('heroinfo__isDelete'))
      
    4. 对于date/time字段,可与timedelta()进行运算

      Company.objects.list.filter(bpub_date__lt=F('bpub_date')+timedelta(days=1))
      
  • Q对象

    Q 对象相比 F 对象更加复杂一点,它主要应用于包含逻辑运算的复杂查询。Q 对象把关键字参数封装在一起,并传递给 filter、exclude、get 等查询的方法。多个 Q 对象之间可以使用&或者|运算符组合(符号分别表示与和或的关系),从而产生一个新的 Q 对象。当然也可以使用~(非)运算符来取反,从而实现NOT查询。

    1. 最简单的 Q 对象的使用方法是将单个字段类属性作为参数进行查询

      Book.objects.filter(Q(title__contains="P"))
      
    2. Q 对象在实际的应用中往往是较为复杂的,和常和逻辑运算符一起使用

      # 查找c语言中文网出版的书或价格低于35的书 
      Book.objects.filter(Q(retail_price__lt=35)|Q(pub_id='2')) #两个Q对象是或者的逻辑关系
      # 查找不是c语言中文出版的书且价格低于45的书 
      Book.objects.filter(Q(retail_price__lt=45)&~Q(pub_id='2')) #条件1成立条件2不成立
      
    3. Q 对象也可以与类属性的字段名组合在一起使用,但在这种情况下,Django 规定,Q 对象必须放在前面

      Book.objects.filter(Q(price__lte=100),title__icontains="p") #组合使用
      
3.5.6 order_by方法
# 升序: 模型类.objects.order_by('属性名')
# 降序: 模型类.objects.order_by('-属性名')
3.5.7 aggregate方法

用法: 模型类.objects.aggregate(聚合类(‘模型属性’))

返回值是一个字典, 格式: `{‘属性名__聚合函数’: 值}

from django.db.models import Sum, Count, Max, Min, Avg

select avg(id) from Company;

cc = Company.objects.aggregate(Avg("id"))
cc
{'id__avg': Decimal('4.0000')}
3.5.8 关联查询-OneToOneField
# 正向查询(有外键一方)
user = User.objects.get(id=1)        				# 获取正向数据(有外键一方的对象)
# 对象.属性字段
user_detail = user.user_detail       				# 获取关联数据对象
user_detail_name = user_detail.name	 				# 获取关联数据对象属性

# 反向查询(无外键一方)
user_detail = UserDetail.objects.get(id=1)        	# 获取反向数据
# 对象.外键存在方小写的表名(或者对象.外键一方设置的related_name值)
user = user_detail.user					 		  	# 获取关联数据对象
user_name = user.name				 			  	# 获取关联数据对象属性

3.5.9 关联查询-ForeignKey
# 正向查询(有外键一方)
role = Role.objects.get(id=7) 		 				# 获取正向数据(有外键一方的对象)
# 对象.属性字段
company = role.company				 				# 获取关联数据对象
company_name = company.name			 				# 获取关联数据对象属性

# 反向查询(无外键一方)
company = Company.objects.first()    				# 获取反向数据
# 对象.外键存在方小写的表名(或者对象.外键一方设置的related_name值),返回 QuerySet 列表
roles = company.roles.all()			 				# 获取关联数据对象QuerySet列表
role_name = roles[0].name			 				# 获取关联数据对象属性
3.5.10 关联查询-ManyToManyField
# 正向查询(有外键一方)
company = Company.objects.get(id=1)  				# 获取正向数据(有外键一方的对象)
# 对象.属性字段,返回 QuerySet 列表
groups = company.groups.all()		 				# 获取关联数据对象QuerySet列表
group_name = groups[0].name			 				# 获取关联数据对象属性

# 反向查询(无外键一方)
group = Group.objects.get(id=5)		 				# 获取反向数据
# 对象.外键存在方小写的表名(或者对象.外键一方设置的related_name值),返回 QuerySet 列表
companys = group.company.all()		 				# 获取关联数据对象QuerySet列表
company_name = companys[0].name		 				# 获取关联数据对象属性
3.5.11 基于QuerySet和双下划线的跨表查询
  1. OneToOneField

    # 正向查询(有外键一方):通过外键一方查询关联表信息
    # models.objects.filter(id=1(有外键一方查询条件)).values(关联属性__关联表字段)
    User.objects.filter(id=1).values('user_detail__name', "user_detail__gender")
    # <QuerySet [
    {'user_detail__id': 1, 'user_detail__name': '测试用户1', 'user_detail__gender': 1}]>
    
    
    # 反向查询(无外键一方):
    # models.objects.filter(外键存在方小写的表名(或者对象.外键一方设置的related_name值)__属性).values(无外键方表属性)
    UserDetail.objects.filter(user__id=1).values("id",'name', "gender")
    # <QuerySet [{'id': 1, 'name': '测试用户1', 'gender': 1}]>
    
  2. ForeignKey

    # 正向查询(有外键一方):通过外键一方查询关联表信息(无外键方表信息)
    # models.objects.filter(id=1(有外键一方查询条件)).values(关联属性__关联表字段)
    Role.objects.filter(id=4).values("company__id", "company__name")
    # <QuerySet [{'company__id': 1, 'company__name': '测试公司1'}, {'company__id': 5, 'company__name': '测试公司2'}, {'company__id': 7, 'company__name': '测试公司4'}, {'company__id': 1, 'company__name': '测试公司1'}]>
    
    
    # 反向查询(无外键一方):
    # models.objects.filter(外键存在方小写的表名(或者对象.外键一方设置的related_name值)__属性).values(无外键方表属性)
    Company.objects.filter(roles__name__contains="测试角色").values("id", "name")
    # <QuerySet [
    {'id': 1, 'name': '测试公司1'}, {'id': 5, 'name': '测试公司2'}, {'id': 7, 'name': '测试公司4'}]>
    
    
  3. ManyToManyField

    # 正向查询(有外键一方):通过外键一方查询关联表信息(无外键方表信息)
    # models.objects.filter(id=1(有外键一方查询条件)).values(关联属性__关联表字段)
    Company.objects.filter(id__gte=6).values("groups__name", "groups__id")
    # <QuerySet 
    [{'groups__name': '测试组', 'groups__id': 4}, {'groups__name': '测试组', 'groups__id': 5}]>
    
    # 反向查询(无外键一方):
    # models.objects.filter(外键存在方小写的表名(或者对象.外键一方设置的related_name值)__属性).values(无外键方表属性)
    Group.objects.filter(company__id__gte=6).values("name", "id")
    # <QuerySet [{'name': '测试组', 'id': 4}, {'name': '测试组', 'id': 5}]>
    
  4. 跨多表查询,涉及三个表或者以上

Role.objects.filter(id=4).values(
    "company__id", "company__name", "company__groups__id", "company__groups__name")
# <QuerySet [{'company__id': 1, 'company__name': '测试公司1', 'company__groups__id': 1, 'company__groups__name': '测试组'}, {'company__id': 1, 'company__name': '测试公司1', 'company__groups__id': 5, 'company__groups__name': '测试组'}]>

Company.objects.filter(roles__id=4).values("id", "name", "groups__id", "groups__name")
# <QuerySet [{'id': 1, 'name': '测试公司1', 'groups__id': 1, 'groups__name': '测试组'}, {'id': 1, 'name': '测试公司1', 'groups__id': 5, 'groups__name': '测试组'}]>


3.6 查询数据处理
  1. QuerySet 取值

    # 1、当使用filter()条件查询或者all()返回的是 QuerySet 列表
    yy = Company.objects.all()   #或者 yy = Company.objects.filter()
    <QuerySet [<Company: Company object>, <Company: Company object>]>
    
    # 此时取值为
    yy[0].name
    
    # 2、当使用filter()条件查询或者all()后添加values()返回的是 QuerySet 字典列表
    yy = Company.objects.all().values()    #或者 yy = Company.objects.filter()values()
    <QuerySet [{'id': 1, 'name': '测试公司1', 'create_time': datetime.datetime(2021, 5, 12, 15, 24, 48, 236193, tzinfo=<UTC>), 'update_time': datetime.datetime(2021, 5, 12, 15, 26, 42, 431296, tzinfo=<UTC>)}]>
    
    # 此时取值为
    yy[0].get("name")
    
    

  2. QuerySet 转列表

    yy = Company.objects.all()
    yy = list(yy)
    
  3. QuerySet 转json

    yy = Company.objects.all()
    yy = list(yy)
    # 转json字符串
    json.dumps(yy)
    
    # 问题
    # 1、TypeError: Object of type 'datetime' is not JSON serializable
    # 2、中文转ascii值了
    
    # 解决
    pip install tzlocal
    
    json.dumps(list(UserDetail.objects.filter().values()), default=str)
    
  4. 具体对象转字典

    # 使用 get()、first()、last() 、filter()[0]或者返回具体对象的时候
    
    yy = Company.objects.filter()[0]
    # <Company: Company object>
    yy = Company.objects.get(id=1)
    # <Company: Company object>
    
    # 对象直接取值
    yy.name
    
    # 对象转字典
    from django.forms.models import model_to_dict 
    model_to_dict(yy)
    
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值