Django ORM 多對多關係 (給中間表添加額外字段)

配置項目

編輯 settings.py,配置數據庫連接

"""
......
"""

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'test',
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': 'localhost',
        'PORT': '3306',
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"
        },
    }
}

"""
......
"""

新建app

python manage.py startapp orm_demo

添加到已安裝的app

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'orm_demo',
]

模型

編輯 orm_demo/models.py,寫一個很經典的模型,學生表,課表,選課表。

from django.db import models


class Student(models.Model):
    name = models.CharField(max_length=50)


class Course(models.Model):
    name = models.CharField(max_length=50)

    students = models.ManyToManyField(
        to='Student',
        related_name='courses',
        through='StudentCourse',
        through_fields=('course', 'student',)
    )


class StudentCourse(models.Model):
    student = models.ForeignKey(
        to='Student',
        related_name='sc',
        on_delete=models.CASCADE
    )
    course = models.ForeignKey(
        to='Course',
        related_name='sc',
        on_delete=models.CASCADE
    )
    grade = models.IntegerField()

    class Meta:
        db_table = 'orm_demo_sc'
        unique_together = ('student', 'course',)

把模型映射到數據庫中

python manage.py makemigrations
python manage.py migrate

生成的表結構:

選課表(中間表)的 DDL:

CREATE TABLE `orm_demo_course_students` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `grade` int(11) NOT NULL,
  `course_id` int(11) NOT NULL,
  `student_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `orm_demo_course_students_student_id_course_id_d0874fcc_uniq` (`student_id`,`course_id`),
  KEY `orm_demo_students_co_course_id_68b9b7e4_fk_orm_demo_` (`course_id`),
  CONSTRAINT `orm_demo_students_co_course_id_68b9b7e4_fk_orm_demo_` FOREIGN KEY (`course_id`) REFERENCES `orm_demo_course` (`id`),
  CONSTRAINT `orm_demo_students_co_student_id_3811d8a3_fk_orm_demo_` FOREIGN KEY (`student_id`) REFERENCES `orm_demo_student` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

基本操作

添加數據

Student.objects.create(name='小明')
Student.objects.create(name='小紅')
Student.objects.create(name='小王')
Student.objects.create(name='小張')
Student.objects.create(name='小陳')
Course.objects.create(name='語文')
Course.objects.create(name='數學')
Course.objects.create(name='英語')
Course.objects.create(name='體育')
Course.objects.create(name='物理')
Course.objects.create(name='化學')
Course.objects.create(name='生物')

students = Student.objects.all()
courses = Course.objects.all()

for student in students:
    for course in courses:
        grade = random.randint(50, 100)
        StudentCourse.objects.create(
            student=student,
            course=course,
            grade=grade
        )

查詢

成績優異的學生

students = Student.objects.filter(sc__grade__gt=95).distinct()
for student in students:
    print(student.name)
小明
小王
小張
小陳

print(students.query) 查看轉換的對應 sql 語句:

SELECT DISTINCT `orm_demo_student`.`id`, `orm_demo_student`.`name`
FROM `orm_demo_student` INNER JOIN `orm_demo_course_students` ON (`orm_demo_student`.`id` = `orm_demo_course_students`.`student_id`)
WHERE `orm_demo_course_students`.`grade` > 95

這一比較。。。很容易得出一個結論,人生苦短,我用python。

所有學生的平均成績

students = Student.objects.annotate(avg=Avg('sc__grade'))
for student in students:
    print(student.name, student.avg)
小明 82.0
小紅 64.2857
小王 77.5714
小張 80.0
小陳 71.1429

轉換成對應的 sql 語句:

SELECT `orm_demo_student`.`id`, `orm_demo_student`.`name`, AVG(`orm_demo_course_students`.`grade`) AS `avg`
FROM `orm_demo_student` LEFT OUTER JOIN `orm_demo_course_students` ON (`orm_demo_student`.`id` = `orm_demo_course_students`.`student_id`)
GROUP BY `orm_demo_student`.`id`
ORDER BY NULL

數學的最高分和最低分

result = Course.objects.get(name='數學').sc.aggregate(Max('grade'), Min('grade'))
print(result)
{'grade__max': 95, 'grade__min': 53}

不及格的記錄條數

result = StudentCourse.objects.filter(grade__lt=60).aggregate(count=Count('*'))
print(result)
{'count': 9}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章