上一篇中,我们已经建立了基本的 Model 数据模型,并且熟悉了关于数据结构的存取、更新操作。本篇中主要介绍了对 Model 数据进行操作的常用方法,包含有许多示例 参见 ,亲自操作一下就很容易明白。
为了方便,我们可以通过 Python 在终端提供的交互式 Shell 来尝试和熟悉各种数据接口类的 API 。
python manage.py shell
使用这个命令会启动 Python 的交互式 Shell ,并且预先加载好 Django 所需的环境和变量。
示例:创建和修改新对象
涉及到的新方法:
all()
列出所有对象save()
将该对象存入数据库
from polls.models import Question, Choice # 导入要操作的 Model Question.objects.all() from django.utils import timezone q = Question(question_text="What's new?", pub_date=timezone.now()) # 创建一个新的 Question 对象 q.save() # 保存后才会被写入数据库 q.id # 输出属性 q.question_text q.pub_date q.question_text = "What's up?" # 修改属性 q.save() Question.objects.all()
示例:为 Model 编写自定义方法
因为 Question.objects.all()
的输出 [<Question: Question object>]
是一个没有任何意义的对象标识,我们可以为 Question 类型增一个 __str__
方法。同时可以了解一下如何添加自定义方法。修改之前的 polls/models.py
,在里面加入:
import datetime from django.utils import timezone # … class Author(models.Model): # … def __str__(self): return self.name class Question(models.Model): # ... def __str__(self): return self.question_text def was_published_recently(self): # 判断该 Question 是否为 1 天内添加的自定义方法 return self.pub_date >= timezone.now() - datetime.timedelta(days=1) class Choice(models.Model): # ... def __str__(self): return self.choice_text
现在再回到交互式 Shell ,测试刚才新增的方法的效果。在 models.py
中对方法的修改并不需要重新迁移生成,就可以马上生效:
from polls.models import Question, Choice Question.objects.all() # 此时的输出已经带有描述文字
当然,如果改动了 models.py
中的数据结构,记得要重新 makemigrations 和 migrate 。
关联对象常用方法参考
这些方法常用在含有 ForeignKey 或 ManyToManyField 类的关联对象中。
add(obj1, obj2, ...)
添加新的关联remove(obj1, obj2, ...)
删除关联clear()
删除所有关联set([obj1, obj2, …])
直接用新的对象组覆盖已有的关联count()
计数子对象数delete()
删除对象create()
新建关联对象并直接引用到数据库(不需要再手动保存)
示例:数据的添加
a1 = Author(name="dim”,email="dim@example.com") a2 = Author(name="ikaros",email="ikaros@example.com") a1.save() a2.save() q.authors.add(a1,a2) # 给 ManyToManyField 添加多个指向 q.authors.all() q.authors.count() q.authors.all()[1].delete() q.authors.all() q.choice_set.create(choice_text='Not much', votes=0) # 在 Question 下新建一个 Choice q.choice_set.create(choice_text='The sky', votes=0) q.choice_set.all() c = q.choice_set.create(choice_text='Just hacking again', votes=0) c.question # 从 q.choice_set 新创建的 Choice 已经自动指向了父级 q
其他 Model 类型方法参考
以下是前文未提到的一些对 Model 对象实例进行操作的方法。参见
refresh_from_db()
将缓存中该对象的值更新为与数据库相同clean_fields(exclude=None)
验证 Field 有效性clean()
应当由你自行定义,提供验证 Field 是否有效和自动修复(填入数据)的功能full_clean()
调用 clean_fields() 和 clean() 方法(如果有)validate_unique()
类似 clean_fields() ,用于检测重复save(force_insert=True, force_update=True)
启用强制插入或更新
数据的取出、复制和筛选
针对每个 Models ,都提供了一个称为 objects 的对象管理器 (Manager)。利用 objects 的各种方法,可以方便的进行数据的管理工作。关于筛选对象的条件语句称为 QuerySet 。
涉及到的新方法:
objects.get()
获取单个对象objects.filter()
获取多个对象exclude()
排除符合条件的对象F()
在条件语句中引用其他属性的值Q()
合并复杂查询update()
批量修改
为了方便,再增加一组测试数据,然后尝试示例:
q2 = Question(question_text=“Try another?”, pub_date=timezone.now()) q2.save() Question.objects.get(id=1) # 根据 id 获取对象,也可以用其他 Field 的值搜索 q3 = Question.objects.get(pk=1) # 根据主键 (Primary Key) 获取对象,在这里就是 id
将一个对象副本的唯一标识 (pk, id 等) 删除,再重新存入,此时会自动分配 pk 或 id ,即实现了对象的复制。
q3.pk = None q3.save() q3.pk # 可以看出 id 已经更新
get() 只能取得一个对象,无匹配或者不止一个匹配时都会报错,而 filter() 可以取得多个匹配。
from django.utils import timezone current_year = timezone.now().year Question.objects.get(pub_date__year=current_year) Question.objects.filter(pub_date__year=current_year) qlist = Question.objects.filter(question_text__startswith='What') from datetime import timedelta qlist2 = qlist.exclude(pub_date__gte=datetime.date.today() + timedelta(days=3))
对 ManyToManyField 类 Field 指向的对象集,同样可以应用 get() 和 filter() 。举例来说,要获取关联了该 Question 对象的全部 Choice 对象,只需要加上一个 _set 后缀:
q.choice_set.filter(choice_text__startswith='Just hacking')
至于 F() 和 Q() ,使用时需要额外引用其所在的模块。
from django.db.models import F Author.objects.filter(email__startswith=F('name')) from django.db.models import Q Question.objects.filter(Q(question_text__startswith='What'),Q(pub_date__lt=date(2011, 11, 11)) | Q(pub_date__gte=date(2012, 3, 4))) Author.objects.all().update(email="admin@example.com") Author.objects.all().update(email=F('name'))
QuerySet 条件语句参考
这里是在括号中使用的一些常用条件语句后缀,在这些条件语句中,利用双下划线区分层级来描述最终的指向,比如连接 Model 和 Field 的名称,最后再加上双下划线以及条件后缀。具体可以参考上方的示例。
__excat
精确等于,如果是字符则大小写也要相同__contains
内容包含__startswith, __endswith
开始于,结尾于__iexact, __istartswith, __endswith
不精确等于, 不开始于,不结尾于__gt, __gte, __lt, __lte
大于,大于等于,小于,小于等于
失踪人口回归=。=
大过年的时候我居然就用卡的不行的 iBook 在老家坑出了这么一篇文章,个中辛苦 …… 反正说了也没人想听 (:зゝ∠)
现在还没什么余力去对高中生活做什么解释。就这样吧。请不抱期待的等待咱的更新w
我发现我还漏了一坨 QuerySet 的 API ˊ_>ˋ 算了明天再说吧 ……
路过点赞
钉子终于更文了
(:зゝ∠) 然而更的是这种没人看的文
真心佩服。我高中时还什么都不懂呢。。。
新年快乐……竟然正好看到更新,然而啥都不懂TAT
钉子终于更了w
最近在捣鼓Flask,相比Django感觉更自由的样子~
也是不错的框架2333
春节有心写这写真心不错呢,吾辈春节感觉一点都没休息(撸自己的小项目),给家里当了5天专职司机,全家出去旅游. 于是乎由上班了
话说,跪求钉子的头像插件,博客里评论头像神low….用插件设置下结果评论者和我一个头像了…( •̀ ω •́ )
是说随机默认头像这个么?这个是 WordPress 的默认功能,试试看后台的「讨论」设置看看吧。
钉子居然更新惹(
不错,不错,看看了!
看看!
围观菊苣!
话说菊苣的主题真的超赞!
作为一个即将高考的高三党,给你鼓鼓气。另外,作为一只还算挺学霸的狗,我想说,你的生活是绝对充实的!加油!
菊苣教一下我那个你那个关键词那个框框是怎么加上去的?
誒?是說行內的 Code 標籤麼?
嗯嗯。那个怎么弄的?
就是 CSS 改了下样式外加 JS 加了个 coneditable 属性罢了 …… 你可以参考下我的 style.css 和 script.js 有关 code 和 pre 标签的部分
钉触!
感谢大触
博客风格真的挺喜欢的
从大学用到工作一年,nice