zoukankan      html  css  js  c++  java
  • Django 连表操作

    介绍

    基本概括

    • 一对多:models.ForeignKey(其他表)
    • 多对多:models.ManyToManyField(其他表)
    • 一对一:models.OneToOneField(其他表)

    当你去调用它们时。关系如下:

    第一个参数:模型,模型名(str)

    第二个参数:是与主表与从表的关系。

    • CASCADE 级联,删除主表数据时连通一起删除外键表中数据
    • PROTECT 保护,通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据
    • SET_NULL 设置为NULL,仅在该字段null=True允许为null时可用
    • SET_DEFAULT 设置为默认值,仅在该字段设置了默认值时可用
    • SET() 设置为特定值或者调用特定方法
    • DO_NOTHING 不做任何操作,如果数据库前置指明级联性,此选项会抛出IntegrityError异常

    注意事项

    1.id字段不写的话会自动添加
    2.对于外键字段,Django会在字段名上添加"_id"来创建数据库中的列名
    3.外键字段ForeignKey有一个null=True的设置,你可以赋给它空值None

    文章参考:http://www.py3study.com/Article/details/id/1562.html

    一对一

    OneToOneField

    定义模型

    作者与作者详细,可以看成一对一的关系。

    class Author(models.Model):
        name = models.CharField(max_length=30)
        age = models.IntegerField()
        gender = models.CharField(max_length=30)
    class AuthorDetail(models.Model):
        address = models.CharField(max_length=50)
        phone = models.IntegerField()
        author = models.OneToOneField('Author',models.CASCADE,default='')

    创建数据

    创建作者

    from my_app import models
    
        author = models.Author.objects
        data = {
            'name':'kidd',
            'age':18,
            'gender':''
        }
    
        author.create(**data)

    创建作者详细

    第一种

    from my_app import models
    
        author = models.Author.objects.filter(id=1)[0]
        detail = models.AuthorDetail.objects
        data = {
            'address':'青岛市',
            'phone':12345,
            'author':author
        }
        detail.create(**data)

     第二种

    from my_app import models
    
        detail = models.AuthorDetail.objects
        data = {
            'address':'青岛市',
            'phone':54321,
            'author_id':1
        }
        detail.create(**data)

    如果不知道author_id从哪来的,建议先看注意事项。

    跨表查询

    基于对象

    返回类型:str

    正向查询

    通过作者姓名查询详细地址

    from my_app import models
    
        author = models.Author.objects.filter(name='kidd')[0]
        author_address = author.authordetail.address

    反向查询

    通过手机号查询作者姓名

    from my_app import models
    
        detail = models.AuthorDetail.objects.get(phone=54321)
        author = detail.author.name

    基于下划线

    返回类型:QuerySet

    正向查询

    from my_app import models
    
    address = models.Author.objects.filter(name='kidd').values('authordetail__address')

    反向查询

    from my_app import models
    
    author = models.AuthorDetail.objects.filter(author__name='kidd').values('address')

    一对多

    ForeignKey

    定义模型

    出版社可以出不同的书。

    class Publish(models.Model):
        name = models.CharField(max_length=50)
        city = models.CharField(max_length=50)
    class Book(models.Model):
        title = models.CharField(max_length=50)
        price = models.IntegerField()
        publish = models.ForeignKey(Publish,models.CASCADE)

    创建数据

    创建出版社

        from my_app import models
    
        data = {
            'name':'清X',
            'city':'北京',
        }
        models.Publish.objects.create(**data)

    创建书

    第一种

    from my_app import models
    
        obj = models.Publish.objects.get(id=1)
        data = [
            {
                'title':'九阳真经',
                'price':68,
                'publish':obj
            },
            {
                'title':'易筋经',
                'price':66,
                'publish':obj
            }
        ]
        for value in data:
            models.Book.objects.create(**value)

    第二种

        from my_app import models
    
        data = [
            {
                'title':'正骨内经',
                'price':99,
                'publish_id':1
            },
            {
                'title': '太玄经',
                'price': 89,
                'publish_id': 1
            },
        ]
        for value in data:
            models.Book.objects.create(**value)

    跨表查询

    基于对象

    正向查询

    查看书从哪个城市出版

        from my_app import models
    
        book = models.Book.objects.filter(id=2).first()
        city = book.publish.city

    反向查询

    查看出版社出版的所有书名

        from my_app import models
    
        publish = models.Publish.objects.filter(id=1).first()
        books = publish.book_set.all()
        for book in books:
            print(book.title)

    基于下划线

    正向查询

        from my_app import models
    
        city = models.Book.objects.filter(id=2).values('publish__city')
        print(city)

    反向查询

        from my_app import models
    
        books = models.Publish.objects.filter(id=1).values('book__title')
        for book in books:
            print(book.get('book__title'))

    多对多

    ManyToManyField

    定义模型

    作者跟书之间存在多对多的关系,一个作者可以创建好几本书,一本书可以有好几个作者。

    class Author(models.Model):
        name = models.CharField(max_length=30)
        age = models.IntegerField()
        gender = models.CharField(max_length=30)
    
    class Book(models.Model):
        title = models.CharField(max_length=50)
        price = models.IntegerField()
        authors = models.ManyToManyField(Author)

    当你迁移完数据后,你会发现多了一张表,而这样表是Django自动创建的。我的这站表名为:my_app_book_authors

    创建数据

    创建作者

    from my_app import models
    
        persons = [
            {
                'name': '小明',
                'age':28,
                'gender':''
            },
            {
                'name': '小红',
                'age': 26,
                'gender': ''
            },
            {
                'name': '小军',
                'age': 28,
                'gender': ''
            },
        ]
    
        for person in persons:
            models.Author.objects.create(**person)

    创建书

        from my_app import models
    
        book = models.Book.objects.create(title='学不会的数学',price=99)
        persons = models.Author.objects.filter(id__lt=5)
        book.authors.add(*persons)

    上面创建方式因为是从book中进行创建的,因为在Book中有author对象

    如果想要在作者中创建,即没有ManyToManyField,可以加:模型名__set

    关系表:

    现在我想把:作者id=3,书名 学不会的数学且价格为99从关系表中删除。

        from my_app import models
    
        book = models.Book.objects.filter(title='学不会的数学',price=99).first()
        author = models.Author.objects.filter(id=3).first()
        book.authors.remove(author)

    更多

    # 清空被关联对象集合,无需传参
    book.authors.clear()
    # 先清空再设置,传递的参数必须是可迭代对象,一般为列表,列表内可以是对象,也可以是id
    book.authors.set()

    跨表查询

    基于对象

    正向查询

    从书中查找所有作者

        from my_app import models
    
        books = models.Book.objects.filter(title='学不会的数学')[0]
        books = books.authors.all()
        for book in books:
            print(book.name)

    反向查询

    查看作者写的书

        from my_app import models
    
        authors = models.Author.objects.get(id=1)
        authors = authors.book_set.all()
        for author in authors:
            print(author.title)

    基于下划线

    正向查询

    按字段

        from my_app import models
    
        authors = models.Book.objects.filter(title='学不会的数学').values('authors__name')
        for author in authors:
            print(author.get('authors__name'))

    反向查询

    按表名

        from my_app import models
    
        authors = models.Author.objects.filter(book__title='学不会的数学').values('name')
        for author in authors:
            print(author.get('name'))

    多对多(手动)

    当我们使用Django自带的ManyToManyField时,它会自动的创建第三张表。这里我们将手动创建

    class Person(models.Model):
        name = models.CharField(max_length=64)
    class Hobby(models.Model): title = models.CharField(max_length=64)
    class PH_Key(models.Model): person = models.ForeignKey(Person,models.CASCADE) hobby = models.ForeignKey(Hobby,models.CASCADE) class Meta: unique_together = ['person','hobby'] # 保证值唯一
    from my_app import models
        person = models.Person.objects
        hobby = models.Hobby.objects
        ph_key = models.PH_Key.objects
        ph_key.create(person=person.get(id=1),hobby=hobby.get(id=1))

    当你再去创建一样的时,会报错。因为加了值唯一。

    不建议这么去用,还是用Django自带的吧。

    聚合查询

    语法

    aggregate(*args,**kwargs)

    是QuerySet中的一个方法。

    使用

    需要函数,一块联合用应。

    算出下图的平均值,总和。

    from django.db.models import Avg,Sum
    from my_app import models
    
    # 使用默认键名
    result = models.Person.objects.all().aggregate(Avg('age'),Sum('age'))
    
    # 自定义键名
    result = models.Person.objects.all().aggregate(avg=Avg('age'),sum=Sum('age'))

    更多函数

    1.expression
        引用模型字段的一个字符串,或者一个query expression
    2.output_field
        用来表示返回值的model field,一个可选的参数
    3.extra
        关键字参数可以给聚合函数生成的SQL提供额外的信息
    4.Avg
        返回给定表达式的平均值,它必须是数值,除非指定不同的output_field
    5.Count
        返回与expression相关的对象的个数,有一个可选的参数distinct,如果distinct=True,那么Count将只计算唯一的实例,默认值是False
    6.Max
        返回给定字段的最大值
    7.Min
        返回给定字段的最小值
    8.Sum
        返回给定字段的总和

    分组查询

    语法

    annotate(*args,**kwargs)

    也是QuerySet中的一个方法。

    使用

    当然,他也需要与函数联合使用。这可能是个糟糕的例子。

    如下图:从名字相同中,找出最大年龄。

    from django.db.models import Max
    from my_app import models
    
    result = models.Person.objects.all().values('name').annotate(max=Max('age'))

    小结

    # values在annotate()之前,表示group by。之后,表示为取值
    
    # filter在annotate()之前,表示过滤。之后,表示having

    F查询

    注意:只能对数值进行操作。

    将上面的图,继续拿下来使用。

    现在我想将他们的年龄全部加2岁。

    from django.db.models import F
    from my_app import models
    
    models.Person.objects.all().update(age=F('age')+2)

    Q查询

    说明

    # filter()等方法只能是'AND'运算。
    
    # filter(id__ge=0,id__le=10) 即:1<id<9
    
    # 如果需要执行复杂的查询,就需要使用Q对象
    
    # 导入包:
    from django.db.models import Q
    
    # 操作符:
    "&""|""~"

    实例

    依然是这样图表:

    1、查询 id=1 或 id=3 的人名。

    from django.db.models import Q
    from my_app import models
    
    result = models.Person.objects.filter(Q(id=1)|Q(id=3)).values('name')

    2、人名为kidd,年龄大于20,的id。

    from django.db.models import Q
    from my_app import models
    
    result = models.Person.objects.filter(Q(name='kidd'),age__gt=20).values('id')

    也可以这样

    result = models.Person.objects.filter(Q(name='kidd') & Q(age__gt=20)).values('id')

    3、人名不为kidd,且年龄为20,其他所有人的人名。

    from django.db.models import Q
    from my_app import models
    
    result = models.Person.objects.filter(~Q(name='kidd')&Q(age=20)).values('name')

    查询结果

    有时候在多对多表中,常常会有重复的ID。

    例如,老师跟班级之间的关系,老师可以交多个班级,班级可以有多个老师,当你在取数据时会出现一个老师出现多次的情况,为了避免这种情况可以简单做一个字典操作。

    obj = models.Teacher.objects.all().values("id","name","grade__caption","grade__id")
        obj_dic = {}
        for key in obj:
            nid = key["id"]
            if obj_dic.get(nid):
                obj_dic[nid]["grade_list"].append({"grade_id":key["grade__id"],"caption":key["grade__caption"]})
            else:
                obj_dic[nid] = {
                    "nid":nid,
                    "name":key["name"],
                    "grade_list":[
                        {"grade_id":key["grade__id"],"caption":key["grade__caption"]}
                    ]
                }

    总结

    在数据库中,其实没有一对一,多对多全都是模拟出来的,只有一个一对多也就是ForeignKey。

    一对一:

    在要限制的字段中,添加只能出现一次就好了,也就是Django中的 unique=Ture

    多对多:

    创建第三张表,将表进行ForeignKey。

  • 相关阅读:
    跨域常见解决方案
    express-session的简单使用说明
    Spring Cloud中,如何解决Feign/Ribbon第一次请求失败的问题?
    继承父类的静态方法的加载顺序
    sql索引优化
    EXPLAIN 执行计划详解
    JVM总括二-垃圾回收:GC Roots、回收算法、回收器
    dubbo知识体系
    Spring bean的生命流程
    日志体系与异常处理
  • 原文地址:https://www.cnblogs.com/py-peng/p/12625214.html
Copyright © 2011-2022 走看看