介绍

基本概括

  • 一对多: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)

Django 连表操作

创建作者详细

第一种

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)

Django 连表操作

 第二种

from my_app import models

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

Django 连表操作

如果不知道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)

Django 连表操作

创建书

第一种

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)

Django 连表操作

第二种

    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)

Django 连表操作

跨表查询

基于对象

正向查询

查看书从哪个城市出版

    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)

Django 连表操作

创建书

    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)

Django 连表操作

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

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

关系表:

Django 连表操作

现在我想把:作者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)

Django 连表操作

更多

# 清空被关联对象集合,无需传参
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 连表操作

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

聚合查询

语法

aggregate(*args,**kwargs)

是QuerySet中的一个方法。

使用

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

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

Django 连表操作

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'))

Django 连表操作

更多函数

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中的一个方法。

使用

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

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

Django 连表操作

from django.db.models import Max
from my_app import models

result = models.Person.objects.all().values('name').annotate(max=Max('age'))

Django 连表操作

小结

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

# filter在annotate()之前,表示过滤。之后,表示having

F查询

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

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

Django 连表操作

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

from django.db.models import F
from my_app import models

models.Person.objects.all().update(age=F('age')+2)

Django 连表操作

Q查询

说明

# filter()等方法只能是'AND'运算。

# filter(id__ge=0,id__le=10) 即:1<id<9

# 如果需要执行复杂的查询,就需要使用Q对象

# 导入包:
from django.db.models import Q

# 操作符:
"&""|""~"

实例

依然是这样图表:

Django 连表操作

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')

Django 连表操作

 

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')

Django 连表操作

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')

Django 连表操作

查询结果

有时候在多对多表中,常常会有重复的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"]}
                ]
            }

Django 连表操作

总结

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

一对一:

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

多对多:

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