[Study Notes] Fully master Django ORM

References: Fully master Django ORM

1.1 Course content and guidance

Learning objective: Use django independently to complete the development of orm
Learning content: all knowledge points of Django ORM
insert image description here

2.1 Introduction to ORM

ORM: Detailed Explanation of Object-Relational Mapping
Django's ORM
insert image description here
In django, a models.py file will be automatically created under the application folder.

  • Advantages and Disadvantages of Model Class Advantage
    : Let developers focus on business logic processing and improve development efficiency. There is no need to write native SQL statements in the business logic code. Operate the database by manipulating objects.
    Disadvantages: sacrifice program execution efficiency to a certain extent. After writing orm for a long time, you may forget how to write sql.

3.1 Common field types

  • Set the database connection information in the project's settings.py
DATABASES = {
    
    
    "default": {
    
    
        "ENGINE": "django.db.backends.mysql",
        "NAME": "imooc",
        "USER": 'root',
        'PASSWORD': 'xxx',
        'HOST': '127.0.0.1'
    }
}
  • models.py file
from django.db import models

# Create your models here.
class Test(models.Model):
    '''测试学习用'''
    Auto = models.AutoField() # 自增长字段
    BigAuto = models.BigAutoField() # 允许的记录更大

    # 二进制数据
    Binary = models.BinaryField() # 存入二进制数据

    # 布尔型
    Boolean = models.BooleanField() # 允许为空的布尔型
    NullBoolean = models.NullBooleanField() # 不允许为空的布尔型

    # 整型
    PositiveSmallInteger = models.PositiveSmallIntegerField() # 正整数,允许5个字节
    SmallInteger = models.SmallIntegerField() # 6个字节大小的整数
    PositiveInteger = models.PositiveIntegerField() # 10个字节大小正整数
    Integer = models.IntegerField() # 11个字节大小
    BigInteger = models.BigIntegerField() # 20个字节大小的整型

    # 字符串类型
    Char = models.CharField() # 对应varchar, 通常指定max_length
    Text = models.TextField() # longtext, 不需要指定长度

    # 时间日期类型
    Date = models.DateField() # 年月日
    DateTime = models.DateTimeField() # 年月日时分秒
    Duration = models.DurationField() # int,底层通过python的timedelta实现

    # 浮点型
    Float = models.FloatField()
    Decimal = models.DecimalField() # 需要指定整数有多少位,小数有多少位

    # 其他字段
    Email = models.EmailField() # 邮箱
    Image = models.ImageField() # 图片
    File = models.FileField() # 文件
    FilePath = models.FilePathField() # 文件路径
    URL = models.URLField() # 地址
    UUID = models.UUIDField() # 通用唯一识别码
    GenericIPAddress = models.GenericIPAddressField() # IP地址

3.2 Relational fields

  • One-to-one (OneToOnefield)
  • Many-to-one (ForeignKey)
  • Many-to-many (ManyToManyField), default or custom intermediate table
class A(models.Model):
    onetoone = models.OneToOneField(Test)
    
class B(models.Model):
    foreign = models.ForeignKey(A)

class C(models.Model):
    manytomany = models.ManyToManyField(B)

3.3 Field parameters

    1. Parameters that all fields have
db_column = "age"  # 修改在数据库表中的字段的默认名
primary_key = True  # 设置主键,默认是False
verbose_name = "11个字节大小" # 设置别名/备注
unique = True # 设置字段的唯一键属性,这个数据在表中必须唯一
null = True # 数据库层面,数据库中的数据是否可以为空
blank = True # 前端表单提交的时候,数据是否可以为空
db_index = True # 表示给字段建立索引
help_text = "xxx" # 在表单中显示的说明
editable = False # 不允许用户进行编辑
    1. Parameters only for individual fields
max_length = 100 # CharFiled通常一定要指定长度
unique_for_date = True # 时间日期类型才有的字段参数,表示这个字段的日期必须唯一
unique_for_month = True # 时间日期类型才有的字段参数,表示这个字段的月份必须唯一
auto_now = True # 更新当前记录的时间,更新记录时更新的时间
auto_now_add = True # 增加记录时的当前时间,新插入一条数据,插入的时间
max_digits = 4 # 表示总共有多少位数
decimal_places = 2 # 表示小数点有多少位
    1. Parameters for relational fields (the most important and common two)
class A(models.Model):
    '''
    用于外键关联中的反向查询,可以通过父表查询到子表的信息
    子表是A,父表是Test
    如果需要通过Test查询到模型类A中的数据,可以使用related_name
    '''
    onetoone = models.OneToOneField(Test, related_name="one")


class B(models.Model):
    '''
    on_delete
    外键所管理的对象被删除时,进行什么操作
    1.models.CASCADE:模拟SQL语言中的ON DELETE CASCADE约束,将定义有外键的模型对象同时删除!(该操作为当前Django版本的默认操作)
    2.models.PROTECT:阻止上面的删除操作,但是弹出ProtectedError异常
    3.models.SET_NULL:将外键字段设为null,只有当字段设置了null=True时,方可使用该值
    4.models.SET_DEFAULT: 将外键字段设置为默认值,只有当字段设置了default参数时,方可使用
    5.models.DO_NOTHING: 什么也不做
    6:SET(): 设置位一个传递给SET()的值或者一个回调函数的返回值。注意大小写。
    '''
    foreign = models.ForeignKey(A, on_delete=models.CASCADE)
    foreign = models.ForeignKey(A, on_delete=models.PROTECT())
    foreign = models.ForeignKey(A, on_delete=models.SET_NULL, null=True, blank=True)
    foreign = models.ForeignKey(A, on_delete=models.SET_DEFAULT, default=0)
    foreign = models.ForeignKey(A, on_delete=models.DO_NOTHING)
    foreign = models.ForeignKey(A, on_delete=models.SET)

3.4 Self-association

A record of a model class is related to another record of itself.

For example, the implementation of provinces and cities

  • Created from the associated model class
    models.py file
class AddressInfo(models.Model):
    '''
    自关联的模型类有两种写法:
    1. “self”
    2. 自己模型类的名字
    '''
    address = models.CharField(max_length=200, null=True, blank=True, verbose_name="地址")
    pid = models.ForeignKey('self', null=True, blank=True, verbose_name="自关联", on_delete=models.CASCADE)

    # pid = models.ForeignKey("AddressInfo", null=True, blank=True, verbose_name="自关联")

    def __str__(self):
        '''
        给不同的模型对象返回可读的字符串
        :return:
        '''
        return self.address
  • In the development view function
    views.py file
from django.shortcuts import render
from django.views.generic import View
from django.http import JsonResponse
from .models import AddressInfo

# Create your views here.
class IndexView(View):
    '''主页'''
    def get(self, request):
        return render(request, "address.html")

class AddressAPI(View):
    def get(self, request, address_id): # 接受
        if int(address_id) == 0:
            address_data = AddressInfo.objects.filter(pid__isnull=True).values('id', 'address')
        else:
            address_data = AddressInfo.objects.filter(pid__id=int(address_id)).values('id', 'address')
        area_list = []
        for a in address_data:
            area_list.append({
    
    'id': a['id'], 'address': a['address']})
        return JsonResponse(area_list, content_type='application/json', safe=False)
  • Configure url routing
    in the urls.py file
from django.contrib import admin
from django.urls import path, re_path
from course.views import IndexView, AddressAPI

urlpatterns = [
    re_path("^admin/", admin.site.urls),
    re_path(r'^$', IndexView.as_view(), name='index'),
    re_path(r'^address/(\d+)$', AddressAPI.as_view(), name='address'),
]
  • Write the front-end code
    and create address.html under the templates folder
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>地址信息</title>
</head>
<body>
<select name="" id="pro">
    <option value="">请选择省</option>
</select>
<select name="" id="city">
    <option value="">请选择市</option>
</select>
<select name="" id="dis">
    <option value="">请选择县</option>
</select>

<script src="/static/jquery-3.7.0.min.js"></script>
<script>
    address = function (a,b){
      
      
        $.get('/address/' + a, function (dic){
      
      
            $.each(dic, function(index, item){
      
      
                b.append('<option value="' + item.id + '">' + item.address + '</option>')
            })
        })
    };
    $(function (){
      
      
        pro = $('#pro'); // 省
        city = $('#city'); // 市
        dis = $('#dis'); //县

        // 查询省信息
        address(0, pro);
        // 根据省的改变查询市的信息
        pro.change(function(){
      
      
            city.empty().append('<option value="">请选择市</option>');
            dis.empty().append('<option value="">请选择市</option>');
            address(this.value, city)
        })
        // 根据市的改变查询县的信息
        city.change(function(){
      
      
            dis.empty().append('<option value="">请选择县</option>');
            address(this.value, dis)
        })
    })
</script>
</body>
</html>
  • Register data class model in admin.py
from django.contrib import admin
from course.models import AddressInfo

# Register your models here.
admin.site.register(AddressInfo)
  • Create users via manage.py
createsuperuser

4.1 Metadata introduction

Django Meta metadata class property analysis

There is a subclass under each model class: Meta
is the place where metadata is defined.
It is used to define information related to databases or data tables,
such as setting display, setting sorting, setting unique keys, etc.

from django.db import models

class AddressInfo(models.Model):
    '''
    自关联的模型类有两种写法:
    1. “self”
    2. 自己模型类的名字
    '''
    address = models.CharField(max_length=200, null=True, blank=True, verbose_name="地址")
    pid = models.ForeignKey('self', null=True, blank=True, verbose_name="自关联", on_delete=models.CASCADE)
    # pid = models.ForeignKey("AddressInfo", null=True, blank=True, verbose_name="自关联")
    note = models.CharField(max_length=200, null=True, blank=True, verbose_name='说明')

    def __str__(self):
        '''
        给不同的模型对象返回可读的字符串
        :return:
        '''
        return self.address

    class Meta:
        # 定义元数据
        db_table = 'address'
        # ordering = ('pid',)  # 指定按照什么字段排序
        verbose_name = '省市县地址信息'  # 模型类的复数名
        verbose_name_plural = verbose_name
        # abstract = True  # 定义当前模型是否是抽象类,是否用于继承
        # permissions = (('定义好的权限','权限说明'),) # 二元元组,如((have_read_permission', '有读的权限'))
        # managed = False # 默认为True,是否按照Django既定的规则管理模型类
        unique_together = ('address', 'note')  # 一元元组或二元元组((), ()),联合唯一键
        # app_label = "" # 模型类在哪个应用
        # db_tablespace # 定义数据库表空间的名字

There will be model class development examples later
insert image description here

4.2 Model class development example

  • Lecturer Information Form
  • Course Information Sheet
  • Student Information Form
  • The teaching assistant information table
    The lecturer information table and the course information table have a many-to-one relationship: one lecturer may publish multiple courses.
    There is a many-to-many relationship between courses and students: one course can be studied by multiple students, and one student can study multiple courses.
    Teaching assistants and lecturers have a one-to-one relationship.

For Courses, Lecturer is the parent table
For Students, Courses is the parent table
For Teaching Assistants, Lecturer is the parent table
Foreign key fields are written in child tables
Many to many relationships generate intermediate tables

from django.db import models


# Create your models here.
class Teacher(models.Model):
    '''讲师信息表'''
    nickname = models.CharField(max_length=30, primary_key=True, db_index=True, verbose_name='昵称')
    introduction = models.TextField(default="这位同学很懒,木有签名的说", verbose_name="简介")
    fans = models.PositiveIntegerField(default='0', verbose_name="粉丝数")
    create_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")

    class Meta:
        verbose_name = "讲师信息表"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.nickname


class Course(models.Model):
    '''课程信息表'''
    title = models.CharField(max_length=100, primary_key=True, db_index=True, verbose_name='课程名')
    teacher = models.ForeignKey(to=Teacher, null=True, blank=True, on_delete=models.CASCADE, verbose_name='课程讲师')
    type = models.CharField(choices=((1, "实战课"), (2, "免费课"), (0, "其他")), max_length=1, default=0,
                            verbose_name="课程类型")
    price = models.PositiveSmallIntegerField(verbose_name="价格")
    volume = models.BigIntegerField(verbose_name='销量')
    online = models.DateField(verbose_name="上线时间")
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')

    class Meta:
        verbose_name = "课程信息表"
        verbose_name_plural = verbose_name

    def __str__(self):
        return f"{
      
      self.get_type_display()} - {
      
      self.title}"  # get_type_display是django-orm中自带的函数,用于获取枚举属性的字符串


class Student(models.Model):
    '''学生信息表'''
    nickname = models.CharField(max_length=30, primary_key=True, db_index=True, verbose_name='昵称')
    course = models.ManyToManyField(to=Course, verbose_name='课程')
    age = models.PositiveSmallIntegerField(verbose_name='年龄')
    gender = models.CharField(choices=((1, '男'), (2, "女"), (0, "保留")), max_length=1, default=0, verbose_name='性别')
    study_time = models.PositiveIntegerField(default='0', verbose_name='学习时长(h)')
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')

    class Meta:
        verbose_name = '学生信息表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.nickname


class TeacherAssistant(models.Model):
    '''助教信息表'''
    nickname = models.CharField(max_length=30, primary_key=True, db_index=True, verbose_name='昵称')
    teacher = models.OneToOneField(Teacher, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='讲师')
    hobby = models.CharField(max_length=100, null=True, blank=True, verbose_name='爱好')
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')

    class Meta:
        verbose_name = "助教信息表"
        db_table = "courses_assistant"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.nickname

5.1 Django change data table

makemigrations is the content to be generated to generate the database, you can find the corresponding py file under the migrations folder of the project.
And migrate is to execute the file we just generated, and store the execution process in the django_migrations table of the database.
So if you want to delete a model class, you need

  1. Delete the model class code.
  2. Delete the corresponding py file under the migrations folder.
  3. Delete the record corresponding to django_migrations.
  4. delete data table

5.2 Django data import

4 ways to import:

  1. Use the manage.py shell
manage.py shell

# 假设需要在讲师信息表种插入一条数据
from course.models import Teacher # 导入模型类
t = Teacher(nickname = "Jack") # 创建一个Teacher对象
t.save() # 保存

Not in line with practice, only import one by one

  1. The script implements batch import
    orm_data.py script
import os
import sys
import random
import django
from datetime import date

# 在imooc项目中运行脚本
project_path = os.path.dirname(
    os.path.dirname(os.path.abspath(__file__)))  # 'D:\计算机学习\Python\Python小项目\gx\djangoProject\djangoProject'
sys.path.append(project_path)  # 将项目路径添加到系统搜寻路径当中
os.environ['DJANGO_SETTINGS_MODULE'] = 'imooc.settings'  # 设置项目的配置文件
django.setup()  # 初始化一个django环境,便于orm操作

from course.models import Teacher, Course, Student, TeacherAssistant


def import_data():
    """使用Django ORM导入数据"""
    # 讲师数据 create()
    Teacher.objects.create(nickname='Jack', introduction='Python工程师', fans=666)
    Teacher.objects.create(nickname='Allen', introduction='Java工程师', fans=123)
    Teacher.objects.create(nickname='Henry', introduction='Golang工程师', fans=818)

    # 课程数据 bulk_create()
    Course.objects.bulk_create([Course(title=f'Python系列教程{
      
      i}', teacher=Teacher.objects.get(nickname="Jack"),
                                       type=random.choice((0, 1, 2)),
                                       price=random.randint(200, 300), volume=random.randint(100, 10000),
                                       online=date(2018, 10, 1))
                                for i in range(1, 5)])

    Course.objects.bulk_create([Course(title=f'Java系列教程{
      
      i}', teacher=Teacher.objects.get(nickname="Allen"),
                                       type=random.choice((0, 1, 2)),
                                       price=random.randint(200, 300), volume=random.randint(100, 10000),
                                       online=date(2018, 6, 4))
                                for i in range(1, 4)])

    Course.objects.bulk_create([Course(title=f'Golang系列教程{
      
      i}', teacher=Teacher.objects.get(nickname="Henry"),
                                       type=random.choice((0, 1, 2)),
                                       price=random.randint(200, 300), volume=random.randint(100, 10000),
                                       online=date(2018, 1, 1))
                                for i in range(1, 3)])

    # 学生数据 update_or_create()
    # 先通过nickname去查数据
    # 存在的话就更新defaults指明的数据字典,不存在就创建新的
    Student.objects.update_or_create(nickname="A同学", defaults={
    
    "age": random.randint(18, 58),
                                                                 "gender": random.choice((0, 1, 2)),
                                                                 "study_time": random.randint(9, 999)})
    Student.objects.update_or_create(nickname="B同学", defaults={
    
    "age": random.randint(18, 58),
                                                                 "gender": random.choice((0, 1, 2)),
                                                                 "study_time": random.randint(9, 999)})
    Student.objects.update_or_create(nickname="C同学", defaults={
    
    "age": random.randint(18, 58),
                                                                 "gender": random.choice((0, 1, 2)),
                                                                 "study_time": random.randint(9, 999)})
    Student.objects.update_or_create(nickname="D同学", defaults={
    
    "age": random.randint(18, 58),
                                                                 "gender": random.choice((0, 1, 2)),
                                                                 "study_time": random.randint(9, 999)})

    # 正向添加
    # 销量大于等于1000的课程
    Student.objects.get(nickname="A同学").course.add(*Course.objects.filter(volume__gte=1000))
    # 销量大于5000的课程
    Student.objects.get(nickname="B同学").course.add(*Course.objects.filter(volume__gt=5000))
    # 反向添加
    # 学习时间大于等于500小时的同学
    Course.objects.get(title='Python系列教程1').student_set.add(*Student.objects.filter(study_time__gte=500))
    # 学习时间小于等于500小时的同学
    Course.objects.get(title='Python系列教程1').student_set.add(*Student.objects.filter(study_time__lte=500))

    # 助教数据 get_or_create()
    # 存在就get查询,不存在就create创建
    TeacherAssistant.objects.get_or_create(nickname="助教1", defaults={
    
    "hobby": "慕课网学习",
                                                                       "teacher": Teacher.objects.get(nickname="Jack")})
    TeacherAssistant.objects.get_or_create(nickname="助教2", defaults={
    
    "hobby": "追慕女神",
                                                                       "teacher": Teacher.objects.get(
                                                                           nickname="Allen")})
    TeacherAssistant.objects.get_or_create(nickname="助教3", defaults={
    
    "hobby": "减肥减肥",
                                                                       "teacher": Teacher.objects.get(
                                                                           nickname="Henry")})
    return True


if __name__ == '__main__':
    if import_data():
        print("数据导入成功!")
  1. Fixtures provide serialized files that can be recognized by Django's serialization -> model, and then stored in the database
  • Export all data through the dumpdata command
# 通过dumpdata命令导出数据
python manage.py dumpdata > imooc.json

This command must be typed on the command line, and cannot be entered into manage.py through work and then typed

  • Delete teaching assistant information table data
  • Import data through fixtures
# 通过loaddata命令导入数据
python manage.py loaddata imooc.json 

In the process of loading data, the most likely error is that the code in the exported data file data.json is not utf-8. You need to convert the data.json file to utf-8 format, and then load the data into mysql.
The way to modify the file encoding can be done through notepad–.

  1. Import data at the database level

5.3 Django export data

  1. Export data through the dumpdata command
  2. Export in the database linked by pycharm
    insert image description here
  3. Export through database management software such as navicat

6.1 Introduction to query sets

objects is the object manager of the model class
. all is to fetch all the results

class IndexView(View):
    '''主页'''

    def get(self, request):
        # 1.查询、检索、过滤
        teachers = Teacher.objects.all()
        print(teachers) # <QuerySet [<Teacher: Allen>, <Teacher: Henry>, <Teacher: Jack>]>
        teacher2 = Teacher.objects.get(nickname='Jack') # get()只能返回一条结果,多条会报错,所以传递的通常是主键或者唯一键
        print(teacher2, type(teacher2)) # Jack <class 'course.models.Teacher'>
        teacher3 = Teacher.objects.filter(fans__gte=500) # 返回QuerySet,可以是多条结果
        for t in teacher3:
            print(f"讲师姓名{
      
      t.nickname}--粉丝数{
      
      t.fans}")
            '''
            讲师姓名Henry--粉丝数818
            讲师姓名Jack--粉丝数666
            '''

        # 2.字段数据匹配,大小写敏感
        teacher4 = Teacher.objects.filter(fans__in=[666, 1231])
        print(teacher4) # <QuerySet [<Teacher: Jack>]>
        teacher5 = Teacher.objects.filter(nickname__icontains='A') # 前面含i表示大小写不敏感
        print(teacher5) # <QuerySet [<Teacher: Allen>, <Teacher: Jack>]>

        # 3.结果切片、排序、链式查询
        print(Teacher.objects.all()[:1]) # <QuerySet [<Teacher: Allen>]>
        teacher6 = Teacher.objects.all().order_by('-fans') # 默认升序,前面带-表示降序
        print(teacher6) # <QuerySet [<Teacher: Henry>, <Teacher: Jack>, <Teacher: Allen>]>
        print(Teacher.objects.filter(fans__gte=500).order_by('nickname')) # 链式反应表示我们可以对返回的查询集继续使用API

        # 4.看执行的原生SQL
        print(str(Teacher.objects.filter(fans__gte=500).order_by('nickname').query)) # 直接打印查询集.query即可
        """select * from (SELECT `course_teacher`.`nickname`, `course_teacher`.`introduction`, `course_teacher`.`fans`,
         `course_teacher`.`create_at`, `course_teacher`.`updated_at` FROM `course_teacher` 
         WHERE `course_teacher`.`fans` >= 500 ORDER BY `course_teacher`.`nickname` ASC) ct
        """
        return render(request, "address.html")

6.2 API that returns a new QuerySet

There are two types of APIs.

6.2.1 all(), filter(), order_by(), exclude(), reverse(), distinct(): 去重

s1 = Student.objects.all().exclude(nickname='A同学')  # 排除A同学的类对象
for s in s1:
    print(s.nickname, s.age)
s2 = Student.objects.all().exclude(nickname='A同学').reverse() # 反向排序,必须先在模型类的Meta子类中设置ordering
for s in s2:
    print(s.nickname, s.age)

Changing the metadata of a model class does not require rebuilding the data table

6.2.2 extra(): implement field aliases, defer(): exclude some fields, only(): select some fields

s3 = Student.objects.all().extra(select={
    
    'name': "nickname"}) # 将nickname字段名改成name
for s in s3:
    print(s.name)

6.2.3. values(), values_list() Get the QuerySet in the form of dictionary or tuple

 print(TeacherAssistant.objects.values('nickname', 'hobby'))
 # <QuerySet [{'nickname': '助教1', 'hobby': '慕课网学习'}, {'nickname': '助教2', 'hobby': '追慕女神'}, ...]>
 print(TeacherAssistant.objects.values_list('nickname', 'hobby'))
 # <QuerySet [('助教1', '慕课网学习'), ('助教2', '追慕女神'), ('助教3', '减肥减肥')]>
 print(TeacherAssistant.objects.values_list('nickname', flat=True)) # 只取一个字段时,可以使用flat=True
 # <QuerySet ['助教2', '助教3', '助教1']>

6.2.4. dates(), datetimes() Get query set according to time and date

print(Course.objects.dates('created_at', 'month', order='DESC'))
# <QuerySet [datetime.date(2023, 11, 1), datetime.date(2023, 9, 1), datetime.date(2023, 7, 1)]>
print(Course.objects.datetimes('created_at', 'month', order='DESC')) # 可筛选的范围更多,年月日时分秒都可以

6.2.5. union(): union, intersection(): intersection, difference(): complement

p_240 = Course.objects.filter(price__gte=240)
p_260 = Course.objects.filter(price__lte=260)
print(p_240.union(p_260))
print(p_240.intersection(p_260))
print(p_240.difference(p_260))

6.2.6. select_related(): one-to-one, many-to-one query optimization

Django Basics (29): The usage and difference of select_related and prefetch_related

courses = Course.objects.all()
for c in courses:
    print(f"{
      
      c.title}--{
      
      c.teacher.nickname}--{
      
      c.teacher.fans}")

Configure the settings of the project, configure the log, so that it can output all debug information on the terminal,
first import the logging module in the settings.py file,
and then configure all the log managers at the bottom

# settings.py中
LOGGING = {
    
    
    'version': 1, # 日志级别
    # 日志处理器
    'handers': {
    
    
        'console': {
    
    
            'class': 'logging.StreamHandler',
        },
    },
    # 日志管理器
    'loggers': {
    
    
        'django.db.backends': {
    
    
            'handlers': ['console'],
            'level': 'DEBUG' if DEBUG else 'INFO',
        },
    }
}

insert image description here

It can be seen that the course table is checked first, and then the Teacher table is checked 9 times according to the foreign key.
Can the data of the parent table be queried at once when querying the child table? Reduce the number of queries?

Pass select_related and pass in the parameter teacher
insert image description here

The difference here is mainly to use select_related to merge the original two SQL commands into one SQL command, so only one database query is executed. If you look at the raw SQL command at this point, you'll see that Django makes use of knowledge about LEFT JOINs.

6.2.7 prefetch_realated(): many-to-many query optimization

students = Student.objects.filter(age__lt=40)
for s in students:
    print(s.course.all())

insert image description here

prefetch_related() can optimize many-to-many queries

students = Student.objects.filter(age__lt=30).prefetch_related('course')
for s in students:
    print(s.course.all())
return render(request, 'address.html')

insert image description here

6.2.8 Reverse query: query child table information through parent table

print(Teacher.objects.get(nickname='Jack').course_set.all())

insert image description here

You can set related_name for the child table to facilitate reverse query

class Course(models.Model):
    '''课程信息表'''
    title = models.CharField(max_length=100, primary_key=True, db_index=True, verbose_name='课程名')
    teacher = models.ForeignKey(to=Teacher, null=True, blank=True, on_delete=models.CASCADE, related_name='teac' verbose_name='课程讲师')
print(Teacher.objects.get(nickname='Jack').teac.all())

The effect of subtable name _set and related_name is the same

6.2.9 annotate(): using aggregated counts, sums, and averages

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

# 按teacher分组,组内按volume求和
print(Course.objects.values('teacher').annotate(vol=Sum('volume')))

6.2.10 raw: Execute native SQL

6.3 APIs that do not return QuerySet

Broadly divided into 5 types:

6.3.1 获取对象get(),get_or_create(),first(),last(),latest(),earliest(),in_bulck()

When using latest and earliest, you must set the get_latest_by field in the metadata of the model class as the basis for judging the latest and earliest, such as

class Course(models.Model):
    '''课程信息表'''
    title = models.CharField(max_length=100, primary_key=True, db_index=True, verbose_name='课程名')
    teacher = models.ForeignKey(to=Teacher, null=True, blank=True, on_delete=models.CASCADE, related_name='teac', verbose_name='课程讲师')
    type = models.CharField(choices=((1, "实战课"), (2, "免费课"), (0, "其他")), max_length=1, default=0,
                            verbose_name="课程类型")
    price = models.PositiveSmallIntegerField(verbose_name="价格")
    volume = models.BigIntegerField(verbose_name='销量')
    online = models.DateField(null=True, blank=True, verbose_name="上线时间")
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')

    class Meta:
        verbose_name = "课程信息表"
        get_latest_by = 'created_at'
        verbose_name_plural = verbose_name

in_bulck() returns objects in batches, and needs to return a list according to the primary key

print(Course.objects.in_bulk(['Python系列教程4', 'Golang系列教程1']))
# {'Golang系列教程1': <Course: 1 - Golang系列教程1>, 'Python系列教程4': <Course: 2 - Python系列教程4>}

6.3.2 Create object create(): create; bulk_create(): batch create; update_or_create(): create or update

6.3.3 Update object update(): update; update_or_create(): update or create

Course.objects.filter(title='Java系列教程2').update(price=300)

6.3.4 Delete object delete() Use filter to filter

Course.objects.filter(title='test').delete()

6.3.5 Other operations exists(), count(), aggregate() to determine whether it exists, count the number, and aggregate

print(Course.objects.filter(title='test').exists())
print(Course.objects.filter(title='Java系列教程2').exists())
print(Course.objects.count())
print(Course.objects.count())
print(Course.objects.aggregate(Max('price'), Min('price'), Avg('price')))

Annotate generally counts the grouped results, while aggregate counts the entire data table data

6.4 Custom aggregation query

Not all query functions can be implemented using the default API, such as string concatenation in the group query function.
How to customize aggregate queries: implement group_concat

Rewrite the existing aggregate function class aggregate, and overload the constructor of the parent class to achieve

class GroupConcat(models.Aggregate):
    """自定义实现聚合功能,实现GRUOP_CONCAT功能"""

    function = 'GROUP_CONCAT'
    template = '%(function)s(%(distinct)s%(expressions)s%(ordering)s%(separator)s)'

    def __init__(self, expression, distinct=False, ordering=None, separator=',', **extra):
        super(GroupConcat, self).__init__(expression,
                                          distinct='DISTINCT ' if distinct else '',
                                          ordering=' ORDER BY %s' % ordering if ordering is not None else '',
                                          separator=' SEPARATOR "%s"' % separator,
                                          output_field=models.CharField(), **extra)

courses = Course.objects.values('teacher').annotate(t=GroupConcat('title', distinct=False,
                                                                       ordering='title ASC',
                                                                       separator='-'))
# GROUP_CONCAT ( title ORDER BY title ASC SEPARATOR '-')
“”“
{
    
    'teacher': 'Allen', 't': 'Java系列教程1-Java系列教程2-Java系列教程3'}
{
    
    'teacher': 'Henry', 't': 'Golang系列教程1-Golang系列教程2'}
{
    
    'teacher': 'Jack', 't': 'Python系列教程1-Python系列教程2-Python系列教程3-Python系列教程4'}
”“”

7.1 Use of F objects and Q objects

7.1.1 Use of F-objects: data in the manipulation fields

  • Suppose you want to achieve the price of all courses on the curriculum -11
Course.objects.update(price=F('price') - 11)
  • Assume that you want to get the objects whose course sales are less than 20 times the price
print(Course.objects.filter(volume__lte=F('price') * 20))
# <QuerySet [<Course: 1 - Golang系列教程2>, <Course: 2 - Java系列教程1>, <Course: 2 - Java系列教程3>, <Course: 2 - Python系列教程4>]>

Must be fields of the same type

7.1.2 Use of Q objects: Combining AND, OR, NOT, |, ~, & to implement complex queries

  • Suppose you want to query all java courses with sales greater than 5000

  • Suppose you want to query all go languages ​​or the sales volume is less than 1000

print(Course.objects.filter(Q(title__icontains="java") & Q(volume__gte=5000)))
print(Course.objects.filter(Q(title__icontains="golang") |  Q(volume__lte=1000)))

Guess you like

Origin blog.csdn.net/zhangyifeng_1995/article/details/131914013
Recommended