表和表之间的关系python
一对1、多对1、多对多 ,用book表和publish表本身来想一想关系,想一想里面的操做,加外键约束和不加外键约束的区别,一对一的外键约束是在一对多的约束上加上惟一约束。mysql
实例:咱们来假定下面这些概念,字段和关系git
做者模型:一个做者有姓名和年龄。sql
做者详细模型:把做者的详情放到详情表,包含生日,手机号,家庭住址等信息。做者详情模型和做者模型之间是一对一的关系(one-to-one)数据库
出版商模型:出版商有名称,所在城市以及email。django
书籍模型: 书籍有书名和出版日期,一本书可能会有多个做者,一个做者也能够写多本书,因此做者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,因此出版商和书籍是一对多关联关系(one-to-many)。api
模型创建以下:app
from django.db import models # Create your models here. class Author(models.Model): #比较经常使用的信息放到这个表里面 nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) age=models.IntegerField() # 与AuthorDetail创建一对一的关系,一对一的这个关系字段写在两个表的任意一个表里面均可以 authorDetail=models.OneToOneField(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE) #就是foreignkey+unique,只不过不须要咱们本身来写参数了,而且orm会自动帮你给这个字段名字拼上一个_id,数据库中字段名称为authorDetail_id。在django 1.xx的版本不须要加(to_field="nid",on_delete=models.CASCADE)默认是级联的 ,若是on_delete=models.SET_NULL默认为空。(to="AuthorDetail")要加字符串(引号用反射查找) class AuthorDetail(models.Model):#不经常使用的放到这个表里面 nid = models.AutoField(primary_key=True) birthday=models.DateField() # telephone=models.BigIntegerField() telephone=models.CharField() addr=models.CharField( max_length=64) class Publish(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) city=models.CharField( max_length=32) email=models.EmailField() #CharField--xx@xx.com #多对多的表关系,咱们学mysql的时候是怎么创建的,是否是手动建立一个第三张表,而后写上两个字段,每一个字段外键关联到另外两张多对多关系的表,orm的manytomany自动帮咱们建立第三张表,两种方式创建关系均可以,之后的学习咱们暂时用orm自动建立的第三张表,由于手动建立的第三张表咱们进行orm操做的时候,不少关于多对多关系的表之间的orm语句方法没法使用 #若是你想删除某张表,你只须要将这个表注销掉,而后执行那两个数据库同步指令就能够了,自动就删除了。 class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField( max_length=32) publishDate=models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2) # 与Publish创建一对多的关系,外键字段创建在多的一方,字段publish若是是外键字段,那么它自动是int类型 publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE) #foreignkey里面能够加不少的参数,都是须要我们学习的,慢慢来,to指向表,to_field指向你关联的字段,不写这个,默认会自动关联主键字段,on_delete级联删除 字段名称不须要写成publish_id,orm在翻译foreignkey的时候会自动给你这个字段拼上一个_id,这个字段名称在数据库里面就自动变成了publish_id # 与Author表创建多对多的关系,ManyToManyField能够建在两个模型中的任意一个,自动建立第三张表,而且注意一点,你查看book表的时候,你看不到这个字段,由于这个字段就是建立第三张表的意思,不是建立字段的意思,因此只能说这个book类里面有authors这个字段属性 authors=models.ManyToManyField(to='Author',) #注意不论是一对多仍是多对多,写to这个参数的时候,最后后面的值是个字符串,否则你就须要将你要关联的那个表放到这个表的上面
在django项目下的urlide
访问网页http://127.0.0.1:8000/admin/函数
建立用户
用户名:degang
Email:能够为空
Password:密码要求要有复杂度(要有数字,字母,很多于八位)输入两次密码
在django项目下把models导入到admin中,而后重启项目
AutoField # 主键 CharField # 字符串 TextField # 大字符串 IntegerField # 整形 DateTimeField DateField # 日期 日期时间 BooleanField # 布尔 DecimalField max_digits=5 decimal_places=2 999.99
null=True 数据库该字段能够为空 blank=True 校验时能够为空 default 默认值 unique 惟一索引 verbose_name 显示的名称 choices=((True, '男'), (False, '女')) 可选择的参数
class Meta: # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名 db_table = "person" # admin中显示的表名称 verbose_name = '我的信息' # verbose_name加s verbose_name_plural = '全部用户信息' # 联合索引 index_together = [ ("name", "age"), # 应为两个存在的字段 ] # 联合惟一索引 unique_together = (("name", "age"),) # 应为两个存在的字段
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") class Author(models.Model): name = models.CharField(max_length=32, verbose_name="做者姓名") # 本身建立第三张表,分别经过外键关联书和做者 class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") class Meta: unique_together = ("author", "book")
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") # 经过ORM自带的ManyToManyField自动建立第三张表 class Author(models.Model): name = models.CharField(max_length=32, verbose_name="做者姓名") books = models.ManyToManyField(to="Book", related_name="authors")
方式三:设置ManyTomanyField并指定自行建立的第三张表
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") # 本身建立第三张表,并经过ManyToManyField指定关联 class Author(models.Model): name = models.CharField(max_length=32, verbose_name="做者姓名") books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book")) # through_fields接受一个2元组('field1','field2'): # 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。 class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") class Meta: unique_together = ("author", "book")
注意:
当咱们须要在第三张关系表中存储额外的字段时,就要使用第三种方式。
可是当咱们使用第三种方式建立多对多关联关系时,就没法使用orm提供的set、add、remove、clear方法来管理多对多的关系了,须要经过第三张表的model来管理多对多关系。
to 设置要关联的表。 to_field 设置要关联的字段。 on_delete 同ForeignKey字段。
to 设置要关联的表 to_field 设置要关联的表的字段 related_name 反向操做时,使用的字段名,用于代替原反向查询时的'表名_set'。 related_query_name 反向查询操做时,使用的链接前缀,用于替换表名。 on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。
多对多的参数: to 设置要关联的表 related_name 同ForeignKey字段。 related_query_name 同ForeignKey字段。 through 在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。 但咱们也能够手动建立第三张表来管理多对多关系,此时就须要经过 through来指定第三张表的表名。 through_fields 设置关联的字段。 db_table 默认建立第三张表时,数据库中表的名称。
元信息 ORM对应的类里面包含另外一个Meta类,而Meta类封装了一些数据库的信息。主要字段以下: class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") class Meta: unique_together = ("author", "book") db_table ORM在数据库中的表名默认是 app_类名,能够经过db_table能够重写表名。db_table = 'book_model' index_together 联合索引。 unique_together 联合惟一索引。 ordering 指定默认按什么字段排序。 ordering = ['pub_date',] 只有设置了该属性,咱们查询到的结果才能够被reverse(),不然是能对排序了的结果进行反转(order_by()方法排序过的数据)
一对一
obj=models.AuthorDetail.objects.create( birthday='1979-12-12', telephone='13838338', addr='黑龙江' ) models.Author.objects.create( name='王涛', age=72, # authorDetail=obj, )
obj1=models.AuthorDetail.objects.filter(addr='山西').first() #取得对象 models.Author.objects.create( name='b哥', age=63, authorDetail_id=obj1.id )
一对多
new_pu=models.Publish.objects.get(id=1) obj=models.Book.objects.create( title='亚索的故事', publishDate='2013-09-12', price=11.1, publish=new_pu, ) print(obj)
new_pu=models.Publish.objects.get(id=2) models.Book.objects.create( title="箫曲", publishDate='2011-07-04', price=96, publish_id=new_pu.id )
多对多
book_obj=models.Book.objects.get(nid=1) print(book_obj) book_obj.authors.add(*[1,2])
author1 = models.Author.objects.get(id=1) author2 = models.Author.objects.get(id=3) book_obj = models.Book.objects.get(nid=2) book_obj.authors.add(*[author1,author2])
models.AuthorDetail.objects.get(id=6).delete()
models.Publish.objects.get(id=2).delete() models.Book.objects.get(pk=5).delete()
book_obj = models.Book.objects.get(nid=4) print(book_obj) book_obj.authors.remove(2) # book_obj.authors.remove(*[5,6]) # book_obj.authors.clear() # book_obj.authors.add(*[1,]) # book_obj.authors.set('1') # book_obj.authors.set(['5','6'])
URL下
url(r'(\w+)_del/(\d+)/$', views.delete, name='delete')
VIEWS下
def delete(request,table,pk): #table 表名 pk 删除的id print(table,pk) model_cls=getattr(models,table.capitalize(),) #capitalize 首字母大写 model_cls.objects.filter(pk=pk).delete() return redirect(table)
HTML下
<a class="btn btn-danger btn-sm" id="{{ i.nid }}" href="{% url 'delete' 'author' i.nid %}" >删除</a>
models.Author.objects.filter(id=3).update( name='壮壮', age=80, authorDetail_id=7, )
models.Book.objects.filter(pk=1).update( title='b哥的往事3', publishDate='2019-01-01', price='100', publish_id=1. )
book_obj.authors.remove() # 将某个特定的对象从被关联对象集合中去除。 ====== book_obj.authors.remove(*[1,2]),将多对多的关系数据删除 book_obj.authors.clear() #清空被关联对象集合 book_obj.authors.set() #先清空再设置 =====
关系属性(字段)写在哪一个类(表)里面,从当前类(表to)的数据去查询它关联类(表)的数据叫作正向查询,反之叫作反向查询
# 查询 # 一对一 # 正向查询 # 1 查询崔老师的电话号 author_obj = models.Author.objects.filter(name='崔老师').first() print(author_obj.authorDetail) #辽宁峨眉山 print(author_obj.authorDetail.telephone) #444 #2 反向查询 #2 查询一下这个444电话号是谁的. author_detail_obj = models.AuthorDetail.objects.get(telephone='444') print(author_detail_obj.author) #崔老师 print(author_detail_obj.author.name) #崔老师 ''' 正向查询:Authorobj.authorDetail,对象.关联属性名称 Author----------------------------------->AuthorDetail <----------------------------------- 反向查询:AuthorDetailobj.author ,对象.小写类名 ''' # 一对多 # 查询一下李帅的床头故事这本书的出版社是哪一个 # 正向查询 book_obj = models.Book.objects.get(title='李帅的床头故事') print(book_obj.publishs) # B哥出版社 print(book_obj.publishs.name) # B哥出版社 # B哥出版社出版社出版了哪些书 # 反向查询 pub_obj = models.Publish.objects.get(name='B哥出版社') print(pub_obj.book_set.all()) # <QuerySet [<Book: 李帅的床头故事>, <Book: 李帅的床头故事2>]> ''' 正向查询 book_obj.publishs 对象.属性 Book ---------------------------------------------> Publish <---------------------------------------------- 反向查询 publish_obj.book_set.all() 对象.表名小写_set ''' # 多对多 # 李帅的床头故事这本书是谁写的 # 正向查询 book_obj = models.Book.objects.get(title='李帅的床头故事') print(book_obj.authors.all()) # 高杰写了哪些书 author_obj = models.Author.objects.get(name='高杰') print(author_obj.book_set.all()) ''' 正向查询 book_obj.authors.all() 对象.属性 Book ---------------------------------------------> Author <---------------------------------------------- 反向查询 author_obj.book_set.all() 对象.表名小写_set '''
Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要作跨关系查询,就使用两个下划线来连接模型(model)间关联字段的名称,直到最终连接到你想要的model 为止。
''' 基于双下划线的查询就一句话:正向查询按字段,反向查询按表名小写用来告诉ORM引擎join哪张表,一对1、一对多、多对多都是一个写法,注意,咱们写orm查询的时候,哪一个表在前哪一个表在后都没问题,由于走的是join连表操做。 '''
# 查询yuan的手机号 # 正向查询 ret=Author.objects.filter(name="yuan").values("authordetail__telephone") # 反向查询 ret=AuthorDetail.objects.filter(author__name="yuan").values("telephone")
# 练习: 查询苹果出版社出版过的全部书籍的名字与价格(一对多) # 正向查询 按字段:publish queryResult=Book.objects .filter(publish__name="苹果出版社") #经过__告诉orm将book表和publish表进行join,而后找到全部记录中publish.name='苹果出版社'的记录(注意publish是属性名称),而后select book.title,book.price的字段值 .values_list("title","price") #values或者values_list # 反向查询 按表名:book queryResult=Publish.objects .filter(name="苹果出版社") .values_list("book__title","book__price")
# 练习: 查询yuan出过的全部书籍的名字(多对多) # 正向查询 按字段:authors: queryResult=Book.objects .filter(authors__name="yuan") .values_list("title") # 反向查询 按表名:book queryResult=Author.objects .filter(name="yuan") .values_list("book__title","book__price")
# 练习: 查询人民出版社出版过的全部书籍的名字以及做者的姓名 # 正向查询 queryResult=Book.objects .filter(publish__name="人民出版社") .values_list("title","authors__name") # 反向查询 queryResult=Publish.objects .filter(name="人民出版社") .values_list("book__title","book__authors__age","book__authors__name") # 练习: 手机号以151开头的做者出版过的全部书籍名称以及出版社名称 # 方式1: queryResult=Book.objects .filter(authors__authorDetail__telephone__regex="151") .values_list("title","publish__name") # 方式2: ret=Author.objects .filter(authordetail__telephone__startswith="151") .values("book__title","book__publish__name")
只能反向查找用
反向查询时,若是定义了related_name ,则用related_name替换 表名,例如:
publish = ForeignKey(Blog, related_name='bookList')
# 练习: 查询人民出版社出版过的全部书籍的名字与价格(一对多) # 反向查询 再也不按表名:book,而是related_name:bookList queryResult=Publish.objects .filter(name="人民出版社") .values_list("bookList__title","bookList__price")
aggregate(*args, **kwargs) # 计算全部图书的平均价格 >>> from django.db.models import Avg >>> Book.objects.all().aggregate(Avg('price')) #或者给它起名字:aggretate(a=Avg('price')) {'price__avg': 34.35} aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。若是你想要为聚合值指定一个名称,能够向聚合子句提供它。 >>> Book.objects.aggregate(average_price=Avg('price')) {'average_price': 34.35} 若是你但愿生成不止一个聚合,你能够向aggregate()子句中添加另外一个参数。因此,若是你也想知道全部图书价格的最大值和最小值,能够这样查询: >>> from django.db.models import Avg, Max, Min >>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) #count('id'),count(1)也能够统计个数,Book.objects.all().aggregete和Book.objects.aggregate(),均可以 {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
annotate() 为调用的QuerySet中每个对象都生成一个独立的统计值(统计方法用聚合函数)。 models.Publish.objects.annotate(a=Avg('book__price')).values('a') models.Book.objects.values('publish_id','id').annotate(a=Avg('price')) {'pulish_id':1,'a':11.11} 总结 :跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询,,既然是join连表,就可使用我们的双下划线进行连表了。
from django.db.models import F,Q F 针对本身单表中字段的比较和处理 # good(点赞)comment(评论) #比较 models.Book.objects.filter(good__gt=F('comment')*2) #修改 models.Book.objects.all().update(price=F('price')+1)
Q 对象可使用&(与) 、|(或)、~(非) 操做符组合起来。当一个操做符在两个Q 对象上使用时,它产生一个新的Q 对象。 bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon")) 你能够组合& 和| 操做符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可使用~ 操做符取反,这容许组合正常的查询和取反(NOT) 查询: bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title") bookList=Book.objects.filter(Q(Q(authors__name="yuan") & ~Q(publishDate__year=2017))&Q(id__gt=6)).values_list("title") #能够进行Q嵌套,多层Q嵌套等,其实工做中比较经常使用 查询函数能够混合使用Q 对象和关键字参数。全部提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一块儿。可是,若是出现Q 对象,它必须位于全部关键字参数的前面。例如: bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017), title__icontains="python" #也是and的关系,可是Q必须写在前面 )
在模型查询API不够用的状况下,咱们还可使用原始的SQL语句进行查询。
Django 提供两种方法使用原始SQL进行查询:一种是使用raw()方法,进行原始SQL查询并返回模型实例;另外一种是彻底避开模型层,直接执行自定义的SQL语句。
raw()管理器方法用于原始的SQL查询,并返回模型的实例:
注意:raw()语法查询必须包含主键。
这个方法执行原始的SQL查询,并返回一个django.db.models.query.RawQuerySet 实例。 这个RawQuerySet 实例能够像通常的QuerySet那样,经过迭代来提供对象实例。
举个例子:
class Person(models.Model): first_name = models.CharField(...) last_name = models.CharField(...) birth_date = models.DateField(...)
能够像下面这样执行原生SQL语句
>>> for p in Person.objects.raw('SELECT * FROM myapp_person'): ... print(p)
raw()查询能够查询其余表的数据。
举个例子:
ret = models.Student.objects.raw('select id, tname as hehe from app02_teacher') for i in ret: print(i.id, i.hehe)
raw()方法自动将查询字段映射到模型字段。还能够经过translations参数指定一个把查询的字段名和ORM对象实例的字段名互相对应的字典
d = {'tname': 'haha'} ret = models.Student.objects.raw('select * from app02_teacher', translations=d) for i in ret: print(i.id, i.sname, i.haha)
原生SQL还可使用参数,注意不要本身使用字符串格式化拼接SQL语句,防止SQL注入!
d = {'tname': 'haha'} ret = models.Student.objects.raw('select * from app02_teacher where id > %s', translations=d, params=[1,]) for i in ret: print(i.id, i.sname, i.haha)
有时候raw()方法并不十分好用,不少状况下咱们不须要将查询结果映射成模型,或者咱们须要执行DELETE、 INSERT以及UPDATE操做。在这些状况下,咱们能够直接访问数据库,彻底避开模型层。
咱们能够直接从django提供的接口中获取数据库链接,而后像使用pymysql模块同样操做数据库。
from django.db import connection, connections cursor = connection.cursor() # cursor = connections['default'].cursor() cursor.execute("""SELECT * from auth_user where id = %s""", [1]) ret = cursor.fetchone()