# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# Create your models here.
from django.db import models
class Course(models.Model):
"""课程表"""
name = models.CharField(verbose_name='课程名称', max_length=255)
description = models.TextField(verbose_name='课程描述信息', null=True)
price = models.DecimalField(verbose_name=u'课程价格', max_digits=15, decimal_places=2, default=0.0)
deleted = models.BooleanField(verbose_name='课程是否被删除', default=False)
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
edited_at = models.DateTimeField(auto_now=True)
class User(models.Model):
"""用户表:记录用户常用信息"""
name = models.CharField(verbose_name=u'用户的姓名', max_length=32, null=True)
mobile = models.CharField(verbose_name=u'用户的手机号', max_length=32, unique=True)
courses = models.ManyToManyField(verbose_name=u'关联课程', to='Course', through='UserCourse', related_name='user_course')
class UserExtra(models.Model):
"""用户信息额外表: 存储一些用户不常用信息"""
birthday = models.CharField(verbose_name=u'生日', max_length=32, null=True)
email = models.EmailField(verbose_name=u'邮箱', null=True)
user = models.OneToOneField(to=User, related_name='extra') # 跟User表是一对一的关系
class UserCourse(models.Model):
"""课程表跟用户表手动添加的多对多表"""
course = models.ForeignKey('Course', related_name='my_course')
user = models.ForeignKey('User', related_name='my_user')
class Coursechapter(models.Model):
"""课程章节表"""
name = models.CharField(verbose_name='课程名称', max_length=255)
description = models.TextField(verbose_name='课程描述信息', null=True)
course = models.ForeignKey('Course', related_name='course_chapter', null=True)
执行以下命令,进行数据库的迁移:
python manage.py makemigrations app_name[应用的名称]
python manage.py migrate app_name[应用的名称]
迁移成功后可以进行以下的操作咯~
通过子表查询母表:子表对象.母表表名的小写.母表字段名
方式一:
# 先获取User表ID=1的对象
try:
user_obj = models.User.objects.get(pk=1)
except models.User.DoesNotExist:
return JsonResponse({"资源不存在"})
# 通过返回的User对象获取到UserExtra表的email字段
extra_email = user_obj.extra.email
print(extra_email)
# 输出结果:
280773872@qq.com
从母表自身直接获取字段:母表.objects.get(子表名小写__子表字段="xxx").母表字段名
方式二:
# 通过UserExtra表, 获取user_id=1的Extra对象的email字段内容
user_extra = models.UserExtra.objects.get(user__id=1).email
print(user_extra)
# 输出结果:
280773872@qq.com
通过母表查询子表:母表对象.子表表名的小写.子表字段名
方式三:
# 通过ID获取Extra对象.user(子表表名).name(子表字段名)
user_name = models.UserExtra.objects.get(pk=1).user.name # 获取的是user表的name
print(user_name)
# 输出结果:
小明
方向从子表查询:子表.objects.get(一对一的子表字段__母表字段="xxx").子表字段
方式四:
# 首先通过User表, 获取到Extra email=280773872的User对象,
user_name = models.User.objects.get(extra__email=280773872).name
print(user_name)
# 输出结果:
小明
方式一:
# 首先再子表中创建一条记录, 并将新创建的对象给母表进行关联
user_obj = models.User.objects.create(mobile=13051323222, name='小牛')
models.UserExtra.objects.create(user=user_obj, email=390942648)
方式二:
# 跟方式一类似, 只是方式二使用的是ID的方式,进行创建新的记录
user_obj = models.User.objects.create(mobile=13051323322, name='大牛')
models.UserExtra.objects.create(user_id=user_obj.id, email=390942648)
方式三:
# 先创建User对象, 再通过Extra的实例调用.save()
user_obj = models.User.objects.create(mobile=13051323323, name='红牛')
extra_obj = models.UserExtra(user=user_obj, email=123456)
extra_obj.save()
方式四(字典):
# 跟方式一、二相似,只是UserExtra表的参数,是通过字典进行传入
user_obj = models.User.objects.create(mobile=13051333323, name='红牛')
create_extra_dict = {"email": 123321, "birthday": "1993-0630"}
models.UserExtra.objects.create(user=user_obj, **create_extra_dict)
方式一:
# 获取User表ID=1的对象, 使用save()方法将获取User对象的name字段进行更改
user_obj = models.User.objects.get(pk=1)
user_obj.name = "铁牛"
user_obj.save()
方式二:
# 通过user_id获取UserExtra表的查询集,将Extra表中email字段进行更改
models.UserExtra.objects.filter(user_id=1).update(email=38291023)
方式一:
# 使用get()方法进行删除Extra表user_id=1的数据
models.UserExtra.objects.get(user_id=1).delete()
方式二:
# 通过QuerySet方式进行删除, 将Extra表所有数据都清空
models.UserExtra.objects.all().delete()
设置外键(ForeignKey)的字段,这张表一定是指多的一方
举个栗子:
class Course(models.Model):
"""课程表"""
........省略..........................
class Coursechapter(models.Model):
"""课程章节表"""
..........省略.............................
course = models.ForeignKey('Course', related_name='course_chapter')
可以看到以上两张表之间的关系,一个Course(课程表)会对应多个Coursechapter(课程章节表)
可通过英文的点 + 属性,访问外键对象
:
# 获取Coursechapter表 主键为1的对象
try:
chapter_obj = models.Coursechapter.objects.get(pk=1)
except models.Coursechapter.DoesNotExist:
return JsonResponse({"资源不存在"})
# 访问外键对象
course_obj = chapter_obj.course
print(course_obj)
# 输出结果, 是Course对象:
Course object
要对外键进行修改,必须调用save()方法进行保存
:
# 获取Coursechapter表 主键为1的对象
try:
chapter_obj = models.Coursechapter.objects.get(pk=1)
except models.Coursechapter.DoesNotExist:
return JsonResponse({"资源不存在"})
# 修改chapter表中的外键course_id 更改为2
chapter_obj.course_id = 2
chapter_obj.save()
# 可看到数据库中course_id 更改为了2
若外键字段设置有null=True属性,可给该字段复制为None的方法进行移除外键
:
# 获取Coursechapter表, 主键为1的对象
try:
chapter_obj = models.Coursechapter.objects.get(pk=1)
except models.Coursechapter.DoesNotExist:
return JsonResponse({"资源不存在"})
# 移除Coursechapter表中course_id的外键值
chapter_obj.course_id = None
chapter_obj.save()
# 可看到数据库中course_id=null
第一次使用外键关系进行正向访问时,关系对象将被缓存,随后再次使用关系对象的访问会使用这个缓存
:
try:
chapter_obj = models.Coursechapter.objects.get(pk=1)
except models.Coursechapter.DoesNotExist:
return JsonResponse({"资源不存在"})
print(chapter_obj.course) # 访问数据库, 获取实际数据
print(chapter_obj.course) # 不会访问数据库, 直接使用缓存的数据
使用QuerySet的select_related()方法会递归地填充所有一对多关系到缓存中
:
try:
chapter_obj = models.Coursechapter.objects.select_related().get(pk=1)
except models.Coursechapter.DoesNotExist:
return JsonResponse({"资源不存在"})
print(chapter_obj.course) # 不会访问数据库, 直接使用缓存的数据
print(chapter_obj.course) # 不会访问数据库, 直接使用缓存的数据
若一个模型有ForeignKey,那么该ForeignKey所指向的外键模型实例可通过一个管理器进行反向查询,返回模型的所有实例。默认情况下,管理器名为xxx_set, xxx是源模型的小写名称。
# 首先将上面的CourseChapter表中的FK,related_name去掉, 再演示下面的例子
class Coursechapter(models.Model):
"""课程章节表"""
.........省略.........
course = models.ForeignKey('Course')
# 获取Course表中ID为1的对象
try:
course_obj = models.Course.objects.get(pk=1)
except models.Course.DoesNotExist:
return JsonResponse({"资源不存在"})
# 通过表名小写_set 进行反向查询到CourseChapter表的数据
chapter_set = course_obj.coursechapter_set.all()
print(chapter_set)
# 输出结果:CourseChapter表中有2条属于Course对象的数据
<QuerySet [<Coursechapter: Coursechapter object>, <Coursechapter: Coursechapter object>]>
可再ForeignKey字段的定义中,通过设置related_name来重写上面的栗子(xxx_set)。
# 将FK字段, 设置related_name
class Coursechapter(models.Model):
"""课程章节表"""
.........省略...............
course = models.ForeignKey('Course', related_name='course_chapter')
# 获取Course表中ID为1的对象
try:
course_obj = models.Course.objects.get(pk=1)
except models.Course.DoesNotExist:
return JsonResponse({"资源不存在"})
# 通过设置的related_name 来查询到CourseChapter表,全部的对象,返回查询集
chapter_set = course_obj.course_chapter.all()
print(chapter_set)
# 输出结果:
<QuerySet [<Coursechapter: Coursechapter object>, <Coursechapter: Coursechapter object>]>
# 也可以进行filter过滤, name='第一小节'
chapter_set = course_obj.course_chapter.filter(name='第一小节')
print(chapter_set)
# 输出结果:
<QuerySet [<Coursechapter: Coursechapter object>]>
# 也可以获取CourseChapter表中的数量
chapter_count = course_obj.course_chapter.count()
print(chapter_count)
# 输出结果:
2
注意:若FK字段设置related_name,则不可以使用xxx_set的方式
子表对象.(点)母表表名的小写.(点)母表字段名
# 通过chapter表查到course表中的name字段
try:
chapter_obj = models.Coursechapter.objects.get(pk=1)
except models.Coursechapter.DoesNotExist:
return JsonResponse({"资源不存在"})
# chapter_obj(子表).course(母表).name(母表字段名)
course_name = chapter_obj.course.name
print(course_name)
# 输出结果:
上海交通大学 # course表中对应name字段的信息
母表.objects.get(子表名__子表字段="xxx").母表字段名
# 反向从母表入手
course_name = models.Course.objects.get(course_chapter__id=1).name
print(course_name)
# 输出结果:
上海交通大学
方式一:
# 给chapter表增加一条数据
try:
course_obj = models.Course.objects.get(pk=1)
except models.Course.DoesNotExist:
return JsonResponse({"资源不存在"})
# 子表chapter增加一条数据, 外键course 引用上面course_obj ID=1的对象
cur_chapter_obj = models.Coursechapter.objects\
.create(course=course_obj, name='第一小节', description="哲学")
print(cur_chapter_obj)
# 输出结果:
Coursechapter object # 返回CourseChapter对象
方式二:
# 跟方式一相似
try:
course_obj = models.Course.objects.get(pk=1)
except models.Course.DoesNotExist:
return JsonResponse({"资源不存在"})
# 只是创建时外键course变成了course_id = course_obj.id
cur_chapter_obj = models.Coursechapter.objects\
.create(course_id=course_obj.id, name='第二小节', description="法学")
print(cur_chapter_obj)
# 输出结果:
Coursechapter object # 返回CourseChapter对象
方式三:
# 方式一、二使用的是create方法, 方式三用的是save()
try:
course_obj = models.Course.objects.get(pk=1)
except models.Course.DoesNotExist:
return JsonResponse({"资源不存在"})
chapter_obj = models.Coursechapter(
course=course_obj, name='第三节课', description='哲学')
chapter_obj.save()
方式一:
# 从子表进行修改
# 首先根据Chapter表过滤course表字段name="上海交通大学", 返回的是chapter表的查询集, 然后将返回的查询集进行批量更改
models.Coursechapter.objects.filter(course__name="上海交通大学").update(name='上海财经大学')
方式二:
# 先获取母表中的对象, 然后通过course_chapter反向到chapter表, 进行批量更改
try:
course_obj = models.Course.objects.get(pk=1)
except models.Course.DoesNotExist:
return JsonResponse({"资源不存在"})
# 将母表的对象.设置的反向引用,获取chpater表的查询集, 然后调用update()方法进行批量更改
course_obj.course_chapter.update(description='体育学')
注意:update()方法返回的是已经更改的行数。
方式一:
# 使用get方法,将chapter表ID=1进行删除
models.Coursechapter.objects.get(pk=1).delete()
方式二:
# 使用filter()方法, 将chapter表course_id=1的全部删除
models.Coursechapter.objects.filter(course_id=1).delete()
注意:对象和QuerySet都有delete()方法,get()方法返回的是一个对象进行删除,filter()方法返回的是查询集,可以进行批量删除。
子表查询母表:子表对象.子表多对多字段.过滤条件
方式一:
# 获取User表ID=1的对象
try:
user_obj = models.User.objects.get(pk=1)
except models.User.DoesNotExist:
return JsonResponse({"资源不存在"})
# User对象.courses(多对多字段)=UserCourse表的查询集
user_course_list = user_obj.courses.all()
print(user_course_list)
# 输出结果:
<QuerySet [<Course: Course object>]> # 返回结果是QuerySet
子表反向查询:母表对象.filter(字表表名小写__子表字段名='过滤条件')
方式二:
# 通过Course表反向查询, user_course表ID=1,Course查询集
course_list = models.Course.objects.filter(user_course__id=1)
print(course_list)
母表查询子表:
方式三:
# 下面的方法将courses设置的related_name去掉
class User(models.Model):
"""用户表:记录用户常用信息"""
.......省略.........
courses = models.ManyToManyField(verbose_name=u'关联课程', to='Course', through='UserCourse')
# 先通过ID获取Course对象
try:
course_obj = models.Course.objects.get(pk=1)
except models.Course.DoesNotExist:
return JsonResponse({"资源不存在"})
# Course对象.表名小写_set(), 获取对应的User表
user_list = course_obj.user_set.all()
print(user_list)
# 输出结果:
<QuerySet [<User: User object>]> # User表的查询集
母表查询子表:filter(子表外键字段__母表字段='过滤条件')
方式四:
# 获取User表所对应的Course表中name="上海交通大学"的User查询集
user_list = models.User.objects.filter(courses__name='上海交通大学')
print(user_list)
# 输出结果:
<QuerySet [<User: User object>]>
母表查询子表:filter(子表外键字段=母表主键对象)
方式五:
# 先获取Course对象
try:
course_obj = models.Course.objects.get(pk=1)
except models.Course.DoesNotExist:
return JsonResponse({"资源不存在"})
# 通过设置的ManyToManyField字段,进行筛选
user_list = models.User.objects.filter(courses=course_obj)
print(user_list)
# 输出结果:
<QuerySet [<User: User object>]>
# 首先将之前表的一些结构发生了改变, 去掉之前手动添加的第三张表, 改成自动创建的第三张表
class User(models.Model):
"""用户表:记录用户常用信息"""
name = models.CharField(verbose_name=u'用户的姓名', max_length=32, null=True)
mobile = models.CharField(verbose_name=u'用户的手机号', max_length=32, unique=True)
courses = models.ManyToManyField(Course)
# 将之前手动创建的第三张多对多表进行注释, 然后重新进行数据库迁移
# class UserCourses(models.Model):
# """课程表跟用户表手动添加的多对多表"""
# course = models.ForeignKey('Course', related_name='my_course')
# user = models.ForeignKey('User', related_name='my_user')
# 迁移完数据库后, 可以看到数据库会出现一个xxx[app名称]_user_courses的表
使用add()方法进行创建:子表对象.子表多对多字段.add(*QuerySet查询集)
方式一:
# 首先创建User表对象
cur_user_obj = models.User.objects.create(mobile=13212332322, name='上海财经大学')
# 返回Course表的查询集
course_list = models.Course.objects.all()
# 使用add()方法新增第三张表信息
cur_user_obj.courses.add(*course_list)
使用.save()方法进行创建:
方式二:
# 获取User对象
user_obj = models.User.objects.get(pk=2)
# 查询Course,返回查询集
course_list = models.Course.objects.all()
# 使用对象.对多对字段名 = 查询集
user_obj.courses = course_list
# 调用.save()方法进行保存多对多表中的信息
user_obj.save()
使用表名_ set进行创建一条数据:母表对象.子表名小写_set.add(子表对象)
方式三:
# 先获取User表对象
user_obj = models.User.objects.get(pk=3)
# 再获取Course表对象
course_obj = models.Course.objects.get(pk=1)
# 创建一条UserCourse表记录
course_obj.user_set.add(user_obj)
使用表名_ set进行创建多条数据:母表对象.子表名小写_set.add(*子表查询集)
方式四:
# 首先获取User表查询集
user_list = models.User.objects.all()
# 获取Course对象
course_obj = models.Course.objects.get(pk=2)
# 批量创建Course对象所对应的User查询集
course_obj.user_set.add(*user_list)
使用remove(*queryset)方法
方式一:
# 先将User表ID=2的对象进行获取
user_obj = models.User.objects.get(pk=2)
# 获取所有Course表的数据
course_list = models.Course.objects.all()
# 调用remove()方法, 将UserId=2全部删除
user_obj.courses.remove(*course_list)
使用clear()方法
方式二:
# 将User表ID=3的,再UserCourse表中全部删除
user_obj = models.User.objects.get(pk=3)
user_obj.courses.clear()
注意:remove()跟clear()区别,remove()可以通过参数,过滤一些查询条件,而clear()直接将所有满足一个条件的数据,全部删除
class User(models.Model):
"""用户表:记录用户常用信息"""
.......省略..........
courses = models.ManyToManyField(verbose_name=u'关联课程', to='Course', through='UserCourse', related_name='user_course')
class UserCourse(models.Model):
"""课程表跟用户表手动添加的多对多表"""
course = models.ForeignKey('Course', related_name='my_course')
user = models.ForeignKey('User', related_name='my_user')
注意:手动创建的第三张多对多表与普通的多对多不一样,使用手动创建的多对多不能使用add()、create()、remove()和、set()方法来创建、删除关系, 唯独clear()方法可执行,一旦通过手动创建第三张表,就立刻像普通的多对多那样进行查询操作。
一些ORM框架需要你在关系的两端都进行定义。Django的开发者认为这违反了DRY (Don’t Repeat Yourself)原则,所以在Django中你只需要在一端进行定义。
那么这是怎么实现的呢?因为在关联的模型类没有被加载之前,一个模型类根本不知道有哪些类和它关联。
答案在app registry
!在Django启动的时候,它会导入所有INSTALLED_APPS
中的应用和每个应用中的模型模块。每创建一个新的模型时,Django会自动添加反向的关系到所有关联的模型。如果关联的模型还没有导入,Django将保存关联的记录并在关联的模型导入时添加这些关系。
由于这个原因,将模型所在的应用都定义在INSTALLED_APPS
的应用列表中就显得特别重要。否则,反向关联将不能正确工作。