介绍
基本概括
- 一对多: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。