Django框架-数据库查询、一对一、一对多、多对多关系

Django框架—数据库查询、一对一、一对多、多对多关系

一、创建模型类:

# -*- 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[应用的名称]
    
  • 迁移成功后可以进行以下的操作咯~

二、一对一:

2.1:查:

  • 通过子表查询母表:子表对象.母表表名的小写.母表字段名

  • 方式一:

    # 先获取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)
    
    # 输出结果:
    小明
    

2.2:增:

  • 方式一:

    # 首先再子表中创建一条记录, 并将新创建的对象给母表进行关联
    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)
    

2.3:改:

  • 方式一:

    # 获取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)
    

2.4:删:

  • 方式一:

    # 使用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(课程章节表)

3.1:正向查询:

  • 可通过英文的点 + 属性,访问外键对象

    # 获取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)  # 不会访问数据库, 直接使用缓存的数据
    

3.2:反向查询:

  • 若一个模型有ForeignKey,那么该ForeignKey所指向的外键模型实例可通过一个管理器进行反向查询,返回模型的所有实例。默认情况下,管理器名为xxx_set, xxx是源模型的小写名称。

    # 首先将上面的CourseChapter表中的FK,related_name去掉, 再演示下面的例子
    class Coursechapter(models.Model):
        """课程章节表"""
        .........省略.........
        course = models.ForeignKey('Course')
    
    # 获取Course表中ID1的对象
    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表中ID1的对象
    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的方式

3.3:外键表联合查询:

  • 子表对象.(点)母表表名的小写.(点)母表字段名

    # 通过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)
    # 输出结果:
    上海交通大学
    

3.4:增:

  • 方式一:

    # 给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()
    

3.5:改:

  • 方式一:

    # 从子表进行修改
    # 首先根据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()方法返回的是已经更改的行数。

3.6:删:

  • 方式一:

    # 使用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()方法返回的是查询集,可以进行批量删除。

四、多对多:

4.1:查:

  • 子表查询母表:子表对象.子表多对多字段.过滤条件

  • 方式一:

    # 获取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>]>
    

4.2:增、改:

# 首先将之前表的一些结构发生了改变, 去掉之前手动添加的第三张表, 改成自动创建的第三张表
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)
    

4.3:删:

  • 使用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的应用列表中就显得特别重要。否则,反向关联将不能正确工作。

你可能感兴趣的:(Django)