django笔记《模型和数据库一》

文章目录

1 前言

任何软件系统都离不开对数据表的增删改查,django提供了ORM框架来操作数据表,当让也支持原生SQL查询

ORM框架中的对应关系大概如下

ORM数据库表
数据库的一张表
类变量数据库表中的字段
实例对象数据库中的一条记录

通过操作ORM类的实例对象,操作数据库中的表。

2 创建一个demo项目

  • 创建一个虚拟环境
E:\coding\django_deme_project>pipenv install --python 3.10  
Creating a virtualenv for this project...
Pipfile: E:\coding\django_deme_project\Pipfile
Using D:/Envs/Py3.10.1/python.exe (3.10.1) to create virtualenv...
[==  ] Creating virtual environment...created virtual environment CPython3.10.1.final.0-64 in 984ms
  creator CPython3Windows(dest=E:\coding\django_deme_project\.venv, clear=False, no_vcs_ignore=False, global=False)
  seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=C:\Users\Administrator\AppData\Local\pypa\virtualenv)
    added seed packages: pip==22.3.1, setuptools==65.6.3, wheel==0.38.4
  activators BashActivator,BatchActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator

Successfully created virtual environment!
Virtualenv location: E:\coding\django_deme_project\.venv
Creating a Pipfile for this project...
Pipfile.lock not found, creating...
Locking [dev-packages] dependencies...
Locking [packages] dependencies...
Updated Pipfile.lock (e4eef2)!
Installing dependencies from Pipfile.lock (e4eef2)...
  ================================ 0/0 - 00:00:00
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.
  • 进入虚拟环境
E:\coding\django_deme_project>pipenv shell
Launching subshell in virtual environment...
Microsoft Windows [版本 10.0.19044.1320]
(c) Microsoft Corporation。保留所有权利。
  • 安装两个必要的包 django 和 pymysql 其他包随用随装
(.venv) E:\coding\django_deme_project>pip install django
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting django
  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/21/26/28895838228c46ece278d764720995d5a51e4bce7d02d2a54e70f59108e1/Django-4.1.4-py3-none-any.whl (8.1 MB)
Collecting asgiref<4,>=3.5.2
  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/8f/29/38d10a47b322a77b2d12c2b79c789f52956f733cb701d4d5157c76b5f238/asgiref-3.6.0-py3-none-any.whl (23 kB)
Collecting sqlparse>=0.2.2
  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/97/d3/31dd2c3e48fc2060819f4acb0686248250a0f2326356306b38a42e059144/sqlparse-0.4.3-py3-none-any.whl (42 kB)
Collecting tzdata
  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/fa/5e/f99a7df3ae2079211d31ec23b1d34380c7870c26e99159f6e422dcbab538/tzdata-2022.7-py2.py3-none-any.whl (340 kB)
Installing collected packages: tzdata, sqlparse, asgiref, django
Successfully installed asgiref-3.6.0 django-4.1.4 sqlparse-0.4.3 tzdata-2022.7

(.venv) E:\coding\django_deme_project>pip install pymysql
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting pymysql
  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/4f/52/a115fe175028b058df353c5a3d5290b71514a83f67078a6482cff24d6137/PyMySQL-1.0.2-py3-none-any.whl (43 kB)
Installing collected packages: pymysql
Successfully installed pymysql-1.0.2
(.venv) E:\coding\django_deme_project>django-admin startproject demo01

(.venv) E:\coding\django_deme_project>cd demo01

(.venv) E:\coding\django_deme_project\demo01>django-admin startapp app01

虽然django的ORM对于数据库的类型没有要求,但是mysql是主流数据库,所以使用mysql数据库

  • 创建好的项目目录
│─manage.py  
├─app01
│  │  admin.py
│  │  apps.py
│  │  models.py
│  │  tests.py
│  │  views.py
│  │  __init__.py
│  │
│  └─migrations
│          __init__.py
│
└─demo01
        asgi.py
        settings.py
        urls.py
        wsgi.py
        __init__.py

2.1 修改配置文件

修改dem01/settings.py:

  • 配置中文和时区
LANGUAGE_CODE = 'zh-Hans'  # 改语言为中文
TIME_ZONE = 'Asia/Shanghai'  # 改时区为上海
USE_TZ = False  # 为False时,存到数据库的时间不使用标准时区

  • 配置数据库
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'djangodemo01',
        'USER': 'root',
        'PASSWORD': 'root',
        'HOST': '127.0.0.1',
        'PORT': '3306',
    }
}
  • 在demo01/_ _ init _ _.py中安装依赖
import pymysql

pymysql.install_as_MySQLdb()

至此demo项目搭建完成了。

下面开始进入正题:

3 模型

模型准确且唯一的描述了数据。它包含您储存的数据的重要字段和行为。一般来说,每一个模型都映射一张数据库表。
每个模型都是一个 Python 的类,这些类继承 django.db.models.Model
模型类的每个属性都相当于一个数据库的字段。

3.1 主键

每个模型都需要拥有一个设置了 primary_key=True 的字段(无论是显式的设置还是 Django 自动设置)。
默认情况下,Django 给每个模型一个自动递增的主键,其类型在 AppConfig.default_auto_field 中指定,或者在 DEFAULT_AUTO_FIELD 配置中全局指定
在这里插入图片描述

如果你想自己指定主键, 在你想要设置为主键的字段上设置参数 primary_key=True。如果 Django 看到你显式地设置了 Field.primary_key,将不会自动在表(模型)中添加 id 列。

3.2 django 内置字段类型

字段类型描述备注
AutoField一个 IntegerField,根据可用的 ID 自动递增。你通常不需要直接使用它;如果你没有指定,主键字段会自动添加到你的模型中。
BigAutoField一个 64 位整数,与 AutoField 很相似,但保证适合 1 到 9223372036854775807 的数字。
BigIntegerField一个 64 位的整数,和 IntegerField 很像,只是它保证适合从 -9223372036854775808 到 9223372036854775807 的数字。该字段的默认表单部件是一个 NumberInput。
BinaryField一个用于存储原始二进制数据的字段。可以指定为 bytes、bytearray 或 memoryview。
BooleanField一个 true/false 字段。该字段的默认表单部件是 CheckboxInput,或者如果 null=True 则是 NullBooleanSelect。当 Field.default 没有定义时,BooleanField 的默认值是 None。
CharField一个字符串字段,适用于小到大的字符串。对于大量的文本,使用 TextField。该字段的默认表单部件是一个 TextInput。最常用的字段类型
DateField一个日期,在 Python 中用一个 datetime.date 实例表示DateField.auto_now(每次保存对象时,自动将该字段设置为现在。对于“最后修改”的时间戳很有用。请注意,当前日期 总是 被使用,而不仅仅是一个你可以覆盖的默认值。只有在调用 Model.save() 时,该字段才会自动更新。当以其他方式对其他字段进行更新时,如 QuerySet.update(),该字段不会被更新,尽管你可以在这样的更新中为该字段指定一个自定义值。) DateField.auto_now_add(当第一次创建对象时,自动将该字段设置为现在。对创建时间戳很有用。请注意,当前日期是 始终 使用的;它不是一个你可以覆盖的默认值。因此,即使你在创建对象时为该字段设置了一个值,它也会被忽略。如果你想修改这个字段,可以设置以下内容来代替 auto_now_add=True :)
DateTimeField一个日期和时间,在 Python 中用一个 datetime.datetime 实例表示。与 DateField 一样,使用相同的额外参数。
DecimalField一个固定精度的十进制数,在 Python 中用一个 Decimal 实例来表示。它使用 DecimalValidator 验证输入
EmailField一个 CharField,使用 EmailValidator 来检查该值是否为有效的电子邮件地址。
DurationField一个用于存储时间段的字段——在 Python 中用 timedelta 建模
FileField一个文件上传字段primary_key 参数不支持,如果使用,会引起错误。
FilePathField一个 CharField,其选择仅限于文件系统中某个目录下的文件名。有一些特殊的参数,其中第一个参数是 必须的。一个目录的绝对文件系统路径
FloatField在 Python 中用一个 float 实例表示的浮点数。
GenericIPAddressFieldIPv4 或 IPv6 地址,字符串格式(如 192.0.2.30 或 2a02:42fe::4 )。该字段的默认表单部件是一个 TextInput。
ImageField继承 FileField 的所有属性和方法,但也验证上传的对象是有效的图像。需要 Pillow 库。
IntegerField一个整数。从 -2147483648 到 2147483647 的值在 Django 支持的所有数据库中都是安全的。
JSONField一个用于存储 JSON 编码数据的字段。在 Python 中,数据以其 Python 本地格式表示:字典、列表、字符串、数字、布尔值和 None。
PositiveBigIntegerField0 到 9223372036854775807
PositiveIntegerField0 到 2147483647
PositiveSmallIntegerField0 到 32767
SlugFieldSlug 是一个报纸术语。slug 是一个简短的标签,只包含字母、数字、下划线或连字符。它们一般用于 URL 中。
SmallAutoField就像一个 AutoField,1 到 32767
SmallIntegerField就像一个 IntegerField,从 -32768 到 32767 的值
TextField一个大的文本字段。该字段的默认表单部件是一个 Textarea。
TimeField一个时间,在 Python 中用 datetime.time 实例表示。接受与 DateField 相同的自动填充选项。
URLFieldURL 的 CharField,由 URLValidator 验证。
UUIDField一个用于存储通用唯一标识符的字段。使用 Python 的 UUID 类。当在 PostgreSQL 上使用时,它存储在一个 uuid 的数据类型中,否则存储在一个 char(32) 中。

3.3 自定义字段类型

留白

3.4 django字段选项

以下选型对所有字段都有效,且是可选的

字段选型描述备注
null如果是 True, Django 将在数据库中存储空值为 NULL。默认为 False。null参数只影响数据库,如果希望在表单中允许空值,设置blank属性为True
blank如果是 True ,该字段允许为空。默认为 False 。null纯属数据库相关,blank与验证相关,blank=False则该字段为必填字段
choices一个 sequence 本身由正好两个项目的迭代项组成(例如 [(A,B),(A,B)…]get_filed_display()来获取实际值
db_column这个字段要使用的数据库列名。如果没有给出列名,Django 将使用字段名。
db_index如果是 True,将为该字段创建数据库索引。
db_tablespace如果这个字段有索引,那么要为这个字段的索引使用的 数据库表空间 的名称。默认是项目的 DEFAULT_INDEX_TABLESPACE 设置(如果有设置),或者是模型的 db_tablespace (如果有)。如果后端不支持索引的表空间,则忽略此选项。
default该字段的默认值。可以是一个值或者是个可调用的对象,如果是个可调用对象,每次实例化模型时都会调用该对象。
editable如果是 False,该字段将不会在管理或任何其他 ModelForm 中显示。在 模型验证 中也会跳过。默认为 True。
error_messageserror_messages 参数可以让你覆盖该字段引发的默认消息。传入一个与你想覆盖的错误信息相匹配的键值的字典。
help_text额外的“帮助”文本,随表单控件一同显示。即便你的字段未用于表单,它对于生成文档也是很有用的。
primary_key如果设置为 True ,将该字段设置为该模型的主键。primary_key=True 意味着 null=False 和 unique=True。一个对象只允许有一个主键。主键字段是只读的。如果您改变了现有对象的主键值,然后将其保存,则会在旧对象旁边创建一个新对象。
unique如果设置为 True,这个字段必须在整个表中保持值唯一。
unique_for_date将其设置为 DateField 或 DateTimeField 的名称,要求该字段的日期字段值是唯一的。
unique_for_month
unique_for_year
verbose_name字段的一个人类可读名称,如果没有给定详细名称,Django 会使用字段的属性名自动创建,并将下划线转换为空格。
validators要为该字段运行的验证器列表

3.5 字段备注名

除了 ForeignKey, ManyToManyField 和 OneToOneField,任何字段类型都接收一个可选的位置参数 verbose_name,如果未指定该参数值, Django 会自动使用字段的属性名作为该参数值,并且把下划线转换为空格。

first_name = models.CharField("person's first name", max_length=30)

verbose_name = person’s first name

first_name = models.CharField(max_length=30)

verbose_name = first name

惯例是不将 verbose_name 的首字母大写,必要时 Django 会自动把首字母转换为大写。

3.5 META

class School(models.Model):
    name = models.CharField(max_length=32, help_text='学校名称', unique=True)
    address = models.CharField(max_length=32, help_text='学校地址')

    class META:
        abstract = True   #这个模型将是一个抽象基类。
        app_label = 'myapp'  # 如果在 INSTALLED_APPS 中定义了一个应用程序之外的模型,它必须声明它属于哪个应用程序
        base_manager_name = 'objects'  # 管理器的属性名,例如,'objects',用于模型的 _base_manager。
        db_table = 'school_table' # 数据库表名称,默认是“应用名_模型名小写"
        db_tablespace = '' # 此模型要使用的 数据库表空间 名称。如果有设置的话,默认是项目的 DEFAULT_TABLESPACE 配置。如果后端不支持表空间,则忽略此选项。
        default_manager_name = '' # 模型的 _default_manager 管理器名称。
        default_related_name = '' # 从相关对象到这个对象的关系默认使用的名称。默认为 _set。这个选项还可以设置 related_query_name。
        get_latest_by = '' #模型中的字段名或字段名列表,通常是 DateField,DateTimeField 或 IntegerField。这指定了在你的模型中使用的默认字段 Manager 的 last() 和 earliest() 方法。
        managed = True  # True:migrate管理  False:不被migrate管理
        order_with_respect_to = ''  # 使该对象可以根据给定字段(通常是 ForeignKey )进行排序。这可以用来使相关对象相对于父对象可排序。例如,如果一个 Answer 与一个 Question 对象相关,而一个问题有多个答案,并且答案的顺序很重要
        ordering = ['name'] # 对象的默认排序,用于获取对象列表时,-:表示降序  ?:表示随机
        permissions = [('can_deliver_pizzas', 'Can deliver pizzas')] # 创建此对象时要输入权限表的额外权限。为每个模型自动创建添加、更改、删除和查看权限
        default_permissions = ('add', 'change', 'delete', 'view') #
        proxy = True  # 作为另一个模型子类的模型将被视为 代理模型。
        #required_db_features = '' # 当前连接应具备的数据库特征列表,以便在迁移阶段考虑模型。
        # required_db_vendor = ''  # 支持的数据库厂商
        select_on_save = False # 默认False
        indexes = [] # 你想在模型上定义的 indexes 的列表
        unique_together = ['name', 'address']  # 一组字段名,合起来必须是唯一的:
        index_together = ['name', 'address'] # 一组字段名,合在一起,是有索引的:
        constraints = [] #你想在模型上定义的 约束 列表
        verbose_name = 'school'  # 对象的可读名称,单数:
        verbose_name_plural = 'schools' # 对象的复数名称

3.6 关联关系

显然,关系型数据库的强大之处在于各表之间的关联关系。 Django 提供了定义三种最常见的数据库关联关系的方法:多对一,多对多,一对一。

3.6.1 多对一关系

一个学校可以有多个学生,一个学成理论上只能上一所公立学校。
在学校和学生这个关系中,学校是“一方” , 学生是“多方”
对于多对一的关系,应该在多方创建外键,关联到一方,外键通过models.ForeignKey()配置在多方的类属性中,models.ForeignKey中需要两个必填字段,分别是to和on_delete

  • to 表示关联的模型

    # 如果两个模型都属于同一个app, 那么直接写类名就可以
    class School(models.Model):
    	...
    	# 一方
    class Student(models.Model):
    	...
    	# 多方
    	# 注意这种写法一定要把一方的模型类写到上面,否则会报错,因为python代码是从上向下解释的。
    	school = models.ForeignKey(to=School, on_delete=models.CASCADE)
    # 如果两个模型不属于同一个app,但是也存在一对多的关系
    school = models.ForeignKey(to='app_name.model_name', on_delete=models.CASCADE)
    
  • on_delete 表示关联的对象被删除后,有外键约束的记录应该怎么处理
    在这里插入图片描述
    在这里插入图片描述
    如果清华大学被删除了,那下面的三个学生应该怎么处理就是on_delete选型考虑的事情

    • models.CASCADE

    级联删除。Django 模拟了 SQL 约束 ON DELETE CASCADE 的行为,也删除了包含 ForeignKey 的对象。

    • models.PROTECT

    通过引发 ProtectedError,即 django.db.IntegrityError 的子类,防止删除被引用对象。

    • models.RESTRICT

    通过引发 RestrictedError ( django.db.IntegrityError 的一个子类)来防止删除被引用的对象。与 PROTECT 不同的是,如果被引用的对象也引用了一个在同一操作中被删除的不同对象,但通过 CASCADE 关系,则允许删除被引用的对象。

    class Artist(models.Model):
        name = models.CharField(max_length=10)
    
    class Album(models.Model):
        artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
    
    class Song(models.Model):
        artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
        album = models.ForeignKey(Album, on_delete=models.RESTRICT)
    Artist 可以被删除,即使这意味着删除被 Song 引用的 Album,因为 Song 也通过级联关系引用 Artist 本身。例如:
    >>> artist_one = Artist.objects.create(name='artist one')
    >>> artist_two = Artist.objects.create(name='artist two')
    >>> album_one = Album.objects.create(artist=artist_one)
    >>> album_two = Album.objects.create(artist=artist_two)
    >>> song_one = Song.objects.create(artist=artist_one, album=album_one)
    >>> song_two = Song.objects.create(artist=artist_one, album=album_two)
    >>> album_one.delete()
    # Raises RestrictedError.
    >>> artist_two.delete()
    # Raises RestrictedError.
    >>> artist_one.delete()
    (4, {'Song': 2, 'Album': 1, 'Artist': 1})
    
    
    • SET_NULL

    设置 ForeignKey 为空;只有当 null 为 True 时,才有可能。

    • SET_DEFAULT

    将 ForeignKey 设置为默认值,必须为 ForeignKey 设置一个默认值。

    • SET()
    from django.conf import settings
    from django.contrib.auth import get_user_model
    from django.db import models
    
    def get_sentinel_user():
        return get_user_model().objects.get_or_create(username='deleted')[0]
    
    class MyModel(models.Model):
        user = models.ForeignKey(
            settings.AUTH_USER_MODEL,
            on_delete=models.SET(get_sentinel_user),
        )
    
    • DO_NOTHING

    不采取任何行动。如果你的数据库后端强制执行引用完整性,这将导致一个 IntegrityError 除非你手动添加一个 SQL ON DELETE 约束条件到数据库字段。

模型创建代码

class School(models.Model):
    name = models.CharField(max_length=32, help_text='学校名称', unique=True)
    address = models.CharField(max_length=32, help_text='学校地址')

    class Meta:
        base_manager_name = 'objects'  # 管理器的属性名,例如,'objects',用于模型的 _base_manager。
        db_table = 'school_table'  # 数据库表名称,默认是“应用名_模型名小写"
        default_related_name = '_set'  # 从相关对象到这个对象的关系默认使用的名称。默认为 _set。这个选项还可以设置 related_query_name。
        get_latest_by = ''  # 模型中的字段名或字段名列表,通常是 DateField,DateTimeField 或 IntegerField。这指定了在你的模型中使用的默认字段 Manager 的 last() 和 earliest() 方法。
        managed = True  # True:migrate管理  False:不被migrate管理
        ordering = ['name']  # 对象的默认排序,用于获取对象列表时,-:表示降序  ?:表示随机
        unique_together = ['name', 'address']  # 一组字段名,合起来必须是唯一的:
        verbose_name = 'school'  # 对象的可读名称,单数:
        verbose_name_plural = 'schools'  # 对象的复数名称


class Student(models.Model):
    """
    学生类
    """
    name = models.CharField(max_length=32, help_text='学生名称', unique=True)
    age = models.PositiveSmallIntegerField(default=10)
    grade = models.PositiveSmallIntegerField(null=True, blank=True)
    school = models.ForeignKey(to='app01.School', on_delete=models.SET_NULL, related_name='students', null=True)
  • 自关联一对多

    class Student(models.Model):
        """
        学生类
        """
        name = models.CharField(max_length=32, help_text='学生名称', unique=True)
        age = models.PositiveSmallIntegerField(default=10)
        grade = models.PositiveSmallIntegerField(null=True, blank=True)
        school = models.ForeignKey(to='app01.School', on_delete=models.SET_NULL, related_name='students', null=True, blank=True)
        classmate = models.ForeignKey(to='self',on_delete=models.SET_NULL, related_name='classmates', null=True, blank=True) # 自关联多对一
    

3.6.2 多对多关系

  • models.ManyToManyField()
    ManyToManyField 接受一组额外的参数——都是可选的——控制关系如何运作。

    • related_name

    与 ForeignKey.related_name 相同。

    • related_query_name

    与 ForeignKey.related_query_name 相同。

    • symmetrical

    仅在自身上定义多对多字段关系时。考虑以下模型

    from django.db import models
    
    class Person(models.Model):
        friends = models.ManyToManyField("self")
    
    

    当 Django 处理这个模型时,它识别出它本身有一个 ManyToManyField,因此,它没有给 Person 类添加 person_set 属性。相反, ManyToManyField 被认为是对称的,也就是说,如果我是你的朋友,那么你就是我的朋友。
    如果你不想让 self 的多对多关系对称,可以将 symmetrical 设置为 False。这样会强制 Django 添加反向关系的描述符,允许 ManyToManyField 关系是非对称的。

    • through

    Django 会自动生成一个表来管理多对多关系。但是,如果你想手动指定中间表,你可以使用 through 选项来指定代表你要使用的中间表的 Django 模型。
    当使用多对多关联两个表时:中间表格式
    id :关系的主键。
    表1_id :表1 id。
    表2_id :表2 id。
    当使用多对多关联一张表时,也就是自关联多对多的情况
    id :关系的主键。
    from_id :表 id。
    to_id :表 id。

    • through_fields
      只有当指定了一个自定义的中间模型时才会使用,Django 通常会决定使用中间模型的哪些字段来自动建立多对多的关系。然而,考虑以下模型:

      from django.db import models
      
      class Person(models.Model):
          name = models.CharField(max_length=50)
      
      class Group(models.Model):
          name = models.CharField(max_length=128)
          members = models.ManyToManyField(
              Person,
              through='Membership',
              through_fields=('group', 'person'),
          )
      
      class Membership(models.Model):
          group = models.ForeignKey(Group, on_delete=models.CASCADE)
          person = models.ForeignKey(Person, on_delete=models.CASCADE)
          inviter = models.ForeignKey(
              Person,
              on_delete=models.CASCADE,
              related_name="membership_invites",
          )
          invite_reason = models.CharField(max_length=64)
      

      中间表Membership中有两个与Person表的外键,分别是person和inviter,这种情况会导致django在建立多对多关系的时候不知道使用哪个外键,所以需要在ManyToMany中设置through_fields,through_fields是一个包含两个元素的元组,这两个元素分别为中间表中指向两个表的外键属性,比如上个例子中的(person, group)

    • db_table

    要创建的用于存储多对多数据的表的名称。如果没有提供这个表名,Django 将根据以下表名创建一个默认表名:定义关系的模型表和字段本身的名称。

    • db_constraint

    控制是否应该在数据库中为中间表的外键创建约束。默认值是 True,不能同时设置through和db_constraint

    • swappable

    控制迁移框架的反应,如果这个 ManyToManyField 指向一个可交换的模型。如果它是 True ——默认值——那么如果 ManyToManyField 指向的模型与 settings.AUTH_USER_MODEL 的当前值相匹配(或其他可交换模型配置),关系将被存储在迁移中,使用对配置的引用,而不是直接对模型的引用。
    只有当你确定你的模型应该总是指向换入的模型时,你才想把它覆盖为 False,例如,如果它是一个专门为你的自定义用户模型设计的配置文件模型。
    如果不确定,就保留它在默认为 True 的状态。

  • 不通过中间表创建多对多关系

class Student(models.Model):
    """
    学生类
    """
    name = models.CharField(max_length=32, help_text='学生名称', unique=True)
    age = models.PositiveSmallIntegerField(default=10)
    grade = models.PositiveSmallIntegerField(null=True, blank=True)
    school = models.ForeignKey(to='app01.School', on_delete=models.SET_NULL, related_name='students', null=True, blank=True)
    classmate = models.ForeignKey(to='self',on_delete=models.SET_NULL, related_name='classmates', null=True, blank=True)

    def __str__(self):
        return f"{self.school.name}--->{self.name}"


class Teacher(models.Model):
    name = models.CharField(max_length=32, help_text='老师名字', unique=True)
    students = models.ManyToManyField(Student)

    class Meta:
        pass
  • 通过中间表创建多对多关系
class Student(models.Model):
    """
    学生类
    """
    name = models.CharField(max_length=32, help_text='学生名称', unique=True)
    age = models.PositiveSmallIntegerField(default=10)
    grade = models.PositiveSmallIntegerField(null=True, blank=True)
    school = models.ForeignKey(to='app01.School', on_delete=models.SET_NULL, related_name='students', null=True, blank=True)
    classmate = models.ForeignKey(to='self',on_delete=models.SET_NULL, related_name='classmates', null=True, blank=True)

    def __str__(self):
        return f"{self.school.name}--->{self.name}"


class Teacher(models.Model):
    name = models.CharField(max_length=32, help_text='老师名字', unique=True)
    students = models.ManyToManyField(Student, through='Student2Teacher')

    class Meta:
        pass


class Student2Teacher(models.Model):
    """
    老师学生中间表
    """
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE)
    data_joined = models.DateField()

中间表中可以添加其他字段,用于记录一条对应关系,比如老师A何时开始教的学生A,这个时间记录在data_joined字段中。

3.6.3 一对一关系

一对一关系一般用于将两个表合并为一个宽表,通过外键进行关联,同时给外键设置成unique=True。
如果没有为 OneToOneField 指定 related_name 参数,Django 将使用当前模型的小写名作为默认值。

  • 实例-扩展auth_user表

    from django.conf import settings
    from django.db import models
    
    class MySpecialUser(models.Model):
        user = models.OneToOneField(
            settings.AUTH_USER_MODEL,
            on_delete=models.CASCADE,
        )
        supervisor = models.OneToOneField(
            settings.AUTH_USER_MODEL,
            on_delete=models.CASCADE,
            related_name='supervisor_of',
    
    >>> user = User.objects.get(pk=1)
    >>> hasattr(user, 'myspecialuser')
    True
    >>> hasattr(user, 'supervisor_of')
    True
    
    

3.7 字段命名限制

  • 字段名称不能是python关键字
  • 字段名称不能包含连续的两个下划线,不能以下划线结尾

3.8 模型属性:Model.objects

模型当中最重要的属性是Manager(后面简称模型管理类),模型管理类是模型和数据库查询操作之间的接口,执行查询操作必须使用模型管理类,如果没有在模型Meta类中定义base_manager_name 属性,默认的模型管理类属性名为:objects,除非极特殊情况,否则不会改变这个属性名。

模型管理类只能通过模型类访问,不能通过实例访问

3.9 模型方法

模型方法定义在模型内,模型方法提供“行级”操作能力,模型管理类(Manager:Model.objects)提供“表级”操作能力。

  • __str__()

一个 Python 的“魔法方法”,返回值友好地展示了一个对象。

  • get_absolute_url()

该方法告诉 Django 如何计算一个对象的 URL。Django 在后台接口使用此方法,或任意时间它需要计算一个对象的 URL。
任何需要一个唯一 URL 的对象需要定义此方法。

3.9.1 重写django定义的模型方法:如save() delete()

  • 重写save()方法,选择性保存

    
    class Blog(models.Model):
        name = models.CharField(max_length=100)
        tagline = models.TextField()
    
        def save(self, *args, **kwargs):
            if self.name == "Yoko Ono's blog":
                return # Yoko shall never have her own blog!
            else:
                super().save(*args, **kwargs)  # Call the "real" save() method.
    

3.10 模型继承

3.10.1 通过抽象基类继承

在模型类的Meta类中定义abstract属性为True。
其他模型可以做为抽象类的子类,这时子类会同步抽象类的所有属性,但是django不会创建抽象类的表。

class BaseModel(models.Model):
    create_time = models.DateTimeField(auto_now_add=datetime.datetime.now)  # 创建时时间
    update_time = models.DateTimeField(auto_now=datetime.datetime.now)  # 更新时时间

    creator = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True)

    class Meta:
        abstract = True


class Blog(BaseModel):
    """
    博客类
    """
    title = models.CharField(max_length=128, unique=True)

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

上面这两个类第一个是抽象模型类,第二个是继承抽象模型类的博客类。

做数据库迁移:

python manage.py makemigrations
python manage.py migrate 

查看数据库中的数据表:

在这里插入图片描述

查看app01_blog表结构:

在这里插入图片描述

在模型类blog中继承了抽象基类中定义的类属性。

3.10.2 多表继承

from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

Place 的所有字段均在 Restaurant 中可用,虽然数据分别存在不同的表中。所有,以下操作均可:

>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")

若有一个 Place 同时也是 Restaurant,你可以通过小写的模型名将 Place 对象转为 Restaurant 对象。

>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>

3.10.3 代理模型

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class MyPerson(Person):
    class Meta:
        proxy = True

    def do_something(self):
        # ...
        pass

MyPerson 类与父类 Person 操作同一张数据表。特别提醒, Person 的实例能通过 MyPerson 访问,反之亦然。

>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>

你也可以用代理模型定义模型的另一种不同的默认排序方法。你也许不期望总对 “Person” 进行排序,但是在使用代理时,总是依据 “last_name” 属性进行排序:

class OrderedPerson(Person):
    class Meta:
        ordering = ["last_name"]
        proxy = True

现在,普通的 Person 查询结果不会被排序,但 OrderdPerson 查询接轨会按 last_name 排序。

4 增、删、改

4.1 新增数据

4.1.1 单表增加数据

class School(models.Model):
    name = models.CharField(max_length=32, help_text='学校名称', unique=True)
    address = models.CharField(max_length=32, help_text='学校地址')

用这个模型做为测试模型:

python manage.py shell 进入终端操作界面:

PS E:\coding\django_deme_project\demo01> python .\manage.py shell
Python 3.10.1 (tags/v3.10.1:2cd268a, Dec  6 2021, 19:10:37) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)

  • 方式一:
>>> from app01.models import School 
>>> School 
<class 'app01.models.School'>
>>> s1 = School(name="北京大学", address="北京市")
>>> s1.save() 
  • 方式二:
>>> School.objects.create(name="浙江大学",address="杭州")
<School: School object (3)>

4.1.2 验证数据

重写save()方法即可

使用下面三个模型进行测试
教室模型、学生模型、老师模型
一个教室有多个学生,一个学生理论上只有一个教室
一个学生可以有多位老师,一个老师也可以同时教多个学生

4.1.3 多对一数据增加

class ClassRoom(models.Model):
    name_choices = (
        (1, "初级班"),
        (2, "中级班"),
        (3, "高级班"),
        (4, "特级班"),
    )
    name = models.PositiveSmallIntegerField(choices=name_choices, default=1)


class Student(models.Model):
    name = models.CharField(max_length=32)
    age = models.PositiveSmallIntegerField(default=12)
    class_room = models.ForeignKey(to='app02.ClassRoom', on_delete=models.SET_NULL, null=True, blank=True,
                                   related_name='students')


class Teacher(models.Model):
    name = models.CharField(max_length=32)
    students = models.ManyToManyField(Student)

执行数据库迁移命令

python manage.py makemigrations app02
python manage.py migrate app02

执行完成之后在数据库中在这里插入图片描述
会创建4张表格,分别是:

  • 插入教室表:

    >>> cr1 = ClassRoom(name=1) 
    >>> cr2 = ClassRoom(name=2) 
    >>> cr3 = ClassRoom(name=3) 
    >>> cr4 = ClassRoom(name=4) 
    >>> cr1.save()              
    >>> cr2.save() 
    >>> cr3.save()              
    >>> cr4.save() 
    
  • 插入学生表

    >>> from app02.models import Teacher, Student, ClassRoom
    >>> cr1 = ClassRoom.objects.get(pk=1)
    >>> cr1
    <ClassRoom: 初级班>
    >>> cr2 = ClassRoom.objects.get(pk=2) 
    >>> cr2
    <ClassRoom: 中级班>
    >>> cr3 = ClassRoom.objects.get(pk=3) 
    >>> cr3                               
    <ClassRoom: 高级班>
    >>> cr4 = ClassRoom.objects.get(pk=4) 
    >>> cr4
    <ClassRoom: 特级班>
    >>> s1 = Student(name="kobe") 
    >>> s1.save()
    >>> s2 = Student(name='james')
    >>> s2.save()
    >>> s3 = Student(name='wade')  
    >>> s3.save()
    >>> s4 = Student(name='paul')  
    >>> s4.save()
    >>> s5 = Student(name='curry') 
    >>> s5.save()
    >>> s6 = Student(name='tompson') 
    >>> s6.save()                    
    >>> s7 = Student(name='green')   
    >>> s7.save()
    >>> s8 = Student(name='willianms')
    >>> s8.save()
    
  • 建立外键关联

    • 方式一:在多方建立外键关联

      # 给学生添加一个外键属性
      >>> s1.class_room = cr1 # 这里注意,如果没有执行s1.save()数据库不会更新
      >>> s1.save()
      
      
    • 方式二:在一方建立外键关联 add() clear() create() set()

      # 如设置related_name='students' ,给“一方"设置反向字段,一般建议设置这个选项
      >>> cr1.students.add(s1)
      >>> cr1.students.add(s2,s3)  
      # 如没有设置related_name
      >>> cr1.student_set.add(s1)
      >>> cr1.student_set.add(s2)
      # 清楚所有关系
      >>>cr1.students.clear() 
      # 创建一个多方对象,并建立外键关联
      >>> cr2.students.create(name="jordan", age=40)
      >>><Student: <中级班> jordan:40>
      # set() 函数的参数是列表,列表元素是学生对象
      >>> cr3.students.set([s4,s5])
      

4.1.3 多对多数据增加

使用学生和老师模型做多对多关系的测试模型:

学生数据已经添加完成了,就用上面那些。

再创建几条老师的数据:

>>> t1 = Teacher(name='张老师')
>>> t2 = Teacher(name='王老师') 
>>> t3 = Teacher(name='李老师') 
>>> t4 = Teacher(name='赵老师') 
>>> t1.save()  
>>> t2.save()  
>>> t3.save()  
>>> t4.save() 
  • 获取关联模型的结果

    >>> t1.students.all() # 直接获取属性
    <QuerySet []>
    
    >>> s1.teacher_set.all() # 如果ManyToMany没有设置related_name,通过model小写_set获取所有
    <QuerySet []>
    
  • 添加关联数据(没有自定义第三张表的情况)

    # 通过老师关联学生
    >>> t1.students.add(s1,s2,s3) 
    # 关联三条数据app02_teacher_students
    #id  teacher_id  student_id
    #1   1           1
    #2   1           2
    #3   1           3
    >>> t1.students.set([s4, s5])
    # set([]) set函数会清楚之前所有的关系,重写进行设置
    #id  teacher_id  student_id
    #1   1           4
    #2   1           5
    >>> t1.students.clear() 
    # 清除所有的关系,效果等价于t1.students.set([])
    
    # 通过学生关联老师
    >>> s1.teacher_set.add(t1,t2)
    #id  teacher_id  student_id
    #1   1           1
    #2   2           1
    >>> s1.teacher_set.set([t2,t3,t4])
    #id  teacher_id  student_id
    #1   2           1
    #2   3           1
    #3   4           1
    >>>s1.teacher_set.clear()
    # 清除所有的关联关系
    >>> t1.students.add(s1,s2,s3,s4)
    >>> t1.students.remove(s1) # 删除一个已经拥有的关联关系,如果没有这个联系也不会报错 
    
  • 如果通过自定义第三张表做关联(自定义第三张的情况)

    如果有自定义的第三张数据表,并且第三张数据表中有其他字段

    >>>t1.students.add(s1, through_defaults={'other_field':'field_value'})
    

4.2 删除数据

>>> s1.delete() 
(1, {'app02.Student': 1})
#delete() 方法返回一个元组,元组第一个值为已删除的对象个数(即删除的数据行数),元组的第二个值是一个字典,它包含了对象类型和删除的对象个数。

4.3 改变数据

  • 方式一:

    >>> student = Student.objects.get(pk=2) 
    >>> student
    <Student: <初级班> james:34>
    >>> student.age = 100
    >>> student.save()
    
    
  • 方式二:

    >>> Student.objects.filter(id=2).update(age=23)
    1 # 返回更新的记录条数
    >>> Student.objects.filter(id__gte=2).update(age=34)
    7
    

5 查

特别注意:获取objects属性必须使用模型类,不能使用模型实例

>>> s1.objects
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "E:\coding\django_deme_project\.venv\lib\site-packages\django\db\models\manager.py", line 186, in __get__
    raise AttributeError(
AttributeError: Manager isn't accessible via Student instances

**QuerySet **是一个非常关键的对象,后面我们单独章节说这个对象。

5.1 获取全部记录

方法 all() 返回了一个包含数据库中所有对象的 QuerySet 对象。

>>> Student.objects.all() 
<QuerySet [<Student: <初级班> james:100>, <Student: <初级班> wade:34>, <Student: <高级班> paul:34>, <Student: <高级班> curry:34>, <Student: <不明班级> tompson:34>, <Student: <不明班级> green:34>, <Student: <中级班> jordan:34>]>

5.2 过滤器检索对象

5.2.1 filter(**kwargs)

返回一个新的 QuerySet,包含的对象满足给定查询参数。

5.2.2 exclude(**kwargs)

返回一个新的 QuerySet,包含的对象不满足给定查询参数。

5.2.3 详解filter、exclude、get中的关键字参数

field__lookupType=value:注意这里是双下划线,lookupType如果没有,会报错TypeError
Student.objects.filter(name__test=‘james’)
django.core.exceptions.FieldError: Unsupported lookup ‘test’ for CharField or join on the field not permitted.

(1) exact(完全匹配)

如果提供的比较值是 None,它将被解释为 SQL NULL

ORM语法

Student.objects.get(id__exact=2) 
Student.objects.filter(class_room__exact=None)

SQL语法

select * from table where id = 2  
select * from app02_student where class_room_id is null;
(2) iexact(不区分大小写匹配)

如果提供的比较值是 None,它将被解释为 SQL NULL

ORM语法

Student.objects.filter(name__iexact='JaMes') 

SQL语法

select * from app02_student where app02_student.`name` LIKE 'James'

注意事项

不能给外键字段添加iexact选项

Student.objects.filter(classroom__iexact=None) 
# 报错如下:
# django.core.exceptions.FieldError: Related Field got invalid lookup: iexact
(3) contains(区分大小写的包含)

ORM语法

>>> Student.objects.filter(name__contains='p')  
<QuerySet [<Student: <高级班> paul:34>, <Student: <不明班级> tompson:34>]>
>>> Student.objects.filter(name__contains='P') 
<QuerySet []>

SQL语法

select * from app02_student where `name` LIKE '%p%'
(4) icontains(不区分大小写的包含)

ORM语法

>>> Student.objects.filter(name__icontains='P')
<QuerySet [<Student: <高级班> paul:34>, <Student: <不明班级> tompson:34>]>
>>> Student.objects.filter(name__icontains='p') 
<QuerySet [<Student: <高级班> paul:34>, <Student: <不明班级> tompson:34>]>

SQL语法

select * from app02_student where `name` LIKE '%p%'  # LIKE和ILIKE
(5) in(判断字段值是否在一个可迭代对象中)

可迭代对象一般包含元组、列表、字符串

ORM语法

>>> Student.objects.filter(id__in=[1,2,3,4,5])  
<QuerySet [<Student: <初级班> james:100>, <Student: <初级班> wade:34>, <Student: <高级班> paul:34>, <Student: <高级班> curry:34>]>
>>> Student.objects.filter(name__in=['james'])         
<QuerySet [<Student: <初级班> james:100>]>

SQL语法

select * from app02_student where id in (1,2,3,4)
select * from app02_student where name in ('james')
(6) gt(大于)

ORM语法

>>> Student.objects.filter(id__gt=4)          
<QuerySet [<Student: <高级班> curry:34>, <Student: <不明班级> tompson:34>, <Student: <不明班级> green:34>, <Student: <中级班> jordan:34>]>
>>> Student.objects.filter(birthday__gt="1982-9-1")  
<QuerySet [<Student: <高级班> paul:34>]>

SQL语法

select * from app02_student where id > 4 

select * from app02_student where birthday > "1982-9-1"
(7) gte(大于等于)
(8) lt(小于)
(9) lte(小于等于)
(10) startswith(区分大小写的开头)
>>> Student.objects.filter(name__startswith='j')
<QuerySet [<Student: <初级班> james:100>, <Student: <中级班> jordan:34>]>
>>> Student.objects.filter(name__istartswith='J') 
<QuerySet [<Student: <初级班> james:100>, <Student: <中级班> jordan:34>]>
>>> Student.objects.filter(name__startswith='J')  
<QuerySet []>
>>> Student.objects.filter(name__endswith='s')   
<QuerySet [<Student: <初级班> james:100>]>
>>> Student.objects.filter(name__endswith='S') 
<QuerySet []>
>>> Student.objects.filter(name__iendswith='S')
<QuerySet [<Student: <初级班> james:100>]>

SQL语法

# startswith -> j
select * from app02_student where app02_student.`name` LIKE 'j%'

# endswith -> s
select * from app02_student where app02_student.`name` LIKE '%s'
(11)istartswith(不区分大小写的开头)

SQL语法


(12)endswith(区分大小写的结尾)

SQL语法


(13)iendswith(不区分大小写的结尾)

SQL语法


(14)range(范围测试,包含开始和结尾)
>>> start_date = datetime.date(1990,1,1) 
>>> end_date = datetime.date(2022,1,1)  
>>> Student.objects.filter(birthday__range=(start_date, end_date))
<QuerySet [<Student: <中级班> jordan:34>]>

>>> start_date = datetime.date(1980,12,1)                          
>>> end_date = datetime.date(1981,11,1)                            
>>> Student.objects.filter(birthday__range=(start_date, end_date))
<QuerySet [<Student: <初级班> james:100>, <Student: <初级班> wade:34>]>

在这里插入图片描述

SQL语法

select * from app02_student where birthday between "1990-1-1" and "2022-1-1"
(15) 以下是时间相关的查询字段:
(16) date (从日期时间字段取出日期值)

ORM语法

>>> Student.objects.filter(birthday__date__gt='2022-01-01')
<QuerySet [<Student: <高级班> curry:34>, <Student: <不明班级> tompson:34>, <Student: <不明班级> green:34>]>

注意:当 USE_TZ 为 True 时,字段在过滤前会被转换为当前时区

SQL语法


(17) year(从日期时间字段取出年份)

ORM语法

>>> Student.objects.filter(birthday__year__gt='2022')
<QuerySet [<Student: <高级班> curry:34>, <Student: <不明班级> tompson:34>]>
>>> Student.objects.filter(birthday__year='2022')     
<QuerySet [<Student: <不明班级> green:34>]>


SQL语法

select * from app02_student where birthday > '2022-01-01'

select * from app02_student where birthday between '2022-01-01' and '2022-12-31'
(18) iso_year(精确的 ISO 8601 周号年份匹配)

ORM语法

>>> Student.objects.filter(birthday__iso_year__gt='2022')
<QuerySet [<Student: <高级班> curry:34>, <Student: <不明班级> tompson:34>]>
>>> Student.objects.filter(birthday__iso_year='2022')     
<QuerySet [<Student: <不明班级> green:34>]>


SQL语法

(19) month(精确的月份匹配1到12)

ORM语法

>>> Student.objects.filter(birthday__month=12)   
<QuerySet [<Student: <初级班> james:100>, <Student: <中级班> jordan:34>]>

SQL语法

select * from app02_student where EXTRACT(month from birthday) = 12
(20) day(精确的日期匹配1到31)

ORM语法

>>> Student.objects.filter(birthday__day=1)    
<QuerySet [<Student: <初级班> james:100>, <Student: <初级班> wade:34>, <Student: <高级班> paul:34>, <Student: <中级班> jordan:34>]>


SQL语法

select * from app02_student where EXTRACT(day from birthday) = 1

获取所有生日是1号的记录

在这里插入图片描述

(21) week

对于日期和日期时间字段,根据 ISO-8601 ,返回星期号(1-52 或 53),即星期从星期一开始,第一周包含一年的第一个星期四。

ORM语法



SQL语法


(22) week_day

对于日期和日期时间字段,“星期几”匹配。
从 1(星期日)到 7(星期六)取一个整数值,代表一周的一天。

ORM语法



SQL语法


(23) iso_week_day

对于日期和日期时间字段,精确匹配 ISO 8601 星期几。允许链接其他字段的查询。
取一个整数值,代表一周的 1(星期一)到 7(星期日)。

ORM语法



SQL语法


(24) quarter(季度)

ORM语法



SQL语法


(25) time(取datetime中的时间部分)

ORM语法



SQL语法


(26) hour(取datetime中的小时部分)

0~23 的整数

ORM语法



SQL语法


(27) minute(取datetime中的分钟部分)

0~59 的整数

ORM语法



SQL语法


(28) second(取datetime中的秒部分)

0~59 的整数

ORM语法



SQL语法


(29)isnull(取datetime中的秒部分)

取 True 或 False,分别对应 IS NULL 和 IS NOT NULL 的 SQL 查询。

ORM语法



SQL语法


(30)regex(区分大小写的正则表达式匹配)

满足re模块正则的语法

ORM语法



SQL语法

SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL

SELECT ... WHERE REGEXP_LIKE(title, '^(An?|The) +', 'c'); -- Oracle

SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL

SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite
(31)iregex(不区分大小写的正则表达式匹配)

满足re模块正则的语法

ORM语法



SQL语法

SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL

SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle

SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL

SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite

5.3 跨表查询

注意:print(query_set.query) 打印原生SQL查询

5.3.1 多对一跨表查询

教室和学生是一组多对一关系。

  • 查询教室为初级班的所有学生

ORM

>>> s = Student.objects.filter(class_room__name=1)
>>> s
<QuerySet [<Student: <初级班> james:100>, <Student: <初级班> wade:34>]>

SQL

>>> print(s.query)
SELECT 
`app02_student`.`id`, 
`app02_student`.`name`, 
`app02_student`.`age`, 
`app02_student`.`class_room_id`, 
`app02_student`.`birthday` 
FROM `app02_student` INNER JOIN `app02_classroom` 
ON (`app02_student`.`class_room_id` = `app02_c
lassroom`.`id`) 
WHERE `app02_classroom`.`name` = 1
  • 查询学生姓名中有字母j的所有班级

ORM

>>> ClassRoom.objects.filter(students__name__contains='j')
<QuerySet [<ClassRoom: 初级班>, <ClassRoom: 中级班>]>

SQL

>>> cr = ClassRoom.objects.filter(students__name__contains='j')
>>> print(cr.query)
SELECT 
`app02_classroom`.`id`, 
`app02_classroom`.`name` 
FROM `app02_classroom` 
INNER JOIN `app02_student` 
ON (`app02_classroom`.`id` = `app02_student`.`class_room_id`) 
WHERE `app02_student`.`name` LIKE BINARY %j%

5.3.3 多对多跨表查询

学生和老师是一组多对多关系

  • 查询李老师教的所有的学生

ORM

>>> qs=Student.objects.filter(teacher__id=1)           
>>> qs
<QuerySet [<Student: <初级班> james:100>, <Student: <初级班> wade:34>, <Student: <高级班> paul:34>]>

sql

>>> print(qs.query)
SELECT 
`app02_student`.`id`, 
`app02_student`.`name`, 
`app02_student`.`age`, 
`app02_student`.`class_room_id`, 
`app02_student`.`birthday` 
FROM `app02_student` INNER JOIN `app02_teacher_students` 
ON (`app02_student`.`id` = `app02_teacher_students`.`student_id`) 
WHERE `app02_teacher_students`.`teacher_id` = 1

  • 查询james、curry、green这些同学的老师

ORM

>>> qs = Teacher.objects.filter(students__name__in=['james','green', 'curry'])
>>> qs
<QuerySet [<Teacher: <Teacher> 张老师>]>

SQL

>>> print(qs.query)
SELECT 
`app02_teacher`.`id`, 
`app02_teacher`.`name` 
FROM `app02_teacher` INNER JOIN `app02_teacher_students` 
ON (`app02_teacher`.`id` = `app02_teacher_students`.`teacher_id`) 
INNER JOIN `app02_student` 
ON (`app02_teacher_students`.`student_id` = `app02_student`.`id`) 
WHERE `app02_student`.`name` IN (james, green, curry)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kobe_OKOK_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值