多表操作
表与表之间的关系
1、一对多(必须在“多”的表中创建关联字段,外加约束)
2、一对一(在任意表中加关联字段,外加约束)
3、多对多
详细信息在mysql数据库部分已经说过,这里不再细说
用ORM创建多张表及创建他们之间的联系
1、注意事项
- 表的名称myapp_modelName,是根据模型中的原数据自动生成的;id字段是自动添加的
- 对于外键字段,Django会使用"外键属性_id"来创建表中属性
- 这个例子中的CREATE TABLE SQL语句使用PostSQL语法格式,要注意的是Django中根据settings中指定的数据库来使用相应的SQL语句。
- 定义好models后,需要告诉Django使用这些模型(在配置文件(settings.py)的INSTALLED_APPS中添加models所在的应用名称)
- 外键字段有一个"null=True"的属性,用来表示外键可以接受空值
2、以一个实例来说明用ORM创建多张表及创建他们之间的联系的方法(具体的在代码注释中解释),下面是本实例创建数据表的代码:
#创建图书表book
class Books(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2)#最大位数为8,小数点占两位
publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)#创建关联外键,其中参数为关联到那张表,并且必须设置删除方式(级联/非空等),且会自动在publish(属性名)后加"_id",类型会自动设为int型
pub_date = models.DateField() #创建日期,格式须为"2012-12-12"
authors = models.ManyToManyField(to='Author') #自动创建第三张表,关联两张表(即自动创建了一个多对多的关系),创建的表名为app01_books_authors
#类中方法
def __str__(self):
return self.title
#创建出版社表
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
email = models.CharField(max_length=32)
#创建作者表
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.CharField(max_length=32)#此处用models.IntegerField()更好
email = models.CharField(max_length=32)
#ad = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)#创建一个关联字段(由于author表在此之前已经创好,表示创建一个关联字段,关联的表是“AuthorDetail”
#创建一张表,关联作者表和书籍表(关系类型为多对多)
class Author2Book(models.Model):
nid = models.AutoField(primary_key=True)
book = models.ForeignKey(to='Books',on_delete=models.CASCADE)#创建外键,外键是Books表的主键。删除方式为级联。
author = models.ForeignKey(to='Author',on_delete=models.CASCADE)
#一对一表(作者详情表)
class AuthorDetail(models.Model):
addr = models.CharField(max_length=46)
tel = models.IntegerField()
author = models.OneToOneField(to='Author',on_delete=models.CASCADE)#创建关联字段,这个是关联Author表,如果在Author表中已经创建语句,则此句可以省略。
跨表查询
1、正向查询与反向查询
从有关联字段的那个表查起,为正向查询,反之则为反向查询
2、跨表查询的要点:
正向查询按字段;反向查询按表名小写,下面以实例来解释这两句话
3、实例
>一对多查询
>#表一(查询一个结果)
book = Books.objects.filter().first()
#表二
publish = Publish.objects.filter(nid=book.publish_id).first()
#将以上两个组合成最终结果进行打印即可
>#直接用"."运算符取到相应的对象,然后在取到相应的值(查询一个结果)
book.publish.name(跨表查询)
book.publish.email
>查询某出版社的书籍(查询多个结果)(正向反向查询)
pub_obj = Publish.objects.filter().first()
pub_obj.book_set #表名_set:将所有结果放在一个集合内(反向查询使用)
正式语法:pub_obj.book_set.all()
>多对多查询
>正向查询按字段:book.authors.all()#authors字段
>反向查询按"表名小写_set":book_set.all()
>一对一查询
>正向查询按字段:book.字段
>反向查询按表名小写:author.book
>示例:
>
2、基于双下划线(基于join实现,其本质就是拼表):本实例在ORM2项目中的query2路径下
>要点(核心):正向查询按字段,反向查询按表名小写
>查询结果形式:以queryset数据类型(类列表),将每个结果组成一个字典,每个字典为列表的一个元素
>实例一:查询《花》这本书的出版社的名字和邮箱(多对一)
>对应的sql语句如下:select app01_publish.`name`,app01_publish.email from app01_books
inner join app01_publish on app01_books.publish_id=app01_publish.nid where app01_books.title="花"
>整个实例的函数如下:
def query2(request):
#####################################基于双下划线的跨表查询(基于join实现)################################
查询要点:正向查询按字段,反向查询按表名小写
查询《花》这本书的出版社的名字和邮箱(多对一):此处可以用Books表开头,也可以用Publish表开头
以Books为基表进行查询和连接组合
ret = Books.objects.filter(title='花').values('publish__name','publish__email')#这里是跨表查询,首先在values()方法中,告诉ORM要进行跨表(所跨表为publish,用__表示跨表)查询(正向),然后再接"__name"告诉ORM引擎要显示publish表下的name和email字段
以Publish为基表进行查询和连接组合,首先告诉ORM引擎,要用表名小写的方式,然后再用__字段名来指示用连接的表的那个字段
ret = Publish.objects.filter(books__title='花').values('name','email')#values()方法中的字段表示要显示那个字段
print(ret)
查询“人民出版社”出版的所有书籍的名称(一对多)
ret = Publish.objects.filter(name='人民出版社').values('books__title')
print(ret)
ret = Books.objects.filter(publish__name="人民出版社").values('title')
print(ret)
查询《计算机网络》这本书的作者的年龄(一对多)
ret = Books.objects.filter(title='计算机网络').values('authors__age')
print(ret)
ret = Author.objects.filter(books__title='计算机网络').values('age')
print(ret)
查询“老舍”编写的所有书籍的名称(多对多)
ret = Author.objects.filter(name='老舍').values('books__title')
print(ret)
ret = Books.objects.filter(authors__name='老舍').values('title')
print(ret)
查询“老舍”的手机号(一对一)
ret = Author.objects.filter(name='老舍').values('authordetail__tel')
print(ret)
ret = AuthorDetail.objects.filter(author__name="老舍").values('tel')
print(ret)
查询手机号为“668956”的作者名字
ret = Author.objects.filter(authordetail__tel=668956).values('name')
print(ret)
ret = AuthorDetail.objects.filter(tel=668956).values('author__name')
print(ret)
return HttpResponse('ok')
>延伸:连续跨表
Publish.objects.filter(name='人民出版社').values('books__title','books__authors__name')#books__authors__name为:先跨到books表,然后再跨到authors字段下的表,最后再取其中的属性name
>从Publish跨到books表,从Publish表跨到books表,然后再按字段跨到Author表,再取其中相应属性name
分组查询
分组查询函数为annotate(别名=具体的函数,如Avg,Count等)
;而且当分组复杂时,报alias错误,另外,别名也是必须的;其具体语法为:类名.objects.values(分组依据的字段名).annotate(别名=具体的函数,如Avg,Count等)
,这句话表示的是按照前面values()里面的字段进行分组。
此外,单表分组查询时分组的依据一定不能是主键,因为没有意义(跨表同样,不能用表的主键进行划分)。
1、分组查询的思路
- 先将涉及到的表组合成一张表,再用单表查询的方式进行查询
- 跨表的分组查询
实例:
# 查询每个出版社的名称和其出版的书籍的个数(示例)
Publish.objects.values('name').annotate(price_avg=Avg("books__price"))
- 需要注意的是,分组默认是采用主表的主键进行分组的。
#常用语句:
Publish.objects.all().annotate(price_avg=Avg('books__price'))#返回的是按publish的nid分类的三个对象,并且对每个对象下的books书的价格求了平均值
#其中最后会有四个属性,第四个属性为price_avg,后可接values()方法,可以控制最后要显示的结果
#由于默认是用主键进行分组,所以可以省略all()方法
2、实例:查询书籍表每一个出版社id以及对应的书籍个数
Books.objects.all().values('title')等价于Books.objects.values('title')
Books.objects.values('publish_id').annotate(Count(1))#按照前面values()里面的字段进行分组
'''
对应的SQL语句为:select Count(1) from books group by publish_id
'''
F与Q查询
1、F查询(用来比较两个字段的大小时用到)
#1、步骤:
>引入F函数:from django.db.model import F
>示例:查询评论数大于点赞数的书籍
Books.objects.filter(comment_count__gt=F("poll_count"))
#后面的F()函数还可以接运算式进行结果运算(如F()+3,即大于点赞数加3)
2、Q查询(用于或关系的查询)(且的关系在filter()方法中直接加入多个参数进行约束即可)
>步骤:
>引入Q函数:from django.db.model import Q
>示例:查询评论数大于3000或者售价大于100的书籍
Books.objects.filter(Q(comment_count__gt=3000)|Q(price__gt=100))
>其中将每个条件分别包在Q函数里,然后将两个Q函数用关系运算符相联,"|"表示或,“&”表示且,"~"表示非,如下:
Books.objects.filter(Q(comment_count__gt=3000)&Q(price__gt=100))
>这段代码表示查询评论数大于3000且售价大于100的书籍,其等价于:Books.objects.filter(comment_count__gt=3000,price__gt=100)
ORM聚合函数
ORM中,聚合函数都是写在aggregate()函数中的,具体的语法为aggregate(具体的聚合函数,如Avg等)
。
聚合的步骤为:
聚合的步骤:
>引入相应的聚合函数:from django.db.models import Avg,Max,Sum,Min,Count
>编写ORM语句,如下:
#查询所有书籍的平均价格(实例)
ret = Books.objects.all().aggregate(Avg('price'))#结果为{avg_price:值},其中参数也可以起别名,如(priceAvg=Avg('price')
return HttpResponse(ret)
静态文件的设置
在web项目中,会有很多的图片、视频、.css、.js等文件,我们把这些文件统称为静态文件,而Django不允许在页面中直接用本地(实际)路径去访问这些文件,而需要将其放在静态文件夹下,才可以。
具体的配置方法如下:
1、在Django项目文件夹下建立"static"包(即static文件夹)
2、建立"img"文件夹
3、建立"js"文件夹等各种文件夹
4、导入项目用到的静态文件
5、在"settings.py"文件夹中做如下设置:
- 添加:
STATIC_URL = ‘/static/’ #这个是给下面的STATICFILES_DIRS起别名,以后通过/static/就可以直接找到static包,而原本的路径是后面列表中的路径(以os开头的)
STATICFILES_DIRS = [
os.path.join(BASE_DIR,‘static’)#其中"static"参数为第一步建立的包的名称,两者须保持一致
]
以上这两句话为一套东西,其中STATIC_URL在"settings.py"文件的末尾