https://monicalent.com/blog/2014/10/31/django-tastypie-reverse-relationships-filtering/ -- 2014.10.31
Tastypie是Django最受欢迎的REST API框架之一,如果你已经熟悉了Django Model,那么使用它会相当轻松。
不过,它会很难debug,会生成一些诡秘的错误消息。
下面是一些我在使用框架工作时需要解决的问题,贴士和解决问题的方法,以及一些反馈意见。
为Resource加入字段
为Resource加入字段看起来很简单,也确实很简单 -- 但是,有很多方式可以做到,所以你需要决定使用哪种方式才是适合你的。
1.为字段实现专门的dehydrate
函数
from tastypie import fields
from tastypie.resources import ModelResource
from app.models import MyModel
class MyModelResource(ModelResource):
FOO = fiels.CharField()
class Meta:
queryset = MyModel.objects.all()
def dehydrate_FOO(self):
return bundle.obj.data.FOO.upper()
在这里,我们在函数名中的下划线后面指明了对象引用(也就是函数dehydrate_FOO
会操作FOO
字段,在函数中通过bundle.obj
来访问.如果你通过某种方式进行了更新,Tastypie会为你自动更新bundle.data['FOO']
.
2.实现(resource级别的)dehydrate
方法
from tastypie import fields
from tastypie.resources import ModelResource
from app.models import MyModel
class MyModelResource(ModelResource):
class Meta:
queryset = MyModel.objects.all()
def dehydrate(self, bundle):
bundle.data['new_Foo'] = 'This came from nowhere!'
return bundle
如果你要基于或者不基于其它现有的字段来增加新的字段,使用这种方法就说的通。
3.额外的方法
另外还有一些不同的方式可以手动为Tastypie Resource增加字段。如果你有需要,可以查看下面的一些文章资源:
- Tastypie文档
- 一篇blog,介绍如何在
alter_list_data_to_serialize
中加入字段 - *问题:如何为Tastypie返回的JSON增加额外对象
- *问题:Tastypie是否可以在List和Detail中显示不同的字段集合
排除故障
'Bundle'对象不可以使用字典赋值语法.
如果你想要为bundle
而不是bundle.data
进行字典赋值,会出错。请确保你在操作字段的时候,作用的是bundle.data
这个字典.
bundle['new_field'] = 'This will not work.'
bundle.data['new_field'] = 'This works!'
通过外键,外键的反向关系来映射一个对象的属性
这个章节的标题不太好理解,让我给你一些使用场景:
- 我有一个
Grammer
话题对象列表 - 这些话题的内容,以很多不同的语言来编写
- 每个
Content
都对Grammer
话题有一个ForeignKey
关系 - 在查看Grammer的list view的时候,我想同时看到可用内容的不同语言标题
我的初始JSON:
{
"meta": {
"limit": 20,
"next": "/api/v1/grammar/?offset=20&limit=20&format=json",
"offset": 0,
"previous": null,
"total_count": 1
},
"objects": [
{
"id": 18,
"resource_uri": "/api/v1/grammar/18/",
"name": "First Declension Nouns - Feminine (α-stem)",
}
]
}
我的目标JSON:
{
meta: {
limit: 20,
next: "/api/v1/grammar/?offset=20&limit=20&format=json",
offset: 0,
previous: null,
total_count: 1
},
objects: [
{
id: 18,
resource_uri: "/api/v1/grammar/18/",
name: "First Declension Nouns - Feminine (α-stem)",
titles: {
de: "Die a-Deklination",
en: "First Declension Nouns",
it: "Sostantivi femminili"
}
}
]
}
你可以看到,目标是把关联的content标题组建为字典的形式,使用它们语言的short_code
作为字典的键。我们需要首选获取content,通过grammer来过滤,最后将它们映射为字典。
下面是相关的Django Models:
import textwrap
from django.db import models
class Language(models.Model):
name = models.CharField("Language name(english)",
max_length=200,
help_text=('e.g. German)')
short_code = models.CharField('shortcode',
max_length=5,
def __unicode__(self):
return unicode(self.name) or u""
class Grammer(models.Model):
name = models.CharField("title of grammer section",
max_length=200,
help_text=textwrap.dedent("""
Short, descriptive title of the grammer
concept.
"""))
class Content(models.Model):
title = models.CharField('title',
max_length=200,
help_text=textwrap.dedent("""
Short, descriptive title of the grammer
concept.
"""))
grammer_ref = models.ForeignKey(Grammer,
verbose_name='grammer topic',
null=True,
blank=True,
help_text=textwrap.dedent("""
The morphology directly
described by this content.
"""))
source_lang = models.ForeignKey(Language,
related_name='content_written_in',
help_text=textwrap.dedent("""
Language the content is written
in.
"""))
target_lang = models.ForeignKey(Language,
related_name='content_written_about',
help_text="Language the content teaches.")
content = models.TextField("Learning Content",
help_text=textwrap.dedent("""
Write this in Markdown.
"""))
def __unicode__(self):
return unicode(self.title) or u""
api/grammer.py
- GrammerResource
使用dehydrate
函数来为resource对象增加新的字段,另外加入了一个helper函数用来帮助reduce掉content对象列表。
from tastypie import fields
from tastypie.resources import ModelResource
from app.models import Grammer, Content
from api.content import ContentResource
class GrammerResource(ModelResource):
# 在这里,我们使用反向关系来获取和这个grammer关联的content对象
content = fields.ToManyField(ContentResource, 'content_set',
related_name='content',
blank=True,
null=True,
use_in='detail',
full=True)
class Meta:
queryset = Grammer.objects.all()
allowed_methods = ['get']
def build_title(self, memo, content):
lang = content.resource_lang.short_code
memo[lang] = content.title
return momo
def dehydrate(self, bundle):
bundle.data['titles'] = reduce(self.build_title,
Content.objects.filter(grammer_ref=bundle.obj), {})
return bundle
如果你已经习惯map/reduce
,那么这个代码是能够自解释的。
额外的资源
通过关系来过滤
有一件事,Tastypie看起来没有良好的支持,就是通过值来过滤model的关系。
请考虑下面的使用场景:
- 你有一个
Task
model - 你想要使用另一个model(
TaskSequence
)来排序这个tasks - 你将一个
Task
和一个TaskSequence
以及其它的元数据关联起来(通过through
关系,一个叫做TastContext
的model,它包含task顺序的信息)
如果你只告诉Tastypie要`TaskSequence,你只会得到下面的数据:
{
id: 60,
name: "The Aorist Tense",
query: "pos=verb&tense;=aor",
ref: "s542,s546",
resource_uri: "/api/v1/grammar/60/",
task_sequence: {
id: 2,
name: "Verbs for Beginners",
resource_uri: "",
tasks: [
{
endpoint: "word",
hint_msg: "Try again.",
id: 4,
name: "identify_morph:person",
success_msg: "Good job!"
},
{
endpoint: "word",
hint_msg: "Try again.",
id: 5,
name: "identify_morph:number"
success_msg: "Good job!"
}
]
}
不过,我们需要关心的through表,来决定task的顺序。我们希望的JSON是下面这样:
{
id: 60,
name: "The Aorist Tense",
query: "pos=verb&tense;=aor",
ref: "s542,s546",
resource_uri: "/api/v1/grammar/60/",
task_sequence: {
id: 2,
name: "Verbs for Beginners",
resource_uri: "",
tasks: [
{
id: 4,
max_attempts: 10,
order: 0,
resource_uri: "",
target_accuracy: 0.5,
task: {
endpoint: "word",
hint_msg: "Try again.",
id: 4,
name: "identify_morph:person",
success_msg: "Good job!"
}
},
{
id: 5,
max_attempts: 5,
order: 1,
target_accuracy: 0.8,
task: {
endpoint: "word",
hint_msg: "Try again.",
id: 5,
name: "identify_morph:number",
success_msg: "Good job!"
}
}
]
}
相关Resources
请注意看下面的三个Tastypie Resource。有趣的代码出现在`TaskSequenceResource。我们在这里通过through表来过滤了tasks.
"""
api/task.py
"""
from tastypie.resources import ModelResource
from app.models import Task
class TaskResource(ModelResource):
class Meta:
queryset = Task.objects.all()
allowed_methods = ['get']
"""
api/task_context.py
"""
from tastypie import fields
from tastypie.resources import ModelResource
from app.models import TaskContext
class TaskContextResource(ModelResource):
task = fields.ToOneField('api.task.TaskResource',
'task',
full=True,
null=True,
blank=True)
class Meta:
queryset = TaskContext.objects.all()
allowed_methods = ['get']
"""
api/task_consequence.py
"""
from tastypie import fields
from tastypie.resources import ModelResource
from app.models import TaskSequence
class TaskSequenceResource(ModelResource):
tasks = fields.ManyToManyFields('api.task.TaskContextResource',
attribute=lambda bundle:
bundle.obj.tasks.through.objects.filter(
task_sequence=bundle.obj) or bundle.obj.tasks,
full=True)
class Meta:
queryset = TaskSequence.objects.all()
allowed_methods = ['get']
故障排查
对象是没有
through
属性的.
请注意,lambda bundle: bundle.obj.through.objects
是错的,因为中间缺少了tasks。through只有在queryset中有。
自引用Resources
有时候,Model需要引用自身。例如,有一个Person model,这个model可能会有很多人际关系(关系的类型也是Person)。
难点出来了,在每个Person Model都有一个关系列表的时候。很可能会出现下面的错误:
Maximum recursion depth exceeded
1.让Model的关系不对称(并且不要设置full=True
)
在我们的例子中,这意味着PersonA可以关联PersonB,但是反过来不行。
这让Tastypie可以很容易的处理:
# app/models.py
from django.db import models
class Person(models.Model):
relatives = models.ManyToManyField('self',
related_name='relates_to',
symmetrical=False,
null=True,
blank=True)
# api/person.py
from tastypie import fields
from tastypie.resources import ModelResource
from myapp.models import Person
class PersonResource(ModelResource):
relatives = fields.ToManyField('self', 'relatives')
class Meta:
queryset = Person.objects.all()
2.使用use_in
选项
from tastypie import fields
from tastypie.resources import ModelResource
from myapp.models import Person
class PersonResource(ModelResource):
relatives = fields.ToManyField('self', 'relatives', use_in='list')
class Meta:
queryset = Person.objects.all()
使用这种方式,relatives字段不会在detail view中显示。
3.创建'shallow'版本的resource
如果你需要在你的list view中使用full=True
。最简单避免无限递归的方式是,创建两个resource:
# api/person.py
from tastypie import fields
from tastypie.resources import ModelResource
from myapp.models import Person
from api.relative import RelativeResource
class PersonResource(ModelResource):
relatives = fields.ManyToManyField(RelativeResource,
'relatives',
null=True,
blank=True,
full=True)
class Meta:
queryset = Person.objects.all()
allow_methods = ['get']
# api/relative.py
from tastypie import fields
from tastypie.resource import ModelResource
from myapp.models import Person
class RelativeResource(ModelResource):
class Meta:
queryset = Person.objects.all()
allow_methods = ['get']
请注意,只有PersonResource
设定了full=True
。因为RelativeResource
没有设定m2m字段,所以就不会进入无尽循环。
故障排查
Options
对象没有api_name
属性
请注意你要指向resource,而不是model。
额外资源
Django Tastypie: 贴士,技巧和故障排除的更多相关文章
-
JVMTI 中间JNI系列功能,线程安全和故障排除技巧
JVMTI 中间JNI系列功能,线程安全和故障排除技巧 jni functions 在使用 JVMTI 的过程中,有一大系列的函数是在 JVMTI 的文档中 没有提及的,但在实际使用却是很实用的. 这 ...
-
FIDDLER的使用方法及技巧总结(连载五)FIDDLER的一些故障排除
五.FIDDLER的一些故障排除
-
《DevOps故障排除:Linux服务器运维最佳实践》读书笔记
首先,这本书是Linux.CN赠送的,多谢啦~ http://linux.cn/thread-12733-1-1.html http://linux.cn/thread-12754-1-1.html ...
-
Kubernetes Deployment故障排除图解指南
个人K8s还在学习中,相关博客还没有写,准备学第二遍再开始学,发现这篇文章挺好,先转载一下. 原创: 白明的赞赏账户 下面是一个示意图,可帮助你调试Kubernetes Deployment(你可以 ...
-
使用JDK工具进行Java服务器应用程序故障排除
Java性能调优指南–有关提高Java代码性能的各种技巧. 最近又学到了很多新知识,感谢优锐课老师细致地讲解,这篇博客记录下自己所学所想. 1. 介绍 在Java世界中,我们大多数人习惯于在Java应 ...
-
Apache服务器故障排除攻略
Apache服务器故障排除攻略 应用服务器Apache浏览器配置管理网络应用 随着网络技术的普及.应用和Web技术的不断完善,Web服务已经成为互联网上重要的服务形式之一.原有的客户端/服务器模式正 ...
-
理解 OpenStack + Ceph (7): Ceph 的基本操作和常见故障排除方法
本系列文章会深入研究 Ceph 以及 Ceph 和 OpenStack 的集成: (1)安装和部署 (2)Ceph RBD 接口和工具 (3)Ceph 物理和逻辑结构 (4)Ceph 的基础数据结构 ...
-
细化如何安装LNMP + Zabbix 监控安装文档以及故障排除
1.LNMP所需安装包: 上传如下软件包到/soft目录中 mysql- (centos6. 64位自带)也可根据版本自行挑选,前提你了解这个版本 pcre-8.36.tar.gz nginx-.ta ...
-
第十篇 Replication:故障排除
本篇文章是SQL Server Replication系列的第十篇,详细内容请参考原文. 复制故障排除是一项艰巨的任务.在任何复制设置中,都涉及到很多移动部件,而可用的工具并不总是很容易识别问题.Th ...
随机推荐
-
RabbitMQ学习系列(五): RPC 远程过程调用
前面讲过一些RabbitMQ的安装和用法,也说了说RabbitMQ在一般的业务场景下如何使用.不知道的可以看我前面的博客,http://www.cnblogs.com/zhangweizhong/ca ...
-
无废话SharePoint入门教程一[SharePoint概述]
一.前言 听说SharePoint也有一段时间了,可一直处在门外.最近被调到SharePoint实施项目小组,就随着工作一起学习了一下实施与开发.但苦于网上SharePoint入门的东西实在太少,导致 ...
-
Leetcode OJ : Compare Version Numbers Python solution
Total Accepted: 12400 Total Submissions: 83230 Compare two version numbers version1 and version2 ...
-
Win2003部署Framework 4.5框架的MVC4项目
[一篮饭特稀原创,转载请注明出自http://www.cnblogs.com/wanghafan/p/4554672.html] Win2003中IIS6部署Framework 4.5框架的MVC4 ...
-
chrome浏览器插件开发实例
每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 具体步骤: 1.新建 manifest.json 文件 { "name& ...
-
php配置可被设定范围
PHP中的每个指令都有其所属的模式,这些模式决定这一个PHP指定在何时何地.是否能被设定.例如有些指令可以在 PHP 脚本中用 ini_set() 来设定,而有些则只能在php.ini 或 httpd ...
-
高通 sensor 从native到HAL
app注册传感器监听 Android Sensor Framework 的整体架构如下图所示: 前几篇sensor相关的文章介绍了sensor的hal的知识,以press_sensor实时显示气压坐标 ...
-
逆向---01.Nop、中文字符串搜索、保存修改后程序
基础知识:(Nop:删除跳转) gcc编译链接命令: gcc -o 生成文件名 源文件名 gcc编译成汇编代码:gcc -o 生成文件名 -S 源文件名 VS查看汇编代码:(调试模式下,Ctrl+F ...
-
使用ABP框架踩过的坑系列1
企业级(例如ERP)应用, 一遍一遍的在重复:认证.验证.异常处理.日志.国际化和本地化.数据库连接管理.配置管理. 审计记录等,同时.NET有很多最佳实践:分层.模块化.DDD领域驱动.DI ...
-
jQuery find()方法 eq()方法
find() 使用: 返回 <ul> 后代中所有的 <span> 元素: $(document).ready(function(){$("ul").find ...