假定下面这些概念,字段和关系python
做者模型:一个做者有姓名和年龄。 做者详细模型:把做者的详情放到详情表,包含生日,手机号,家庭住址等信息。做者详情模型和做者模型之间是一对一的关系(one-to-one) 出版商模型:出版商有名称,所在城市以及email。 书籍模型: 书籍有书名和出版日期,一本书可能会有多个做者,一个做者也能够写多本书, 因此做者和书籍的关系就是多对多的关联关系(many-to-many); 一本书只应该由一个出版商出版,因此出版商和书籍是一对多关联关系(one-to-many)。
为了存储出版社的邮箱,地址,在 Book 表后面加字段mysql
这样会有大量重复的数据,浪费空间git
一对多:一个出版社对应多本书(关联信息建在多的一方,也就是 Book 表中)sql
一旦肯定表关系是一对多,在多对应的表中建立关联字段django
多对多:一本书有多个做者,一个做者出多本书app
一旦肯定表关系是多对多,建立第三张关系表(中间表,中间表就三个字段,本身的 id,书籍 id 和做者 id)函数
一对一:对做者详细信息的扩展3d
一旦肯定是一对一的关系,在两张表中的任意一张表中创建关联字段+Uniquecode
在 models 建立以下模型orm
from django.db import models # Create your models here. class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32, null=True) price = models.DecimalField(max_digits=5, decimal_places=2) # 一对多的关系一旦确立,关联关系写在多的一方 # on_delete:级联删除 publish = models.ForeignKey(to='Publish', to_field='nid', on_delete=models.CASCADE) # 多对多的关系须要建立第三张表(自动建立第三张表) authors = models.ManyToManyField(to='Author') def __str__(self): return self.title class Author(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) age = models.IntegerField() # unique=True 惟一性约束 author_detail = models.OneToOneField(to='AuthorDetail', to_field='nid', on_delete=models.CASCADE) # author_detail = models.ForeignKey(to='AuthorDatail',to_field='nid',unique=True,on_delete=models.CASCADE) class AuthorDetail(models.Model): nid = models.AutoField(primary_key=True) phone = models.CharField(max_length=16, unique=True) addr = models.CharField(max_length=16) class Publish(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) city = models.CharField(max_length=32) # EmailField :邮箱格式,EmailField用在admin中 email = models.EmailField()
生成的表以下:
# 方式一: publish_obj = models.Book.objects.get(nid=1) book_obj = models.Book.objects.create(name="图解HTTP", price="49", publish_date="2014-11-12", publish=publish_obj) # 方式二,推荐: book_obj = models.Book.objects.create(name="图解HTTP", price="49", publish_date="2014-11-12", publish_id=1)
# 当前生成的书籍对象 book_obj = models.Book.objects.create(name="图解HTTP", price="49", publish_date="2014-11-12", publish_id=1) # 为书籍绑定的做者对象 author_obj1 = models.Author.objects.filter(name="上野宣").first() author_obj2 = models.Author.objects.filter(name="于均良").first() # 绑定多对多关系,即向关系表book_authors中添加纪录 book_obj.authors.add(author_obj1, author_obj2) # 或者 book_obj.authors.add(1, 2)
# 方式二 # 当前生成的书籍对象 book_obj = models.Book.objects.create(name="C Primer Plus", price="60", publish_date="2005-2-12", publish_id=1) # 绑定多对多关系 book_obj.authors.add(*[3, 4])
# 解除绑定 book_obj = models.Book.objects.filter(nid=2).first() # 找到book的id为2的书 book_obj.authors.remove(2) # 解除做者id为2的绑定关系 book_obj.authors.clear() # 解除全部做者的绑定关系
# 解除再绑定:清空全部的绑定关系,再设置一个 book_obj.authors.clear() book_obj.authors.add(1) # 另外一种方法 book_obj.set(1) # 等同于上面的两步操做
正向查询按字段
# 查询上野宣做者的地址 shangyexuan = models.Author.objects.filter(name="上野宣").first() print(shangyexuan.author_detail.addr)
反向查询按表名小写
# 查询地址是日本的做者名字 address = models.AuthorDetail.objects.filter(addr="日本").first() # address.author是做者对象 print(address.author.name)
正向查询按字段:正向查询只会查出一个
# 查询《图解HTTP》这本书的出版社名字 book_obj = models.Book.objects.filter(title="图解HTTP").first() print(book_obj.publish.name)
反向查询按表名小写_set.all():返回结果是 queryset 对象
# 查询人民邮电出版社出版的全部书 publish_obj = models.Publish.objects.filter(name="人民邮电出版社").first() books = publish_obj.book_set.all() for book in books: print(book.title)
all() 拿出的是全部书的 queryset 对象,还能够进一步筛选
# 查询人民邮电出版社出版的书名以“IP”结尾的书 publish_obj = models.Publish.objects.filter(name="人民邮电出版社").first() books = publish_obj.book_set.all().filter(title__endswith="IP") for book in books: print(book.title)
正向查询按字段.all():正向查询必定会查出多个
# 查询《图解HTTP》这本书的全部做者 book_obj = models.Book.objects.filter(title="图解HTTP").first() authors = book_obj.authors.all() for author in authors: print(author.name)
反向查询按表名小写_set.all():返回结果是 queryset 对象
# 查询上野宣写的全部书 author_obj = models.Author.objects.filter(name="上野宣").first() books = author_obj.book_set.all() for book in books: print(book.title)
# 查询上野宣做者的地址 ret = models.Author.objects.filter(name="上野宣").values("author_detail__addr") print(ret)
# 查询地址是日本的做者名字 ret = models.AuthorDetail.objects.filter(addr="日本").values('author__name') print(ret)
# 查询《图解HTTP》这本书的出版社名字 ret = models.Book.objects.filter(title="图解HTTP").values('publish__name') print(ret)
# 查询人民邮电出版社出版的全部书的名字 ret = models.Publish.objects.filter(name="人民邮电出版社").values('book__title') print(ret)
# 查询《图解HTTP》这本书的全部做者 ret = models.Book.objects.filter(title="图解HTTP").values('authors__name') print(ret)
# 查询上野宣写的全部书 ret = models.Author.objects.filter(name="上野宣").values('book__title') print(ret)
# 查询人民出版社出版过的全部书籍的名字以及做者的姓名 # 方式一: ret = models.Book.objects.filter(publish__name="人民邮电出版社").values('title', 'authors__name') # 方式二 ret = ret = models.Publish.objects.filter(name="人民邮电出版社").values('book__title', 'book__authors__name')
无论跨了多少张表,它们的查询效率都是同样的。由于 ORM 中,不管是多少张表,都是先把这些表链接起来,再去进行查询。可使用 print(ret.query)
查看 SQL 语句:
补充:
publish = models.ForeignKey(to='Publish', to_field='nid', on_delete=models.CASCADE, related_name='bookList')
# 查询人民邮电出版社出版过的全部书籍的名字和价格(一对多) # 反向查询再也不按表名book,而是 related_name:bookList ret = Publish.objects.filter(name="人民邮电出版社").values('bookList__title', 'bookList__price')
aggregate(*args, **kwargs)
# 计算全部图书的平均价格 from django.db.models import Avg ret = models.Book.objects.all().aggregate(Avg('price')) print(ret) # {'price__avg': 59.0}
aggregate()
是 QuerySet
的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。若是想要为聚合值指定一个名称,能够向聚合子句提供它。
ret = models.Book.objects.aggregate(average_price=Avg('price')) print(ret) # {'average_price': 59.0}
能够向 aggregate()
子句中添加多个参数
from django.db.models import Avg, Max, Min ret = models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) # {'price__avg': 59.0, 'price__max': Decimal('89.00'), 'price__min': Decimal('29.00')}
annotate(*args, **kwargs)
# 统计每一本书做者个数 book_list = models.Book.objects.all().annotate(author_num=Count('authors')) for book in book_list: print(book.title, str(book.author_num)+'个做者') # 或者 book_list = models.Book.objects.annotate(author_num=Count("authors")).values('title', 'author_num') print(book_list)
# 统计每个出版社的最便宜的书 ret = models.Publish.objects.annotate(cheap_book=Min("book__price")).values('book__title', 'book__price') print(ret)
# 统计每一本以图解开头的书籍的做者个数 ret = models.Book.objects.filter(title__startswith="图解").annotate(author_num=Count("authors")).values('title', 'author_num') print(ret)
# 统计不止一个做者的图书 ret = models.Book.objects.annotate(author_num=Count("authors")).filter(author_num__gt=1).values('title', 'author_num') print(ret)
# 根据一本图书做者数量的多少对查询集 QuerySet进行排序 ret = models.Book.objects.annotate(author_num=Count("authors")).order_by('author_num').values('title', 'author_num') print(ret)
F()
的实例能够在查询中引用字段,来比较同一个 model 实例中两个不一样字段的值。Django 支持 F()
对象之间以及 F()
对象和常数之间的加减乘除和取模的操做。
首先将 Book 表中添加两条字段:
class Book(models.Model): # ... # 阅读数 read_num = models.IntegerField(default=0) # 评论数 commit_num = models.IntegerField(default=0) # ...
而后再进行相关查询:
from django.db.models import F, Q # 把《图解HTTP》这本书的评论数加1 ret = models.Book.objects.filter(title="图解HTTP").update(commit_num=F('commit_num')+1)
# 查询评论数大于阅读数的书籍 ret = models.Book.objects.filter(commit_num__gt=F('read_num')).values('title', 'commit_num', 'read_num') print(ret)
filter()
等方法中的关键字参数查询都是一块儿进行 AND
操做的。 若是须要执行更复杂的查询(例如 OR
语句),可使用 Q
查询。
# 查询书名为《图解HTTP》或价格为29的书籍 ret = models.Book.objects.filter(Q(title='图解HTTP') | Q(price=29)).values('title') print(ret)
Q
查询可使用 &
和 |
操做符组合起来。当一个操做符在两个 Q
对象上使用时,它产生一个新的 Q
对象。
上面的查询等同于 SQL 语句:
mysql> select app01_book.title from app01_book where(app01_book.title="图解HTTP" or app01_book.price=29); +-----------------+ | title | +-----------------+ | 图解HTTP | | 追风筝的人 | +-----------------+ 2 rows in set (0.00 sec)
Q
对象可使用 ~
操做符取反,查询函数也能够混合使用 Q
对象和关键字参数。全部提供给查询函数的参数(关键字参数或 Q
对象)都将 AND
在一块儿。可是,若是出现 Q
对象,它必须位于全部关键字参数的前面。例如:
# 查询名字不是追风筝的人,而且价格大于50的书 ret = models.Book.objects.filter(~Q(title='追风筝的人'), price__gt=50).values('title') print(ret)
在实际开发中,外键一般不用
db_constraint=False
, 这样写在 orm 建立表的时候,外键就没了创建外键约束,包括 unique,都是为了避免写脏数据