ORM
映射关系
表名 <-------> 类名
字段 <-------> 属性
表记录 <------->类实例对象
models.py
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
# 与AuthorDetail建立一对一的关系
authorDetail = models.OneToOneField(to="AuthorDetail")
class AuthorDetail(models.Model):
birthday = models.DateField()
telephone = models.BigIntegerField()
addr = models.CharField(max_length=64)
class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField()
class Book(models.Model):
title = models.CharField(max_length=32)
publishDate = models.DateField()
price = models.DecimalField(max_digits=5, decimal_places=2)
# 与Publish建立一对多的关系,外键字段建立在多的一方
publish = models.ForeignKey(to="Publish")
# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
authors = models.ManyToManyField(to='Author')
注意事项:
1、id字段是自动添加的
2、对于外键字段,Django会在字段名上添加‘_id’来创建数据库中的列名
3、null=True就是数据库相应字段中可为空,blank=True是指在django admin中可为空。
3、choices:由二元组组成的一个可迭代对象(如列表或元组),用来给字段提供选择项。如果设置了choices,默认表达将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices中的选项。
这是一个关于 choices 列表的例子:
YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
)
每个元组中的第一个元素,是存储在数据库中的值;第二个元素是在管理界面或 ModelChoiceField 中用作显示的内容。 在一个给定的 model 类的实例中,想得到某个 choices 字段的显示值,就调用 get_FOO_display 方法(这里的 FOO 就是 choices 字段的名称 )。例如:
from django.db import models
class Person(models.Model):
SHIRT_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
添加表记录
普通字段
方式一
publish_obj = Publish(name='Parker',city='北京',email='parker@live.com')
publish_obj.save()
方式二
models.Publish.objects.create(name='Alicce',city='上海',email='Alice@live.com')
外键字段
方式一、
publish_obj = models.Publish.objects.get(id=1)
models.Book.objects.create(title='The Great Expectations',publishDate='2001-12-10',price=32,publish=publish_obj)
方式二、
models.Book.objects.create(title='金瓶梅',publishDate='2014-12-13',price=22,publish_id=2)
多对多字段
book_obj = models.Book.objects.create(title='Jean of Arc',publishDate='2013-12-12',publish_id=1,price=22.13)
Jack = models.Author.objects.create(name='Jack',age=13,authorDetail_id=3)
Ben = models.Author.objects.create(name='Ben',age=13,authorDetail_id=4)
book_obj.authors.add(Jack,Ben)
解除关系
book_obj.authors.remove() # 将某个特定的对象从被关联对象集合中去除。 ====== book_obj.authors.remove(*[])
book_obj.authors.clear() #清空被关联对象集合。
查询表记录
1. all()
2. filter(**kwargs)
3. get(**kwargs)
4. exclude(**kwargs)
5. values(*fields) ##返回一个可迭代的字典序列
6. values_list(*fields) ##返回一个元组序列
7. order_by(*fields) ##对查询结果排序
8. reverse() ##对查询结果反向排序
9. distinct()
10. count()
11. first()
12. last()
13. exists()
#双下划线之单表查询
models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值
models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in
models.Tb1.objects.filter(name__contains="ven")
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and
startswith,istartswith, endswith, iendswith
基于对象的跨表查询
一对多查询
正向查询按字段(publish)
#查询id=1的书籍插板社所在城市
book_obj = models.Book.objects.filter(id=1).first()
print(book_obj.publish) #Publish object
print(book_obj.publish.name)
反向查询案表名+下划线set
# 查询Parker出版社出版的所有书籍
publishes = models.Publish.objects.filter(name='Parker').first() #拿到出版社对象
books = publishes.book_set.all() ##拿到book queryset
for book in books:
print(book.title)
一对一查询
正向查询(按字段 authorDetail)
# 查询Jack作者的手机号
author_egon=Author.objects.get(name="egon")
print(author_egon.authorDetail.telephone)
反向查询(按表名:author)
查询所有住址在Parker的作者姓名
authorDetails = models.AuthorDetail.objects.filter(addr='123123123')
for authorDetail in authorDetails:
print(authorDetail.author.name) #按author表名称
多对多查询(Author与Book)
正向查询(按字段:authors)
#查询金瓶梅的作者名称
book_obj = models.Book.objects.filter(title='Jean of Arc').first()
authors = book_obj.authors.all()
for author in authors:
print(author.name)
反向查询(按表明+下划线set)
查询Jack出版过的书籍名称
author = models.Author.objects.filter(name='Jack').first()
books = author.book_set.all()
for book in books:
print(book.title)
Reminder!
你可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 表名_set 的名称。
基于双下划线的跨表查询
Key:正向查询按字段,反向查询按表名。
# 练习1: 查询Parker出版社出版过的所有书籍的名字与价格(一对多)
# 正向查询 按字段:publish
queryResult=Book.objects
.filter(publish__name="Parker")
.values_list("title","price")
# 反向查询 按表名:book
queryResult=Publish.objects
.filter(name="Parker")
.values_list("book__title","book__price")
# 练习2: 查询Jack出过的所有书籍的名字(多对多)
# 正向查询 按字段:authors:
queryResult=Book.objects
.filter(authors__name="Jack")
.values_list("title")
# 反向查询 按表名:book
queryResult=Author.objects
.filter(name="Jack")
.values_list("book__title","book__price")
# 练习3: 查询Parker版社出版过的所有书籍的名字以及作者的姓名
# 正向查询
queryResult=Book.objects
.filter(publish__name="Parker")
.values_list("title","authors__name")
# 反向查询
queryResult=Publish.objects
.filter(name="Parker")
.values_list("book__title","book__authors__age","book__authors__name")
# 练习4: 手机号以151开头的作者出版过的所有书籍名称以及出版社名称
queryResult=Book.objects
.filter(authors__authorDetail__telephone__regex="151")
.values_list("title","publish__name")
注意:
反向查询时,如果定义了related_name ,则用related_name替换表名,例如: publish = ForeignKey(Blog, related_name='bookList'):
聚合查询&分组查询
聚合:aggregate(args,*kwargs)
# 计算所有图书的平均价格
from django.db.models import Avg
models.Book.objects.all().aggregate(Avg('price'))
以下是aggregate源码:
def aggregate(self, *args, **kwargs):
"""
Returns a dictionary containing the calculations (aggregation)
over the current queryset
If args is present the expression is passed as a kwarg using
the Aggregate object's default alias.
"""
if self.query.distinct_fields:
raise NotImplementedError("aggregate() + distinct(fields) not implemented.")
for arg in args:
# The default_alias property may raise a TypeError, so we use
# a try/except construct rather than hasattr in order to remain
# consistent between PY2 and PY3 (hasattr would swallow
# the TypeError on PY2).
try:
arg.default_alias
except (AttributeError, TypeError):
raise TypeError("Complex aggregates require an alias")
kwargs[arg.default_alias] = arg
query = self.query.clone()
for (alias, aggregate_expr) in kwargs.items():
query.add_annotation(aggregate_expr, alias, is_summary=True)
if not query.annotations[alias].contains_aggregate:
raise TypeError("%s is not an aggregate expression" % alias)
return query.get_aggregation(self.db, kwargs.keys())
aggregate()是queryset的一个终止子句,返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数名称自动生成的别名。此名称也可以人为指定。
models.Book.objects.aggregate(average_price=Avg('price'))
当然,也可以向aggregate()子句中添加另一个参数。例如:
from django.db.models import Avg, Max, Min
models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
分组annotate()
为调用的queryset中每一个对象都生成的独立的统计值(统计方法用聚合函数)。
books=Book.objects.annotate(authorsNum=Count('authors'))
for bookin books:
print(book_obj.title,book_obj.authorsNum)
以下是annotate源码
def annotate(self, *args, **kwargs):
"""
Return a query set in which the returned objects have been annotated
with extra data or aggregations.
"""
annotations = OrderedDict() # To preserve ordering of args
for arg in args:
# The default_alias property may raise a TypeError, so we use
# a try/except construct rather than hasattr in order to remain
# consistent between PY2 and PY3 (hasattr would swallow
# the TypeError on PY2).
try:
if arg.default_alias in kwargs:
raise ValueError("The named annotation '%s' conflicts with the "
"default name for another annotation."
% arg.default_alias)
except (AttributeError, TypeError):
raise TypeError("Complex annotations require an alias")
annotations[arg.default_alias] = arg
annotations.update(kwargs)
clone = self._clone()
names = self._fields
if names is None:
names = {f.name for f in self.model._meta.get_fields()}
for alias, annotation in annotations.items():
if alias in names:
raise ValueError("The annotation '%s' conflicts with a field on "
"the model." % alias)
clone.query.add_annotation(annotation, alias, is_summary=False)
for alias, annotation in clone.query.annotations.items():
if alias in annotations and annotation.contains_aggregate:
if clone._fields is None:
clone.query.group_by = True
else:
clone.query.set_group_by()
break
return clone
annotate()返回的是一个queryset集合
统计每一个出版社的最便宜的书
publishes=models.Publish.objects.annotate(MinPrice=Min('book__price'))
for book in publishes:
print(book.name,book.MinPrice)
也可以用values_list:
法一:
queryResult= Publish.objects
.annotate(MinPrice=Min("book__price"))
.values_list("name","MinPrice")
print(queryResult)
法二:
queryResult=Book.objects.values("publish__name").annotate(MinPrice=Min('price')) # 思考: if 有一个出版社没有出版过书会怎样?
按author表的所有字段 group by
queryResult=Author.objects.annotate(SumPrice=Sum("book__price")).values_list("name","SumPrice")
print(queryResult)
按authors__name group by
print(queryResult2)
Keep in mind:
基于双下划线跨表查询,正向按字段,反向按表名。
Django ORM中批量操作
models.py
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=10, decimal_places=2)
批量插入数据
批量插入数据的时候,首先要创建一个对象的列表,然后调用bulk_create()方法,一次将列表中的数据插入到数据库中。
incoming_product_list_waiting_to_insert = list()
for x in range(10):
incoming_product_list_waiting_to_insert.append(Product(name='product name ' + str(x), price=x))
Product.objects.bulk_create(product_list_to_insert)
批量更新数据
批量更新数据时,先进行数据过滤,然后再调用update方法进行一次性更新。类似于update...from...的SQL语句。
Product.objects.filter(name__contains='name').update(name='new name')
批量删除数据
批量删除数据时,与update类似,先进行数据过滤,然后再调用delete方法进行一次性进行删除。下面的语句生成类似delete...from...的SQL语句。
Product.objects.filter(name__contains='name query').delete()
网友评论