Django数据表关系映射

在关系型数据库中,通常不会把所有数据都放在同一张表中,这样做会额外占用内存空间,
在关系列数据库中通常用表关联来解决数据库。
常用的表关联方式有三种:

  • 一对一映射
    如: 一个身份证对应一个人
  • 一对多映射
    如: 一个班级可以有多个学生
  • 多对多映射
    如: 一个学生可以报多个课程,一个课程可以有多个学生学习

一对一映射

  • 语法OneToOneField

    class A(model.Model):
        ...
    
    class B(model.Model):
        属性 = models.OneToOneField(A)
    
  • 用法示例
    创建男人和妻子类

    # file : xxxxxxxx/models.py
    from django.db import models
       
    class Man(models.Model):
        '''男人模型类'''
        name = models.CharField('姓名', max_length=50)
    
    class Wife(models.Model):
        '''妻子模型类'''
        name = models.CharField("姓名", max_length=50)
        husband = models. (Man)  # 增加一对一属性
    
  • 创始一对一的数据记录

    from . import models
    man1 = models.Man.objects.create(name='王先生')
    wife1 = models.Wife.objects.create(name='王夫人', husband=man1)  # 关联王老师
    man2 = models.Man.objects.create(name='隔壁老王')  # 一对一可以没有数据对应的数据 
    
  • 一对一数据的相互获取
    Wife 对象中,通过 husband 属性找到对应的Man对象
    Author对象中,通过wife 属性找到对应的Wife对象

  • 正向查询
    直接通过关联属性查询即可

    # 通过 wife 找 Man
    from . import models
    wife = models.Wife.objects.get(name='王夫人')
    print(wife.name, '的老公是', wife.husband.name)
    
  • 反向查询
    通过反向关联属性查询
    反向关联属性为实例对象.引用类名(小写),如男人的反向引用为男人对象.wife
    当反向引用不存在时,则会触发异常

    # 通过 man.wife 关联属性 找 wife,如果没有对应的wife则触发异常
    man1 = models.Man.objects.get(name='王先生')
    print(man1.name, '的妻子是', man1.wife.name)
    man2 = models.Man.objects.get(name='隔壁老王')
    try:
        print(man2.name, '的妻子是', man2.wife.name)
    except:
        print(man2.name, '还没有妻子')
    
  • 作用:
    主要是解决常用数据与不常用数据的存储问题,
    把经常加载的一个数据放在主表中,不常用数据放在另一个副表中。
    这样在访问主表数据时不需要加载副表中的数据以提高访问速度提高效率和节省内存空间,
    如经常把书的内容和书名建成两张表,因为在网站上经常访问书名等信息,但不需要得到书的内容。

一对多映射

  • 用法语法ForeignKey
    当一个A类对象可以关联多个B类对象时

    class A(model.Model):
        ...
    
    class B(model.Model):
        a = models.ForeignKey(A)
    
  • 外键类ForeignKey
    构造函数:ForeignKey(to, on_delete, **options)
    参数

    • to 关联的主类
    • on_delete
      models.CASCADE 级联删除。 Django模拟SQL约束ON DELETE CASCADE的行为,并删除包含ForeignKey的对象。
      models.PROTECT 抛出ProtectedError 以阻止被引用对象的删除;
      SET_NULL 设置ForeignKey值为null;需要指定该列null=True
      SET_DEFAULTForeignKey设置为其默认值;必须设置ForeignKey的默认值
      其它参请参考文档 https://docs.djangoproject.com/en/1.11/ref/models/fields/#foreignkey
    • **options 可以是常用的字段选项如:nullunique
  • 示例
    有二个出版社对应五本书的情况.
    清华大学出版社 有书(C++,Java,Python)
    北京大学出版社 有书(西游记,水浒)

    • 定义一对多类

      # file: one2many/models.py
      from django.db import models
      class Publisher(models.Model):
          '''出版社'''
          name = models.CharField('名称', max_length=50, unique=True)
      
      class Book(models.Model):
          title = models.CharField('书名', max_length=50)
          publisher = models.ForeignKey(Publisher, null=True)
      
      
    • 创建一对多的对象

      # file: xxxxx/views.py
      from . import models
      pub1 = models.Publisher.objects.create(name='清华大学出版社')
      models.Book.objects.creat e(title='C++', publisher=pub1)
      models.Book.objects.create(title='Java', publisher=pub1)
      models.Book.objects.create(title='Python', publisher=pub1) 
      
      pub2 = models.Publisher.objects.create(name='北京大学出版社')
      models.Book.objects.create(title='西游记', publisher=pub2)
      models.Book.objects.create(title='水浒', publisher=pub2)
      
    • 查询:
      通过多查一通过 publisher 属性查询即可

      # 通过一本书找到对应的出版社
      abook = models.Book.objects.get(id=1)
      print(abook.title, '的出版社是:', abook.publisher.name)
      

      通过一查多通过book_set(),管理器对象

      # 通过出版社查询对应的书
      pub1 = models.Publisher.objects.get(name='清华大学出版社')
      books = pub1.book_set.all()  # 通过book_set 获取pub1对应的多个Book数据对象
      # books = models.Book.objects.filter(publisher=pub1)  # 也可以采用此方式获取
      print("清华大学出版社的书有:")
      for book in books:
          print(book.title)
      

多对多映射

多对多表达对象之间多对多复杂关系,如: 每个人都有不同的学校(小学,初中,高中,…),每个学校都有不同的学生…

  • 语法ManyToManyField

  • 示例
    一个作者可以出版多本图书
    一本图书可以被多名作者同时编写

    class Author(models.Model):
        ...
    
    class Book(models.Model):
        ...
        authors = models.ManyToManyField(Author)
    
  • 数据查询
    通过 Book 查询对应的所有的 Authors

    book.authors.all() -> 获取 book 对应的所有的author的信息
    book.authors.filter(age__gt=80) -> 获取book对应的作者中年龄大于80岁的作者的信息
    

    通过 Author 查询对应的所有的Books
    Django会生成一个关联属性 book_set 用于表示对对应的book的查询对象相关操作

    author.book_set.all()
    author.book_set.filter()
    author.book_set.create(...)  # 创建新书并联作用author
    author.book_set.add(book)   # 添加已有的书为当前作者author
    author.book_set.clear()  # 删除author所有并联的书
    
  • 示例:
    多对多模型

    class Author(models.Model):
        '''作家模型类'''
        name = models.CharField('作家', max_length=50)
        def __str__(self):
            return self.name
    class Book(models.Model):
        title = models.CharField('书名', max_length=50)
        author = models.ManyToManyField(Author)
        def __str__(self):
            return self.title
    

    多对多视图操作

    from django.http import HttpResponse
    
    from . import models
    
    def many2many_init(request):
        # 创建两人个作者
        author1 = models.Author.objects.create(name='吕泽')
        author2 = models.Author.objects.create(name='王老师')
    
        # 吕择和王老师同时写了一本Python
        book11 = author1.book_set.create(title="Python")
        author2.book_set.add(book11)  #
    
        # 王老师还写了两本书
        book21 = author2.book_set.create(title="C")  # 创建一本新书"C"  
        book22 = author2.book_set.create(title="C++")  # 创建一本新书"C++"
    
        return HttpResponse("初始化成功")
    
    def show_many2many(request):
        authors = models.Author.objects.all()
        for auth in authors:
            print("作者:", auth.name, '发出版了', auth.book_set.count(), '本书: ')
            for book in books:
                print('    ', book.title)
        print("----显示书和作者的关系----")
        books = models.Book.objects.all()
        for book in books:
            auths = book.author.all()
            print(book.title, '的作者是:', '、'.join([str(x.name) for x in auths]))
        return HttpResponse("显示成功,请查看服务器端控制台终端")
    

实际开发中,对于多对多的关系,在数据库设计层面,一般都会使用中间表来处理。
中间表中以外键的方式(一对多)关联两个多方。
同样的,在Django中也可以使用中间模型类来处理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章