Django之模型的高级用法
from django.db import models
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
def __str__(self):
return self.name class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField()
def __str__(self):
return '%s %s' % (self.first_name, self.last_name) class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
def __str__(self):
return self.title
01-访问外键值
访问 ForeignKey 类型的字段时,得到的是相关的模型对象。
>>> b = Book.objects.get(id=50)
>>> b.publisher
<Publisher: Apress Publishing>
>>> b.publisher.website
'http://www.apress.com/'
ForeignKey 字段也能反向使用,若想获取指定出版社出版的所有 图书,要使用 publisher.book_set.all()。
>>> p = Publisher.objects.get(name='Apress Publishing')
>>> p.book_set.all()
[<Book: The Django Book>, <Book: Dive Into Python>, ...]
其实,book_set 就是一个 QuerySet 对象,可以过滤和切片。
>>> p = Publisher.objects.get(name='Apress Publishing')
>>> p.book_set.filter(title__icontains='django')
[<Book: The Django Book>, <Book: Pro Django>]
book_set 属性是生成的:把模型名的小写形式与 _set 连在一起。
02-访问多对多值
查看一本的书作者要:
>>> b = Book.objects.get(id=50)
>>> b.authors.all()
[<Author: Adrian Holovaty>, <Author: Jacob Kaplan-Moss>]
>>> b.authors.filter(first_name='Adrian')
[<Author: Adrian Holovaty>]
>>> b.authors.filter(first_name='Adam')
[]
如果想查看一位作者撰写的所有图书,使用 author.book_set,
>>> a = Author.objects.get(first_name='Adrian',last_name='Holovaty')
>>> a.book_set.all()
[<Book: The Django Book>, <Book: Adrian's Other Book>]
03-管理器
objects 是个特殊的属性,这是 模型的管理器(manager)。
3.1 添加额外的管理器方法
是为模型添加数据表层功能的首选方式。
下面我们为 Book 模型添加一个管理器方法 title_count(),它的参数是一个关键字,返回书名中 包含关键字的图书数量。
# models.py
from django.db import models
# ... Author 和 Publisher 模型省略了 ...
class BookManager(models.Manager):
def title_count(self, keyword):
return self.filter(title__icontains=keyword).count()
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
num_pages = models.IntegerField(blank=True, null=True)
objects = BookManager()
def __str__(self):
return self.title
注意:
1、我们定义的 BookManager 类扩展 django.db.models.Manager。类中只有一个方法,title_count(),做相
关的计算。注意,这个方法使用了 self.filter(),其中 self 指代管理器自身。
2、我们把 BookManager() 赋值给模型的 objects 属性。这么做的效果是替换模型的“默认”管理器,即未指
定管理器时自动创建的 objects。我们仍把它叫做 objects,以便与自动创建的管理器保持一致。 创建好管理器之后,可以像下面这样使用:
Book.objects.title_count('django') 为的是封装经常执行的查询,以免代码重复。
3.2 修改管理器返回的查询集合
管理器的基本查询集合返回系统中的所有对象。如:Book.objects.all() 返回数据库中的所有图书。
若想 覆盖管理器的基本查询集合,覆盖 Manager.get_queryset() 方法。get_queryset() 方法应该返回一个 QuerySet,包含所需的属性。
下述模型有两个管理器,一个返回所有对象,另一个只返回 Joy 写的书。
from django.db import models
# 首先,定义 Manager 子类
class JoyBookManager(models.Manager):
def get_queryset(self):
return super(JoyBookManager, self).get_queryset().filter(author='Joy') # 然后,放入 Book 模型
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
# ...
objects = models.Manager() # 默认的管理器
joy_objects = JoyBookManager() # 专门查询 Dahl 的管理器
对这个示例模型来说,Book.objects.all() 返回数据库中的所有图书,而 Book.joy_objects.all() 只返回 Joy写的书。
这个示例还指出了另一个有用的技术: 在同一个模型上使用多个管理器。只要需要,可以为模型添加任意个 Manager() 实例。这么做,可以轻易为模型定义常用的“过滤器”。
class MaleManager(models.Manager):
def get_queryset(self):
return super(MaleManager, self).get_queryset().filter(sex='M') class FemaleManager(models.Manager):
def get_queryset(self):
return super(FemaleManager, self).get_queryset().filter(sex='F') class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
sex = models.CharField(max_length=1,
choices=(
('M', 'Male'),
('F', 'Female')
))class MaleManager(models.Manager):
def get_queryset(self):
return super(MaleManager, self).get_queryset().filter(sex='M')
class FemaleManager(models.Manager):
def get_queryset(self):
return super(FemaleManager, self).get_queryset().filter(sex='F')
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
sex = models.CharField(max_length=1,
choices=(
('M', 'Male'),
('F', 'Female')
)
)
people = models.Manager()
men = MaleManager()
women = FemaleManager()
这样定义之后,可以使用 Person.men.all()、Person.women.all() 和 Person.people.all(),而且能得到预期 的结果。
自定义 Manager 对象时要注意,Django 遇到的第一个管理器。
Django 把它解释的第一个管理器定义为“默认的”管理器,而且 Django 在很多地方(管理后台不在此 列)只使用那个管理器。
鉴于此,通常最好小心选择默认的管理器,以防把 get_queryset() 返回的结果覆盖掉,无法检索所需的对象。
04-模型方法
管理器的作用是执行数据表层的操作,而模型方法处理的是具体的模型实例。
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField() def baby_boomer_status(self):
# 返回一个人的出生日期与婴儿潮的关系
import datetime
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
elif self.birth_date < datetime.date(1965, 1, 1):
return "Baby boomer"
else:
return "Post-boomer" def _get_full_name(self):
# 返回一个人的全名
return '%s %s' % (self.first_name, self.last_name)
full_name = property(_get_full_name)
各个模型自动具有的方法,其中几个最常定义:
1、__str__()。这是 Python 的一个“魔法方法”,返回对象的 Unicode 表示形式。
2、get_absolute_url()。这个方法告诉 Django 如何计算一个对象的 URL。Django 在管理后台和需要生成 对象的 URL 时调用这个方法。具有唯一标识的 URL 的对象都要定义这个方法。
4.1 覆盖预定义的模型方法
一系列封装数据库行为的模型方法有时也需要自定义。尤其是 save() 和 delete(),经常需要修改它们的运作方式。
如:
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something()
super(Blog, self).save(*args, **kwargs) # 调用“真正的”save () 方法
do_something_else()
一定要记得调用超类中的方法,即 super(Blog, self).save(*args, **kwargs),确保把对象保存到数据库中。
05-执行原始 SQL
模型的查询 API 不够用时,可以编写原始 SQL。Django 为执行原始 SQL 查询提供了两种方式:使用 Manag- er.raw() 执行,返回模型实例集合;或者完全不用模型层,直接执行自定义的 SQL。
注意:编写原始 SQL 时要非常小心。一定要正确转义通过 params 传入的参数,以防 SQL 注入攻击。
06-执行原始查询
管理器的 raw() 方法用于执行原始的 SQL 查询,其返回结果是模型实例集合:
Manager.raw(raw_query, params=None, translations=None)
这个方法的参数是一个原始的 SQL 查询,执行后返回一个 django.db.models.query.RawQuerySet 实例。
实例可以像常规的 QuerySet 对象一样迭代,获取里面的模型对象。
for p in Person.objects.raw('SELECT * FROM myapp_person'):
print(p)
6.1 模型对应的表名
上例中的 Person 表名(myapp_person)是怎么来的呢?Django 默认把“应用标注”(manage.py startapp 命令指定的名称)与类 名使用下划线联结在一起得到数据库表名。在上述示例中,我们假设 Person 模型在 myapp 应用中,因此对应 的表是 myapp_person。
6.2 把查询中的字段映射到模型字段上
1、raw() 自动把查询中的字段映射到模型字段上。查询中的字段顺序无关紧要。
Person.objects.raw('SELECT id, first_name, last_name, birth_date FROM myapp_person') 2、只要名称匹配就能正确创建模型实例。
此外,还可以使用 raw() 方法的 translations 参数把查询中的字段映射到模型字段上。这个参数的值是一个字典,把查询中的字段名称映射到模型字段的名称上。
>>> name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
>>> Person.objects.raw('SELECT * FROM some_other_table', translations=name_map) 3、索引查找
raw() 支持索引,因此如果只想获得第一个结果,可以这样写:
>>> first_person = Person.objects.raw('SELECT * FROM myapp_person')[0]
然而,索引和切片不是在数据库层执行的。如果数据库中的 Person 对象很多,最好在 SQL 查询中限制数量:
>>> first_person = Person.objects.raw('SELECT * FROM myapp_person LIMIT 1')[0] 4、 延期模型字段
还可以把字段排除在外:
>>> people = Person.objects.raw('SELECT id, first_name FROM myapp_person')
这个查询返回的是延期的 Person 对象。这意味着,查询排除的字段将按需加载。如:
for p in Person.objects.raw('SELECT id, first_name FROM myapp_person'):
print(p.first_name, # 这个属性由查询取回
p.last_name) # 这个属性按需取回
从表面看,好像这个查询把名字和姓都取回了。然而,这个示例其实发起了三个查询。raw() 执行的查询只 取回名字,两人的姓在打印时按需取回。
只有一个字段是不能排除的——主键字段。Django 使用主键标识模型实例,因此必须始终包含在原始查询 中。忘记主键时,抛出 InvalidQuery 异常。 5、添加注解
执行的查询还可以包含模型中没有定义的字段。例如,可以使用 PostgreSQL 的 age() 函数让数据库计算所得诸人的年龄:
>>> people = Person.objects.raw('SELECT *, age(birth_date) AS age FROM myapp_person')
>>> for p in people:
...
print("%s is %s." % (p.first_name, p.age))
John is 37.
Jane is 42.
... 6、为 raw() 传递参数
如果想执行参数化查询,可以把 params 参数传给 raw():
>>> lname = 'Doe'
>>> Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])
params 的值是一个参数列表或字典。使用列表时,查询字符串中的占位符是 %s;使用字典时,占位符是 %(key)s
注意:原始查询中不要使用字符串格式化!
>>> query = 'SELECT * FROM myapp_person WHERE last_name = %s' % lname
Person.objects.raw(query)
千万别这么写!
07-直接执行自定义的 SQL
django.db.connection 对象表示默认的 数据库连接。若想使用这个数据库连接,调用 connection.cursor(),获取一个游标对象。
然后,调用 cur- sor.execute(sql, [params]) 执行 SQL,再调用 cursor.fetchone() 或 cursor.fetchall() 返回所得的行。
from django.db import connection
def my_custom_sql(self):
cursor = connection.cursor()
cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
row = cursor.fetchone()
return row 注意,传入参数时,如果查询中有百分号,应该编写两个百分号:
cursor.execute("SELECT foo FROM bar WHERE baz = '30%'")
cursor.execute("SELECT foo FROM bar WHERE baz = '30%%' AND id = %s", [self.id]) 使用多个数据库时,可以使用 django.db.connections 获取指定数据库的连接(和游标)。django.db.connec-
tions 是一个类似字典的对象,可以使用别名取回指定连接:
from django.db import connections
cursor = connections['my_db_alias'].cursor()
# 其他代码...
7.1 连接和游标
要注意,cursor.execute() 中的 SQL 语句使用占位符 %s,而不直接把参数添加到 SQL 查询中。
使用占位符时,底层数据库库会自动转义参数。还要注意,Django 使用的占位符是 %s,而不是 ?。
7.2 添加额外的管理器方法
添加额外的管理器方法是为模型添加数据表层功能的首选方式。自定义的管理器方法可以返回任何需要的内容,不一定是 QuerySet。
例如,下面这个自定义的管理器提供了 with_counts() 方法,它返回所有 OpinionPoll 对象,每个对象都有额 外的 num_responses 属性,其值为聚合查询的结果:
from django.db import models class PollManager(models.Manager):
def with_counts(self):
from django.db import connection cursor = connection.cursor()
cursor.execute("""
SELECT p.id, p.question, p.poll_date, COUNT(*)
FROM polls_opinionpoll p, polls_response r
WHERE p.id = r.poll_id
GROUP BY p.id, p.question, p.poll_date
ORDER BY p.poll_date DESC""")
result_list = []
for row in cursor.fetchall():
p = self.model(id=row[0], question=row[1], poll_date=row[2])
p.num_responses = row[3]
result_list.append(p)
return result_list class OpinionPoll(models.Model):
question = models.CharField(max_length=200)
poll_date = models.DateField()
objects = PollManager() class Response(models.Model):
poll = models.ForeignKey(OpinionPoll)
person_name = models.CharField(max_length=50)
response = models.TextField()
对这个示例来说,要使用 OpinionPoll.objects.with_counts() 获取具有 num_responses 属性的 OpinionPoll 对 象列表。还有一点要注意:管理器方法可以访问 self.model,获取所依附的模型类。
Django之模型的高级用法的更多相关文章
- django基础之day05,F与Q查询,Q查询的高级用法
#F与Q查询 #*************************** F 查询 ******************** # F 查询数据库中的其他字段!!! #1.查询库存数大于卖出数的书籍 fr ...
- Django框架-模型层
Django框架-模型层 一.单表查询之必知必会13条 1.时间字段中的两个关键性参数 create_time = models.DateField() # 年月日 create_time = mod ...
- Visual Studio 宏的高级用法
因为自 Visual Studio 2012 开始,微软已经取消了对宏的支持,所以本篇文章所述内容只适用于 Visual Studio 2010 或更早期版本的 VS. 在上一篇中,我已经介绍了如何编 ...
- 细说 ASP.NET Cache 及其高级用法
许多做过程序性能优化的人,或者关注过程程序性能的人,应该都使用过各类缓存技术. 而我今天所说的Cache是专指ASP.NET的Cache,我们可以使用HttpRuntime.Cache访问到的那个Ca ...
- 细说 ASP.NET Cache 及其高级用法【转】
阅读目录 开始 Cache的基本用途 Cache的定义 Cache常见用法 Cache类的特点 缓存项的过期时间 缓存项的依赖关系 - 依赖其它缓存项 缓存项的依赖关系 - 文件依赖 缓存项的移除优先 ...
- 简学Python第七章__class面向对象高级用法与反射
Python第七章__class面向对象高级用法与反射 欢迎加入Linux_Python学习群 群号:478616847 目录: Python中关于oop的常用术语 类的特殊方法 元类 反射 一.P ...
- 【Django】模型层说明
[Django模型层] 之前大概介绍Django的文章居然写了两篇..这篇是重点关注了Django的模型层来进行学习. ■ 模型定义 众所周知,Django中的模型定义就是定义一个类,其基本结构是这样 ...
- Django笔记--模型
ORM是"对象-关系-映射"的简称,在Django当中,ORM就是模型类的管理器对象.操作顺序是先定义模型类,再定义模型类管理器,然后在模型类中实例化一个模型类管理器的对象,作为模 ...
- django中模型详解-字段类型与约束条件
这片博文来详细说明django模型的使用,涉及到django模型的创建,字段介绍,以及django模型的crud操作,以及一对一等操作. 在使用模型之前,我们首先设置数据库选项,django的默认数据 ...
随机推荐
- SQL语句复习【专题二】
SQL语句复习[专题二] 单行函数(日期.数学.字符串.通用函数.转换函数)多行函数.分组函数.多行数据计算一个结果.一共5个.sum(),avg(),max(),min(),count()分组函数 ...
- js动态添加控件(输入框为例)
写在前面 昨天得到一个需求,需要在账户登记页面中动态添加输入框,经过半天的捣鼓,最终完美成型,写下来跟大家分享下, 供大家参考 开始复制代码了 如果复制了我所有代码的话,注意看js最后面方法的备注,最 ...
- Springboot + Mybatis + Ehcache
最近在做一个项目,为处理并发性较差的问题,使用了Mybatis二级缓存 但在多表联合查询的情况下,Mybatis二级缓存是存在着数据脏读的问题的 两天就是在想办法解决这个数据脏读的问题 考虑到简易性. ...
- HelloWorld编写过程中注意事项
一.package关键字 * package表示当前代码所属的包(package),是一种组织结构.其他package通过包名调用这个包下内容* package是必须的,每个文件的package必须存 ...
- CF888G Xor-MST[最小生成树+01trie]
前注:关于这题,本人的解法暂时没有成功通过此题,原因是被卡常了.可能需要等待某种机缘来请人调试. 类似uoj的一道题(新年的繁荣),不过是一个有些简单的版本. 因为是完全图,有没有办法明显优化建边,所 ...
- 用 D3.js 画一个手机专利关系图, 看看苹果,三星,微软间的专利纠葛
前言 本文灵感来源于Mike Bostock 的一个 demo 页面 原 demo 基于 D3.js v3 开发, 笔者将其使用 D3.js v5 进行重写, 并改为使用 ES6 语法. 源码: gi ...
- Hadoop-No.11之元数据
元数据的重要性 三个重要理由,让我们不得不在意元数据 元数据允许用户通过一张表的高一级逻辑抽象,而不是HDFS中文件的简单几何,或者HBase中的表来与数据交互.这意味着用户不比关心数据是如何存储的, ...
- Java-JDBCUtil工具类
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import ja ...
- Acwing-198-反素数(约数, 数学)
链接: https://www.acwing.com/problem/content/200/ 题意: 对于任何正整数x,其约数的个数记作g(x),例如g(1)=1.g(6)=4. 如果某个正整数x满 ...
- vue环境搭建及简单接触
1.安装node环境 首先官网安装nodejs,下载地址https://nodejs.org/en/ 很多情况下,npm i 命令安装的包都是要科学上网的,或者就是国际网,下载速度很慢,不过有个淘宝镜 ...