Django模型层
今天来聊聊Django的模型层,Django 提供了一个抽象的模型(“models”)层,为了构建和操纵 Web 应用的数据。
一、模型概念:
1.模型介绍
模型可以准确且唯一的描述了数据。包含储存的数据的重要字段和行为。
一般来说,每一个模型都映射一张数据库表。
下面我们定义一个模型:
from django.db import models
class News(models.Model):
news_title = models.CharField(max_length=20)
news_details = models.CharField(max_length=40)
分析:
通过上面定义的模型可以发现,一个模型就是一个 Python 的类,这些类继承 django.db.models.Model
模型类的每个属性都相当于一个数据库的字段。
当需要调用数据时,可以通过Django 提供的API函数去获取,而且这是一个自动生成访问数据库的 API;
news_title,news_details为数据库的两个字段,每个字段都被指定为一个类属性,并且每个属性映射为一个数据库列
如果我们用建表sql语句表示,就如下面:
CREATE TABLE myapp_news (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);
上面的数据表表的名称 myapp_news 是自动从某些模型元数据中派生出来,你可以被改写。
下面来说说这个表名称:
在Django中,为了节省你的时间,Django 会自动从你的模型类和包含它的应用程序的名称中导出数据库表的名称。一个模型的数据库表名是通过将模型的“app label”(你在 manage.py startapp 中使用的名称)与模型的类名连接起来,并在两者之间加上下划线。
例如:
如果你有一个应用程序 news (由 manage.py startapp mynews 创建),一个定义为 class News 的模型将有一个名为 mynews_news 的数据库表。
要覆盖数据库表名,使用 class Meta 中的 db_table 参数。
如果你的数据库表名是 SQL 的保留字,或者包含 Python 变量名中不允许的字符(特别是连字符)那也没关系。Django 会在幕后引用列名和表名。
在 MariaDB 和 MySQL 中使用小写的表名
当你通过 db_table 覆盖表名时,强烈建议你使用小写的表名,特别是当你使用 MySQL 后端时。
Oracle 的表名引用
为了满足 Oracle 对表名的 30 个字符的限制,并符合 Oracle 数据库的惯例,Django 可能会缩短表名,并将其全部变成大写。为了防止这样的转变,使用带引号的名称作为 db_table 的值:
db_table = '"name_left_in_lowercase"'
在自动建表过程中,一个 id 字段会被自动添加,但是这种行为可以被改写(如果你想自己指定主键, 在你想要设置为主键的字段上设置参数
primary_key=True
。)。
本例子中 创建数据表 的语法是 PostgreSQL 格式的。值得注意的是,Django 依据你在 配置文件 中指定的数据库后端生成对应的 SQL 语句。
2.字段类型
字段选项
以下参数对所有字段类型均有效,且是可选的。
null
Field.null
如果是 null=True, Django 将在数据库中存储空值为 NULL。默认为 False。
避免在基于字符串的字段上使用 null,如 CharField 和 TextField。如果一个基于字符串的字段有 null=True,这意味着它有两种可能的“无数据”值。NULL,和空字符串。在大多数情况下,“无数据”有两种可能的值是多余的,Django 的惯例是使用空字符串,而不是 NULL。
一个例外是当一个 CharField 同时设置了 unique=True 和 blank=True。在这种情况下,null=True 是需要的,以避免在保存具有空白值的多个对象时违反唯一约束。
无论是基于字符串的字段还是非字符串的字段,如果希望在表单中允许空值,还需要设置 blank=True,因为 null 参数只影响数据库的存储。
注解
当使用 Oracle 数据库后端时,不管这个属性是什么,都会存储 NULL 值来表示空字符串。
blank
Field.blank
如果是 True ,该字段允许为空。默认为 False 。
注意,这与 null 不同。 null 纯属数据库相关,而 blank 则与验证相关。如果一个字段有 blank=True,表单验证将允许输入一个空值。如果一个字段有 blank=False,则该字段为必填字段。
choices
Field.choices
一个 sequence 本身由正好两个项目的迭代项组成(例如 [(A,B),(A,B)…] ),作为该字段的选择。如果给定了选择,它们会被 模型验证 强制执行,默认的表单部件将是一个带有这些选择的选择框,而不是标准的文本字段。
每个元组中的第一个元素是要在模型上设置的实际值,第二个元素是人可读的名称。例如:
YEAR_IN_SCHOOL_CHOICES = [
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
]
最好在模型类内部定义选择,并为每个值定义一个合适的名称的常量:
from django.db import models
class Student(models.Model):
FRESHMAN = 'FR'
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'
GRADUATE = 'GR'
YEAR_IN_SCHOOL_CHOICES = [
(FRESHMAN, 'Freshman'),
(SOPHOMORE, 'Sophomore'),
(JUNIOR, 'Junior'),
(SENIOR, 'Senior'),
(GRADUATE, 'Graduate'),
]
year_in_school = models.CharField(
max_length=2,
choices=YEAR_IN_SCHOOL_CHOICES,
default=FRESHMAN,
)
def is_upperclass(self):
return self.year_in_school in {self.JUNIOR, self.SENIOR}
还可以将你的可用选择收集到可用于组织目的的命名组中:
MEDIA_CHOICES = [
('Audio', (
('vinyl', 'Vinyl'),
('cd', 'CD'),
)
),
('Video', (
('vhs', 'VHS Tape'),
('dvd', 'DVD'),
)
),
('unknown', 'Unknown'),
]
每个元组中的第一个元素是应用于该组的名称。第二个元素是一个二元元组的迭代,每个二元元组包含一个值和一个可读的选项名称。分组后的选项可与未分组的选项结合在一个单一的列表中(如本例中的 ‘unknown’ 选项)。
枚举类型
from django.utils.translation import gettext_lazy as _
class Student(models.Model):
class YearInSchool(models.TextChoices):
FRESHMAN = 'FR', _('Freshman')
SOPHOMORE = 'SO', _('Sophomore')
JUNIOR = 'JR', _('Junior')
SENIOR = 'SR', _('Senior')
GRADUATE = 'GR', _('Graduate')
year_in_school = models.CharField(
max_length=2,
choices=YearInSchool.choices,
default=YearInSchool.FRESHMAN,
)
def is_upperclass(self):
return self.year_in_school in {
self.YearInSchool.JUNIOR,
self.YearInSchool.SENIOR,
}
可以通过将其子类化来简洁地定义选择
db_column
Field.db_column
这个字段要使用的数据库列名。如果没有给出列名,Django 将使用字段名。
如果你的数据库列名是 SQL 的保留字,或者包含了 Python 变量名中不允许的字符——特别是连字符——那也没关系。Django 会在幕后引用列名和表名。
db_index
Field.db_index
如果是 True,将为该字段创建数据库索引。
db_tablespace
Field.db_tablespace
如果这个字段有索引,那么要为这个字段的索引使用的 数据库表空间 的名称。默认是项目的 DEFAULT_INDEX_TABLESPACE 设置(如果有设置),或者是模型的 db_tablespace (如果有)。如果后端不支持索引的表空间,则忽略此选项。
default
Field.default
该字段的默认值。可以是一个值或者是个可调用的对象,如果是个可调用对象,每次实例化模型时都会调用该对象。
例如,如果你想为 JSONField 指定一个默认的 dict,使用一个函数:
def contact_default():
return {"email": "to1@example.com"}
contact_info = JSONField("ContactInfo", default=contact_default)
editable
Field.editable
如果是 False,该字段将不会在管理或任何其他 ModelForm 中显示。在 模型验证 中也会跳过。默认为 True。
error_messages
Field.error_messages
error_messages 参数可以让你覆盖该字段引发的默认消息。传入一个与你想覆盖的错误信息相匹配的键值的字典。
关系字段
Django 还定义了一组表示关系的字段。
ForeignKey
class ForeignKey(to, on_delete, **options)
一个多对一的关系。需要两个位置参数:模型相关的类和 on_delete 选项。
要创建一个递归关系(一个与自己有多对一关系的对象)使用 models.ForeignKey(‘self’, on_delete=models.CASCADE)。
如果你需要在一个尚未定义的模型上创建关系,你可以使用模型的名称,而不是模型对象本身:
from django.db import models
class Car(models.Model):
manufacturer = models.ForeignKey(
'Manufacturer',
on_delete=models.CASCADE,
)
# ...
class Manufacturer(models.Model):
# ...
pass
3.索引
Index 选项
class Index(*expressions, fields=(), name=None, db_tablespace=None, opclasses=(), condition=None, include=None)
在数据库中创建一个索引(B 树)。
expressions
Index.expressions
比如:
Index(Lower('title').desc(), 'pub_date', name='lower_title_date_idx')
fields
Index.fields
需要索引字段的名称列表或元组。
默认情况下,索引是以每列的升序创建的。要为列定义一个降序索引,请在字段名前添加一个连字符。
例如 Index(fields=[‘headline’, ‘-pub_date’]) 将创建 SQL 为 (headline, pub_date DESC)。MySQL 上不支持索引排序。在这种情况下,降序索引会像普通索引一样被创建。
name
Index.name
索引的名称。如果没有提供 name,Django 会自动生成一个名称。为了兼容不同的数据库,索引名不能超过 30 个字符,并且不应该以数字(0-9)或下划线(_)开头。
db_tablespace
Index.db_tablespace
该索引要使用的 数据库表空间 名称。对于单字段索引,如果没有提供 db_tablespace,则在字段的 db_tablespace 中创建索引。
如果没有指定 Field.db_tablespace (或者如果索引使用了多个字段),则在模型的 class Meta 里面的 db_tablespace 选项中指定的表空间创建索引。如果这两个表空间都没有设置,则在与表相同的表空间中创建索引。
opclasses
Index.opclasses
要为这个索引使用的 PostgreSQL 运算符类 名称。如果你需要一个自定义的操作类,你必须为索引中的每个字段提供一个操作类。
例如,GinIndex(name=‘json_index’, fields=[‘jsonfield’], opclasses=[‘jsonb_path_ops’]) 使用 jsonb_path_ops 在 jsonfield 上创建一个 gin 索引。
4.Meta 选项
abstract
Options.abstract
如果 abstract = True,这个模型将是一个 抽象基类。
app_label
Options.app_label
如果在 INSTALLED_APPS 中定义了一个应用程序之外的模型,它必须声明它属于哪个应用程序:
app_label = ‘myapp’
如果你想用 app_label.object_name 或 app_label.model_name 来表示一个模型,你可以分别使用 model._meta.label 或 model._meta.label_lower。
base_manager_name
Options.base_manager_name
管理器的属性名,例如,‘objects’,用于模型的 _base_manager。
db_table
Options.db_table
用于模型的数据库表的名称:
db_table = ‘music_album’
5.Model 类
DoesNotExist
exception Model.DoesNotExist
当没有找到预期的对象时,ORM 会引发这个异常。例如, QuerySet.get() 将在没有找到给定查找对象时引发该异常。
MultipleObjectsReturned
exception Model.MultipleObjectsReturned
当给定的查找找到多个对象时, QuerySet.get() 会引发这个异常
objects
Model.objects
每个非抽象的 Model 类必须有一个 Manager 实例添加到其中。Django 确保在你的模型类中至少指定了一个默认的 Manager。如果你没有添加自己的 Manager`,Django 会添加一个属性 objects,包含默认的 Manager 实例。如果添加自己的 Manager 实例属性,则不会出现默认的。
例子:
from django.db import models
class Person(models.Model):
# Add manager with another name
people = models.Manager()
二、QuerySet:
1.执行查询
一旦创建数据模型后,Django 自动给予你一套数据库抽象 API,允许你创建,检索,更新和删除对象。
我们将引用以下模型,这些模型包含了一个 Webblog 应用:
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()
def __str__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
number_of_comments = models.IntegerField()
number_of_pingbacks = models.IntegerField()
rating = models.IntegerField()
def __str__(self):
return self.headline
创建对象
>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()
Django 在你显式调用 save() 才操作数据库。
save() 方法没有返回值。
将修改保存至对象
要将修改保存至数据库中已有的某个对象,使用 save()。
有一个已被存入数据库中的 Blog 实例 b5,本例将其改名,并在数据库中更新其记录:
>>> b5.name = 'New name'
>>> b5.save()
2.QuerySet 方法参考
什么时候 QuerySet 被执行
QuerySet 本身可以被构造,过滤,切片,或者复制赋值等,是无需访问数据库的。只有在你需要从数据库取出数据或者,向数据库存入数据时才需要访问数据库。
比如:
for e in Entry.objects.all():
print(e.headline)
3.查询表达式
查找 API 有两个组成部分:一个是 RegisterLookupMixin 类,用于注册查找;另一个是 查询表达式 API,一个类要想注册为查找,必须实现一组方法。
Django 有两个遵循查询表达式 API 的基类,所有 Django 内置的查找都是从这里派生出来的。
Lookup:查找一个字段(例如 field_name__exact 的 exact)
Transform:转换一个字段
一个查找表达式由三部分组成:
字段部分(如 Book.objects.filter(author__best_friends__first_name…);
转换部分(可省略)(如 __lower__first3chars__reversed);
查找(例如 __icontains),如果省略,默认为 __exact。
还有是注册 API和查询表达式 API等。
三、模型实例:
1.实例方法
比如你要实例一个方法可以这样:
class Model(**kwargs)
2.访问关联的对象
可以通过class RelatedManager来访问关联对象:
ForeignKey 关系的“另一边”。即:
from django.db import models
class Blog(models.Model):
# ...
pass
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE, null=True)
ManyToManyField 关系的两边:
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
toppings = models.ManyToManyField(Topping)
四、迁移:
1.迁移概述
迁移是 Django 将你对模型的修改(例如增加一个字段,删除一个模型)应用至数据库架构中的方式。它们被设计的尽可能自动化,但你仍需要知道何时构建和运行迁移,你还需要了解一些常见问题。
主要的命令有:
migrate,负责应用和撤销迁移。
makemigrations,基于模型的修改创建迁移。
sqlmigrate,展示迁移使用的 SQL 语句。
showmigrations,列出项目的迁移和迁移的状态。
2.操作参考
CreateModel
class CreateModel(name, fields, options=None, bases=None, managers=None)
在项目历史中创建一个新的模型,并在数据库中创建与之匹配的相应表。
DeleteModel
class DeleteModel(name)
从项目历史中删除模型,并从数据库中删除它的表。
RenameModel
class RenameModel(old_name, new_name)
将模型从旧名称改名为新名称。
3.SchemaEditor
Django 中的每个数据库后端都提供了自己的 SchemaEditor 版本,并且总是通过 connection.schema_editor() 上下文管理器:
with connection.schema_editor() as schema_editor:
schema_editor.delete_model(MyModel)
4.编写迁移
比如我们可以用下面方法为特定数据库运行迁移
from django.db import migrations
def forwards(apps, schema_editor):
if schema_editor.connection.alias != 'default':
return
# Your migration code goes here
class Migration(migrations.Migration):
dependencies = [
# Dependencies to other migrations
]
operations = [
migrations.RunPython(forwards),
]
五、高级:
1.管理器
class Manager
Manager 是一种接口,它赋予了 Django 模型操作数据库的能力。Django 应用中每个模型拥有至少一个 Manager。
2.原始 SQL
为了说明什么是原始sql,最好用例子来解释。假设你有以下模型:
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)
John Smith
Jane Jones
3.事务
Django 默认的事务行为
Django 默认的事务行为是自动提交。除非事务正在执行,每个查询将会马上自动提交到数据库, 详见下文。
Django 自动使用事务或还原点,以确保需多次查询的 ORM 操作的一致性,特别是 delete() 和 update() 操作。
由于性能原因,Django 的 TestCase 类同样将每个测试用事务封装起来。
4.聚合
比如有以下模型
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
class Publisher(models.Model):
name = models.CharField(max_length=300)
class Book(models.Model):
name = models.CharField(max_length=300)
pages = models.IntegerField()
price = models.DecimalField(max_digits=10, decimal_places=2)
rating = models.FloatField()
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
pubdate = models.DateField()
class Store(models.Model):
name = models.CharField(max_length=300)
books = models.ManyToManyField(Book)
可通过速查表进行集合查询:
# Total number of books.
>>> Book.objects.count()
2452
# Total number of books with publisher=BaloneyPress
>>> Book.objects.filter(publisher__name='BaloneyPress').count()
73
# Average price across all books.
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}
# Max price across all books.
>>> from django.db.models import Max
>>> Book.objects.all().aggregate(Max('price'))
{'price__max': Decimal('81.20')}
# Difference between the highest priced book and the average price of all books.
>>> from django.db.models import FloatField
>>> Book.objects.aggregate(
... price_diff=Max('price', output_field=FloatField()) - Avg('price'))
{'price_diff': 46.85}
# All the following queries involve traversing the Book<->Publisher
# foreign key relationship backwards.
# Each publisher, each with a count of books as a "num_books" attribute.
>>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count('book'))
>>> pubs
<QuerySet [<Publisher: BaloneyPress>, <Publisher: SalamiPress>, ...]>
>>> pubs[0].num_books
73
# Each publisher, with a separate count of books with a rating above and below 5
>>> from django.db.models import Q
>>> above_5 = Count('book', filter=Q(book__rating__gt=5))
>>> below_5 = Count('book', filter=Q(book__rating__lte=5))
>>> pubs = Publisher.objects.annotate(below_5=below_5).annotate(above_5=above_5)
>>> pubs[0].above_5
23
>>> pubs[0].below_5
12
# The top 5 publishers, in order by number of books.
>>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5]
>>> pubs[0].num_books
1323
5.搜索
可通过标准文本查询方式:
>>> Author.objects.filter(name__contains='Terry')
[<Author: Terry Gilliam>, <Author: Terry Jones>]
6.自定义字段
封装一个 Python 对象,代表手上 桥牌 的细节。不要担心,你不需要知道如何玩桥牌就能学习此例子。你只需知道 52 张牌被均分给 4 个玩家,一般称他们 北,东,南 和 西。我们的类长这样:
class Hand:
"""A hand of cards (bridge style)"""
def __init__(self, north, east, south, west):
# Input parameters are lists of cards ('Ah', '9s', etc.)
self.north = north
self.east = east
self.south = south
self.west = west
# ... (other possibly useful methods omitted) ...
7.多个数据库
在项目中可定义了2个数据库——默认的 PostgreSQL 数据库和名叫 users 的 MySQL 数据库。
DATABASES = {
'default': {
'NAME': 'app_data',
'ENGINE': 'django.db.backends.postgresql',
'USER': 'postgres_user',
'PASSWORD': 's3krit'
},
'users': {
'NAME': 'user_data',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'priv4te'
}
}
8.自定义查询
Django 提供了各种各样的 内置查询器 (例如, exact 和 icontains )。
class NotEqual(Lookup):
lookup_name = 'ne'
def as_sql(self, compiler, connection):
lhs, lhs_params = self.process_lhs(compiler, connection)
rhs, rhs_params = self.process_rhs(compiler, connection)
params = lhs_params + rhs_params
return '%s <> %s' % (lhs, rhs), params
9.查询表达式
Django 支持负、加、减、乘、除、模数运算,以及对查询表达式的幂运算符,使用 Python 常量、变量,甚至其他表达式。
from django.db.models import Count, F, Value
from django.db.models.functions import Length, Upper
# Find companies that have more employees than chairs.
Company.objects.filter(num_employees__gt=F('num_chairs'))
# Find companies that have at least twice as many employees
# as chairs. Both the querysets below are equivalent.
Company.objects.filter(num_employees__gt=F('num_chairs') * 2)
Company.objects.filter(
num_employees__gt=F('num_chairs') + F('num_chairs'))
# How many chairs are needed for each company to seat all employees?
>>> company = Company.objects.filter(
... num_employees__gt=F('num_chairs')).annotate(
... chairs_needed=F('num_employees') - F('num_chairs')).first()
>>> company.num_employees
120
>>> company.num_chairs
50
>>> company.chairs_needed
70
# Create a new company using expressions.
>>> company = Company.objects.create(name='Google', ticker=Upper(Value('goog')))
# Be sure to refresh it if you need to access the field.
>>> company.refresh_from_db()
>>> company.ticker
'GOOG'
# Annotate models with an aggregated value. Both forms
# below are equivalent.
Company.objects.annotate(num_products=Count('products'))
Company.objects.annotate(num_products=Count(F('products')))
# Aggregates can contain complex computations also
Company.objects.annotate(num_offerings=Count(F('products') + F('services')))
# Expressions can also be used in order_by(), either directly
Company.objects.order_by(Length('name').asc())
Company.objects.order_by(Length('name').desc())
# or using the double underscore lookup syntax.
from django.db.models import CharField
from django.db.models.functions import Length
CharField.register_lookup(Length)
Company.objects.order_by('name__length')
# Boolean expression can be used directly in filters.
from django.db.models import Exists
Company.objects.filter(
Exists(Employee.objects.filter(company=OuterRef('pk'), salary__gt=10))
)
10条件表达式
为了说明条件表达式,我们先建一个模型:
from django.db import models
class Client(models.Model):
REGULAR = 'R'
GOLD = 'G'
PLATINUM = 'P'
ACCOUNT_TYPE_CHOICES = [
(REGULAR, 'Regular'),
(GOLD, 'Gold'),
(PLATINUM, 'Platinum'),
]
name = models.CharField(max_length=50)
registered_on = models.DateField()
account_type = models.CharField(
max_length=1,
choices=ACCOUNT_TYPE_CHOICES,
default=REGULAR,
)
when
>>> from django.db.models import F, Q, When
>>> # String arguments refer to fields; the following two examples are equivalent:
>>> When(account_type=Client.GOLD, then='name')
>>> When(account_type=Client.GOLD, then=F('name'))
>>> # You can use field lookups in the condition
>>> from datetime import date
>>> When(registered_on__gt=date(2014, 1, 1),
... registered_on__lt=date(2015, 1, 1),
... then='account_type')
>>> # Complex conditions can be created using Q objects
>>> When(Q(name__startswith="John") | Q(name__startswith="Paul"),
... then='name')
>>> # Condition can be created using boolean expressions.
>>> from django.db.models import Exists, OuterRef
>>> non_unique_account_type = Client.objects.filter(
... account_type=OuterRef('account_type'),
... ).exclude(pk=OuterRef('pk')).values('pk')
>>> When(Exists(non_unique_account_type), then=Value('non unique'))
case
>>> from datetime import date, timedelta
>>> from django.db.models import Case, Value, When
>>> Client.objects.create(
... name='Jane Doe',
... account_type=Client.REGULAR,
... registered_on=date.today() - timedelta(days=36))
>>> Client.objects.create(
... name='James Smith',
... account_type=Client.GOLD,
... registered_on=date.today() - timedelta(days=5))
>>> Client.objects.create(
... name='Jack Black',
... account_type=Client.PLATINUM,
... registered_on=date.today() - timedelta(days=10 * 365))
>>> # Get the discount for each Client based on the account type
>>> Client.objects.annotate(
... discount=Case(
... When(account_type=Client.GOLD, then=Value('5%')),
... When(account_type=Client.PLATINUM, then=Value('10%')),
... default=Value('0%'),
... ),
... ).values_list('name', 'discount')
<QuerySet [('Jane Doe', '0%'), ('James Smith', '5%'), ('Jack Black', '10%')]>
比如条件更新:
>>> a_month_ago = date.today() - timedelta(days=30)
>>> a_year_ago = date.today() - timedelta(days=365)
>>> # Update the account_type for each Client from the registration date
>>> Client.objects.update(
... account_type=Case(
... When(registered_on__lte=a_year_ago,
... then=Value(Client.PLATINUM)),
... When(registered_on__lte=a_month_ago,
... then=Value(Client.GOLD)),
... default=Value(Client.REGULAR)
... ),
... )
>>> Client.objects.values_list('name', 'account_type')
<QuerySet [('Jane Doe', 'G'), ('James Smith', 'R'), ('Jack Black', 'P')]>
条件聚合
>>> # Create some more Clients first so we can have something to count
>>> Client.objects.create(
... name='Jean Grey',
... account_type=Client.REGULAR,
... registered_on=date.today())
>>> Client.objects.create(
... name='James Bond',
... account_type=Client.PLATINUM,
... registered_on=date.today())
>>> Client.objects.create(
... name='Jane Porter',
... account_type=Client.PLATINUM,
... registered_on=date.today())
>>> # Get counts for each value of account_type
>>> from django.db.models import Count
>>> Client.objects.aggregate(
... regular=Count('pk', filter=Q(account_type=Client.REGULAR)),
... gold=Count('pk', filter=Q(account_type=Client.GOLD)),
... platinum=Count('pk', filter=Q(account_type=Client.PLATINUM)),
... )
{'regular': 2, 'gold': 1, 'platinum': 3}
11.数据库函数
比较和转换函数:
Cast,Coalesce,Collate,Greatest
比如下面代码:
>>> from django.db.models import FloatField
>>> from django.db.models.functions import Cast
>>> Author.objects.create(age=25, name='Margaret Smith')
>>> author = Author.objects.annotate(
... age_as_float=Cast('age', output_field=FloatField()),
... ).get()
>>> print(author.age_as_float)
25.0
六、其它:
1.支持的数据库
Django 官方支持以下数据库:
PostgreSQL
MariaDB
MySQL
Oracle
SQLite
2.旧数据库
虽然 Django 很适合开发新应用,但也能用它集成旧数据库。
3.提供初始化数据
如果你想初始化数据:
[
{
"model": "myapp.person",
"pk": 1,
"fields": {
"first_name": "John",
"last_name": "Lennon"
}
},
{
"model": "myapp.person",
"pk": 2,
"fields": {
"first_name": "Paul",
"last_name": "McCartney"
}
}
]
4.优化数据库访问
优化数据库访问有如下步骤:
1.首先性能分析
2.使用标准数据库优化技巧
3.理解 QuerySet
4.理解 QuerySet 的执行过程
5.理解缓存属性
6.使用 with 模板标签
7.使用 iterator()
8.使用 explain()
9.使用 RawSQL
10.使用原生 SQL
11.使用唯一索引列来检索单个对象
12.使用 QuerySet.select_related() 和 prefetch_related()等
5.PostgreSQL 的特有功能
django.contrib.postgres
PostgreSQL 有许多 Django 支持的数据库所不具备的特性。这个可选的模块包含了一些 PostgreSQL 特有数据类型的模型字段和表单字段。