以前提到过Django自带一个叫作sqlite3的小型数据库,当咱们作本地测试时,能够直接在sqlite3上测试。不过该数据库是小型的,在有些细节可能体验不大好,好比用ORM用双下划线查询语法时,使用__contains和__icontains的结果是同样的,由于sqlite3不管怎么样都不区分大小写,并且它还会自动把日期格式的字段转为时间戳(该体验贼差)。python
不过除此以外还好,目前也没发现其余问题,作一些数据的小测试仍是绰绰有余的。mysql
1.1 项目settings.py文件git
链接其余数据库时,咱们须要修改settings.py里面的相关配置,不过默认的配置就是sqlite3的,因此咱们不须要修改涉及数据库的配置。sql
回忆一下,链接其余数据库时(以链接myqsl数据库为例),应该修改成如下配置:数据库
1.2 修改应用下或项目下的的__init__.py文件django
跟链接其余数据库同样,须要在__init__.py中加入替换操做数据库的模块的语句:并发
import pymysql pymysql.install_as_MySQLdb()
1.3 链接sqlite3数据库app
选择链接sqlite3数据库:框架
安装驱动,测试链接成功后点击OK链接sqlite数据库:less
1.3步骤执行完毕后,咱们能够将下图显示的那个数据库删除(固然留着也没事)。
1.4 在模型层中写几个映射数据库中表的类
from django.db import models # Create your models here. class User(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() register_time = models.DateField()
1.5 执行数据库迁移命令(可使用Tools中的Run manage.py Task)
#pycharm下方点击Terminal打开命令行窗口,执行如下两句命令 python3 manage.py makemigrations python3 manage.py migrate
执行完数据库迁移命令后,咱们会发现项目中出现了db.sqlite3数据库,直接双击打开:
1.6 数据库sqlite3链接完成
数据库链接完毕后,咱们能够选择新建.py文件或者使用应用文件夹下自带的tests.py来导入模型层models.py中的类来熟悉一下ORM语句,不过在这以前要作一些准备工做,否则当你经过ORM操做数据库时会报错。
咱们直接使用应用文件夹下自带的tests.py文件测试ORM。首先输入如下语句(前几行能够从manage.py中复制)。
from django.test import TestCase # Create your tests here. import os if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day58.settings") import django django.setup() # 以上语句固定写法,接下来写导入models模块的语句 from app01 import models # 从这开始正常写ORM操做数据库的语句便可
当咱们操做数据库拿回来的是QuerySet对象是,咱们能够经过QuerySet对象.query查看内部的sql语句,那么返回的不是QuerySet对象时该怎么作呢?只须要在settings.py中找个地方将下面语句放着便可(运用日志)。
配置文件配置参数查看全部orm操做内部的sql语句 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
为了更好的了解ORM对数据库的操做,咱们选择链接mysql数据库,毕竟sqlite3仍是有点小瑕疵的。
from django.db import models # Create your models here. class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8, decimal_places=2) publish_time = models.DateField(auto_now_add=True) publish = models.ForeignKey(to='Publish') authors = models.ManyToManyField(to='Author') class Publish(models.Model): name = models.CharField(max_length=32) addr = models.CharField(max_length=32) email = models.EmailField() class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() author_detail = models.OneToOneField(to='AuthorDetail') class AuthorDetail(models.Model): phone = models.CharField(max_length=32) addr = models.CharField(max_length=32)
ORM有不少经常使用字段,其中字段DateField的参数有auto_now跟auto_now_add,若是咱们配置了这两个参数中的一个,建立数据对象时DateField字段会自动建立,无需咱们传值。配置auto_now_add=True,建立数据记录的时候会把当前时间添加到数据库,以后就不在变化。配置上auto_now=True,每次更新数据记录的时候会更新该字段。友情提示,DateFIeld和DateTImeField能够直接接收datetime对象。
ForeignKey有to_field参数,能够指定你想跟一张表的什么字段创建外键,不写默认是id。
当使用数据对象进行数据的save()和delete()时,实际上找的是类Model中的方法(属性与方法的查找顺序),因此咱们能够经过重写save、delete方法来对数据对象保存和删除的过程加以限制。
1.1 新增数据
# 第一种:有返回值,而且就是当前被建立的数据对象 modles.Publish.objects.create(name='',addr='',email='') # 第二种:先实例化产生对象,而后调用save方法保存 book_obj = models.Publish(name='',addr='',email='') book_obj.save()
1.2 修改数据
# 基于数据对象 user_obj = models.User.objects.filter(name='jason').first() user_obj.age = 17 user_obj.save() # 基于queryset对象 models.User.objects.filter(name='kevin').update(age=66)
1.3 删除数据
# 基于数据对象 # user_obj = models.User.objects.filter(name='owen').first() # user_obj.delete() # 基于queryset对象 models.User.objects.filter(name='egon').delete()
1.4 查询数据
以前经常使用的查询是all(惰性查询)查全部及filter条件查询,其中filter条件查询有多个条件时,各条件是and的关系。
models.User.objects.filter(name='egon', age=18) # name='egon' and 'age'=18
其实查询数据能用到的方法一共有13个(objects对象和QuerySet对象方法),最好都记住。而后QuerySet对象可以无限制的点QuerySet方法(后面点方法可能会没有提示,别慌,继续手打):
# 在过滤获得的QuerySet对象的基础上再进行过滤,依次类推 models.User.objects.filter(过滤条件1).filter(过滤条件2).order_by(排序依据的字段名).reverse() # reverse方法使用的前提是QuerySet对象被排序过,order_by是排序(排序依据的字段名前加-号便是反向排序)
<1> all(): #查询全部结果 <2> filter(**kwargs): #它包含了与所给筛选条件相匹配的对象 <3> get(**kwargs): #返回与所给筛选条件相匹配的对象,返回结果有且只有一个,若是符合筛选条件的对象超过一个或者没有都会抛出错误。 <4> exclude(**kwargs): #它包含了与所给筛选条件不匹配的对象 <5> order_by(*field): #对查询结果排序('-id')/('price') <6> reverse(): #对查询结果反向排序 >>>前面要先有排序才能反向 <7> count(): #返回数据库中匹配查询(QuerySet)的对象数量。 <8> first(): #返回第一条记录 <9> last(): #返回最后一条记录 <10> exists(): #若是QuerySet包含数据,就返回True,不然返回False <11> values(*field): #返回一个ValueQuerySet——一个特殊的QuerySet,运行后获得的并非一系列model的实例化对象,而是一个可迭代的字典序列 <12> values_list(*field): #它与values()很是类似,它返回的是一个元组序列,values返回的是一个字典序列 <13> distinct(): #从返回结果中剔除重复纪录
着重记住values方法(普通QuerySet对象:<QuerySet [<Author: Author object>]>):
在上述13种方法中,有些方法执行完返回的不必定是QuerySet对象,多是其余对象。
# 返回QuerySet对象的方法 all()、filter()、exclude()、order_by()、reverse()、distinct() # 特殊的QuerySet values() #返回一个可迭代的字典序列 values_list() #返回一个可迭代的元祖序列 # 返回具体数据对象的 get()、first()、last() # 返回布尔值的方法 exists() #判断QuerySet对象是否为空,空返回False,不然返回True # 返回数字的方法 count() #返回当前QuerySet对象中的数据对象个数
舒适提示:all()跟filter()是惰性查询,即返回QuerySet对象,只有调用QuerySet对象时内部的sql语句才会执行(这就是惰性的精髓)。objects是管理器对象(objects = Manage()),是Models和数据库进行查询的接口。Manage存在于models模块中,因此咱们实际上是能够自定义一个类,继承models.Manage来定义本身的管理器对象的。
上述的查询中,filter的查询条件都是name='值',age=值之类的,但是咱们查询时条件确定会有age>18,age<=18等状况。这个时候就须要用的双下划线查询。
2.1 大于 小于 大于等于 小于等于
filter(price__gt=90) # 大于 great than filter(price__lt=90) # 小于 less than filter(price__gte=90) # 大于等于 great than equal filter(price__lte=90) # 小于等与 less than equal
2.2 存在于某几个条件中
filter(age__in=[11,22,33])
2.3 在某个范围内
filter(age__range=[50,90])
2.4 模糊查询
filter(title__contains='p') # 区分大小写 filter(title__icontains='P') # 不区分大小写
2.5 以什么开头,以什么结尾
# 查询名字以j开头的用户 res = models.User.objects.filter(name__startswith='j') print(res) # 查询名字以n结尾的用户 res = models.User.objects.filter(name__endswith='n') print(res)
2.6 按年查询(针对DateField和针对DateTImeField)
filter(create_time__year='2017')
1.1 新增(主键用pk传比较好,比较稳)
# 直接写id models.Book.objects.create(title='红楼梦',price=66.66,publish_id=1) # 传数据对象 publish_obj = models.Publish.objects.filter(pk=2).first() models.Book.objects.create(title='三国演义',price=199.99,publish=publish_obj)
1.2 修改
# Queryset修改 models.Book.objects.filter(pk=1).update(publish_id=3) publish_obj = models.Publish.objects.filter(pk=2).first() models.Book.objects.filter(pk=1).update(publish=publish_obj) # 对象修改 book_obj = models.Book.objects.filter(pk=1).first() book_obj.publish_id = 3 # 点表中真实存在的字段名 book_obj.save() publish_obj = models.Publish.objects.filter(pk=2).first() book_obj.publish = publish_obj # 点orm中字段名 传该字段对应的表的数据对象 book_obj.save()
1.3 删除
# 使用QuerySet对象删除 models.Book.objects.filter(pk=1).delete() models.Publish.objects.filter(pk=1).delete() # 使用数据对象删除 book_obj = models.Book.objects.filter(pk=3).first() book_obj.delete()
2.1 添加(add)
# 拿到id=3的书籍数据对象 book_obj = models.Book.objects.filter(pk=3).first() # 数据对象.authors能够直接跳到多对多那张表里 # add传值传做者id,能够传多个 book_obj.authors.add(1) book_obj.authors.add(2,3) # add传值支持传对象,并且能够传多个 author_obj = models.Author.objects.filter(pk=1).first() author_obj1 = models.Author.objects.filter(pk=3).first() book_obj.authors.add(author_obj) book_obj.authors.add(author_obj,autor_obj1)
2.2 修改(set)
set接收的参数必须是可迭代对象!!!
# 拿到id=3的书籍数据对象 book_obj = models.Book.objects.filter(pk=3).first() # 数据对象.authors能够直接跳到多对多那张表里 # set传值传做者id,能够传多个 book_obj.authors.set((1,)) book_obj.authors.set((1,2,3)) # set传值支持传对象,并且能够传多个 author_list = models.Author.objects.all() book_obj = models.Book.objects.filter(pk=3).first() book_obj.authors.set(author_list)
2.3 删除(remove)
# 拿到id=3的书籍数据对象 book_obj = models.Book.objects.filter(pk=3).first() # 数据对象.authors能够直接跳到多对多那张表里 # remove传值支持传值,并且能够传多个 book_obj.authors.remove(1) book_obj.authors.remove(2,3) # remove传值支持传对象,并且能够传多个 author_obj = models.Author.objects.all().first() author_list = models.Author.objects.all() book_obj.authors.remove(author_obj) book_obj.authors.remove(*author_list) #须要将Queryset对象打散
2.4 清空(clear)
清空的是你当前这个表记录对应的绑定关系,不会影响其余表。
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.clear()
正向反向查询概念及方法:
# 一对一 # 正向:author---关联字段在author表里--->authordetail 按字段 # 反向:authordetail---关联字段在author表里--->author 按表名小写 # 查询jason做者的手机号 正向查询 # 查询地址是 :山东 的做者名字 反向查询 # 一对多 # 正向:book---关联字段在book表里--->publish 按字段 # 反向:publish---关联字段在book表里--->book 按表名小写_set.all() 由于一个出版社对应着多个图书 # 多对多 # 正向:book---关联字段在book表里--->author 按字段 # 反向:author---关联字段在book表里--->book 按表名小写_set.all() 由于一个做者对应着多个图书 # 连续跨表 # 查询图书是三国演义的做者的手机号,先查书,再正向查到做者,在正向查手机号 # 总结:基于对象的查询都是子查询,这里能够用django配置文件自动打印sql语句的配置作演示
3.1 基于对象的表查询
# 查询书籍是三国演义的出版社邮箱 book_obj = models.Book.objects.filter(title='三国演义').first() print(book_obj.publish.email) # 查询书籍是水浒传的做者的姓名 book_obj = models.Book.objects.filter(title='水浒传').first() print(book_obj.authors) # app01.Author.None print(book_obj.authors.all()) # 查询做者为jason电话号码 user_obj = models.Author.objects.filter(name='jason').first() print(user_obj.authordetail.phone)
# 查询出版社是东方出版社出版的书籍 一对多字段的反向查询 publish_obj = models.Publish.objects.filter(name='东方出版社').first() print(publish_obj.book_set) # app01.Book.None print(publish_obj.book_set.all()) # 查询做者jason写过的全部的书 多对多字段的反向查询 author_obj = models.Author.objects.filter(name='jason').first() print(author_obj.book_set) # app01.Book.None print(author_obj.book_set.all()) # 查询做者电话号码是110的做者姓名 一对一字段的反向查询 authordetail_obj = models.AuthorDetail.objects.filter(phone=110).first() print(authordetail_obj.author.name)
3.2 基于双下划线的查询
# 查询书籍为三国演义的出版社地址 res = models.Book.objects.filter(title='三国演义').values('publish__addr','title') print(res) # 查询书籍为水浒传的做者的姓名 res = models.Book.objects.filter(title='水浒传').values("authors__name",'title') print(res) # 查询做者为jason的家乡 res = models.Author.objects.filter(name='jason').values('authordetail__addr') print(res)
# 查询南方出版社出版的书名 res = models.Publish.objects.filter(name='南方出版社').values('book__title') print(res) # 查询电话号码为120的做者姓名 res = models.AuthorDetail.objects.filter(phone=120).values('author__name') print(res) # 查询做者为jason的写的书的名字 res = models.Author.objects.filter(name='jason').values('book__title') print(res) # 查询书籍为三国演义的做者的电话号码 res = models.Book.objects.filter(title='三国演义').values('authors__authordetail__phone') print(res)
3.3 小练习
# 查询jason做者的手机号 # 正向 res = models.Author.objects.filter(name='jason').values('authordetail__phone') print(res) # 反向 res = models.AuthorDetail.objects.filter(author__name='jason').values('phone') print(res) # 查询出版社为东方出版社的全部图书的名字和价格 # 正向 res = models.Publish.objects.filter(name='东方出版社').values('book__title','book__price') # print(res) 反向 # res = models.Book.objects.filter(publish__name='东方出版社').values('title','price') print(res) # 查询东方出版社出版的价格大于400的书 # 正向 res = models.Publish.objects.filter(name="东方出版社",book__price__gt=400).values('book__title','book__price') print(res) # 反向 res = models.Book.objects.filter(price__gt=400,publish__name='东方出版社').values('title','price') print(res)
在查询的时候先把orm查询语句写出来,再看用到的条件是否在当前表内,在就直接获取,不在就按照正向按字段反向按表名小写来查便可。切忌一口吃成胖子。
须要先导入模块:
from django.db.models import Max,Min,Count,Sum,Avg
# 查询全部书籍的做者个数 res = models.Book.objects.filter(pk=3).aggregate(count_num=Count('authors')) print(res) # 查询全部出版社出版的书的平均价格 res = models.Publish.objects.aggregate(avg_price=Avg('book__price')) print(res) # 4498.636 # 统计东方出版社出版的书籍的个数 res = models.Publish.objects.filter(name='东方出版社').aggregate(count_num=Count('book__id')) print(res)
# 统计每一个出版社出版的书的平均价格 res = models.Publish.objects.annotate(avg_price=Avg('book__price')).values('name','avg_price') print(res) # 统计每一本书的做者个数 res = models.Book.objects.annotate(count_num=Count('authors')).values('title','count_num') print(res) # 统计出每一个出版社卖的最便宜的书的价格 res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price') print(res) # 查询每一个做者出的书的总价格 res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price') print(res)
目前为止咱们filter的条件的值都是咱们本身给的,好比age=18。并且filter多个查询条件时,它们是and的关系。当咱们查询条件的值是来自表中某字段的值,或者咱们查询条件是or的关系,咱们就要引入F、Q查询。
首先F、Q使用前要先导入:
from django.db.models import F,Q
1.1 F查询
假若有一张服装店的销售表(商品、价格、库存、销量),当咱们想要查询销量大于50的商品时很好实现:
res = models.Product.objects.filter(sell__gt=50) print(res)
当咱们要查询销量大于库存的商品时,就要使用F查询了:
from django.db.models import F,Q # F查询 res = models.Product.objects.filter(sell__gt=F('stock')) print(res)
将全部商品的价格提升100:
models.Product.objects.update(price=F('price')+100)
将全部商品的名字后面都加一个爆款:
# 是否是本能会这么想?注意,这是错误的写法,由于ORM不支持+拼接字符串 models.Product.objects.update(price=F('name')+'爆款')
ORM不支持+拼接字符串,mysql中咱们拼接字符串要用concat,此处同理:
# 要进行字符串的拼接须要先导入Concat和Value模块 from django.db.models.functions import Concat from django.db.models import Value models.Product.objects.update(name=Concat(F('name'), Value('爆款')))
1.2 Q查询
Q查询能够将filter中的查询条件变成or的关系,并且还能经过实例化对象的方法实现属性能够是字符串的形式(查询时原本要写name='xxx',Q查询能够实现'name'='xxx')。
注意:Q对象必须放在普通的过滤条件前面。
# Q查询 res = models.Product.objects.filter(price=188.88, name='连衣裙爆款') print(res) from django.db.models import F, Q res = models.Product.objects.filter(Q(price=188.88), Q(name='连衣裙爆款')) # and res1 = models.Product.objects.filter(Q(price=188.88)|Q(name='连衣裙爆款')) # or res2 = models.Product.objects.filter(Q(price=188.88)|~Q(name='连衣裙爆款')) # not # 混合使用 须要注意的是Q对象必须放在普通的过滤条件前面 res3 = models.Product.objects.filter(~Q(name='连衣裙爆款'), price=188.88) # not
前面说Q查询能够实现查询条件写成'name'='xxx'的格式(好比说咱们写一个函数,根据用户输入去数据库中查询,而用户输入都是字符串的形式)。也许你会进行如下尝试:
user_input = 'name' res = models.Author.objects.filter(eval(user_input)='jason') print(res)
而后就会立刻迎来喜报:
要实现该需求,咱们须要使用Q实例化出的对象来实现(先来看一下Q的源码):
实现的代码以下:
from django.db.models import F, Q q = Q() # 先实例化出一个Q的对象 q.connector = 'or' # 经过这个参数能够将Q对象默认的and关系变成or q.children.append(('price', 188.88)) q.children.append(('name', '高跟鞋爆款')) res = models.Product.objects.filter(q) # Q对象查询默认是and,上方截图为证 print(res)
2.1 事务的四大特性:
Atomic(原子性):事务中包含的操做被看作一个逻辑单元,这个逻辑单元中的操做要么所有成 功,要么所有失败。
Consistency(一致性):事务完成时,数据必须处于一致状态,数据的完整性约束没有被破坏,事务在执行过程当中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务历来没 有执行过同样。
Isolation(隔离性):事务容许多个用户对同一个数据进行并发访问,而不破坏数据的正确性 和完整性。同时,并行事务的修改必须与其余并行事务的修改相互独立。
Durability(持久性):事务结束后,事务处理的结果必须可以获得固化。
2.2 事务的语法
在sql语句中使用事务时,咱们须要用start开启事务,又要用end结束事务,这就须要咱们判断事务什么时候开始和结束比较好。而在Django的ORM中,直接使用上下文管理便可,省事又省心。
这里直接拿上面提到过的销售表作示例,好比一件商品卖出后,销量要+1,对应的库存要-1。
# 导入事务的模块 from django.db import transaction from django.db.models import F with transaction.atomic(): # atomic原子性,要么全成功,要么全失败 # 在with代码块儿写你的事务操做 models.Product.objects.filter(id=1).update(stock=F('stock')-1) models.Product.objects.filter(id=1).update(sell=F('sell')+1) # 写其余代码逻辑 print('支付宝到帐~~~')
only和defer是相反的两个过程,有点相似filter()和exclude()的区别。咱们知道values返回的是列表套字典形式的QuerySet对象,而only和defer返回的是列表套对象的QuerySet对象。
以Product为例,当使用only('name')时,返回的对象只携带Product对象的name属性,若是用对象.未携带的属性,那么它会自动再去数据库中Product映射的表中查找该属性,有就带回来返回给你。defer正好相反,defer('name')表示携带除name以外的全部属性,当对象.name时,会自动去数据库中Product映射的表中找name属性带回来给你。注意:only和defer自动找数据库找属性时会走sql语句。
# 输出一下用values取出来的结果(列表套字典,键为'name') test = models.Product.objects.values('name') print(test) # only取出来的是列表套对象,对象只有name属性 res = models.Product.objects.only('name') for i in res: print(i.name) # defer取出来的是列表套对象,对象除了name属性,其余属性都有 res1 = models.Product.objects.defer('name') for i in res: print(i.name)
Django支持自定义字段,须要继承models.Field类。
# 自定义字段,对应数据库中的Char类型 class MyCharField(models.Field): # 数据库中Char类型须要指定长度,因此要传max_length def __init__(self,max_length,*args,**kwargs): self.max_length = max_length # 调用父类init,必定要写关键字传参,由于Field的init参数不少,能够看一下它的源码 super().__init__(max_length=max_length,*args,**kwargs) # 该方法也不能少 def db_type(self, connection): return 'char(%s)'%self.max_length
父类models.Field中的db_type方法
提一下,当咱们给一个表新增一个字段时,若是表中已经有数据了,那么咱们能够将models.py中新增的字段名null设置默认值或者是设置为空。
# 设置默认值 gender = models.IntegerField(default=2) # 容许为空 gender = models.IntegerField(null=True)
choices属性使用的场景也挺多,好比性别,在数据库中咱们能够存储数字,好比1表明男,2表明女,而显示给用户看时,要转变为对应的男或女。
简单示范一下:
class User(models.Model): name = models.CharField(max_length=32) choices = ((1,'男'),(2,'女'),(3,'其余')) gender = models.IntegerField(choices=choices,default=2)
这样数据库里存的就是数字了,那么取出来怎么变成对应的男、女呢:
res = models.User.objects.filter(id=1).first() print(res.gender) # 获取编号对应的中文注释,固定写法get_字段名_display() print(res.get_gender_display()) # 建立对象是传数字便可 models.User.objects.create(...gender=1)
Django大体分为模型层(models)、模板层(templates)、视图层(views)、路由层(urls)。由于Django中控制器接受用户输入的部分由框架自行处理,因此 Django 里更关注的是模型models、模板templates和视图views,称为 MTV模式。不过本质上也是属于MVC框架(模型model,视图view和控制器controller)。
以前建立测试ORM的表时,多对多关系的表是经过语句让ORM自动帮咱们建立的,实际上建立多对多的表有三种方式。
第一种:Django主动咱们帮咱们建立
该方法很方便,可是第三张表字段都是固定的,没法扩展
class Book(models.Model): name = models.CharField(max_length=32) # 该命令让Django自动帮咱们建立多对多关系的第三张表 authors = models.ManyToManyField(to='Author') class Author(models.Model): name = models.CharField(max_length=32)
第二种:纯手动建立第三张表
该方式能够在第三张表中增长额外的字段,可是没法像第一种方式建立的表同样直接利用ORM正向反向查询信息(好比book对象要查询Author表中的内容,只能是反向查询至Book2Author这张表,而后经过该表正向查询Author表的内容)。
class Book(models.Model): name = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) class Book2Author(models.Model): # 经过建立多对多关系的两张表的外键来实现 book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') info = models.CharField(max_length=32)
第三种:半自动建立
当一个项目中对数据库扩展性有要求时,虽然第二种的确增长了扩展性,可是没法充分利用ORM的多表查询。接下来讲的第三种方法结合了前两种的优势。建议使用该方式,扩展性高,若是使用第一种,后期须要在多对多关系的表增长字段时,须要改动的地方不少。
注意:使用第三种方式建立多对多关联关系时,就没法使用set、add、remove、clear方法来管理多对多的关系了,只能经过第三张表的model来管理多对多关系。当更改book对应的author信息时,能够先将book2author中该book对象对应的信息所有删除,而后再根据author信息从新建立数据,实际上ORM管理多对多表关系的set方法内部也是采用该方式。
class Book(models.Model): name = models.CharField(max_length=32) # 第三种建立表的方式 authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author')) class Author(models.Model): name = models.CharField(max_length=32) # 跟上面的ManyToManyField选一个写便可 book = models.ManyToManyField(to='Book',through='Book2Author',through_fields=('author','book')) class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') info = models.CharField(max_length=32)
小提示: