Django Tastypie: 贴士,技巧和故障排除
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中加入字段 - StackOverflow问题:如何为Tastypie返回的JSON增加额外对象
- StackOverflow问题: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的关系。
请考虑下面的使用场景:
- 你有一个
Taskmodel - 你想要使用另一个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服务已经成为互联网上重要的服务形式之一.原有的客户端/服务器模式正 ...
- 分步骤讲解Deployment故障排除
背景假设 当你希望在Kubernetes中部署应用程序时,你通常会定义三个组件: 一个Deployment - 这是一份用于创建你的应用程序的Pod副本的"食谱": 一个Servi ...
- 理解 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 ...
随机推荐
- TCP三次握手原理,你真的了解吗?
最近碰到一个问题,Client 端连接服务器总是抛异常.在反复定位分析.并查阅各种资料搞懂后,我发现并没有文章能把这两个队列以及怎么观察他们的指标说清楚. 问题描述 场景:Java 的 Client ...
- C#模板设计模式使用和学习心得
模板设计模式: 模版方法模式由一个抽象类和一个(或一组)实现类通过继承结构组成,抽象类中的方法分为三种: 抽象方法:父类中只声明但不加以实现,而是定义好规范,然后由它的子类去实现. 模版方法:由抽象类 ...
- 开放数据接口 API 简介与使用场景、调用方法
此文章对开放数据接口 API 进行了功能介绍.使用场景介绍以及调用方法的说明,供用户在使用数据接口时参考之用. 在给大家分享的一系列软件开发视频课程中,以及在我们的社区微信群聊天中,都积极地鼓励大家开 ...
- Flask中使用数据库连接池 DBUtils ——(4)
DBUtils是Python的一个用于实现数据库连接池的模块. 此连接池有两种连接模式: 模式一:为每个线程创建一个连接,线程即使调用了close方法,也不会关闭,只是把连接重新放到连接池,供自己线程 ...
- MongoDB系列:一、MongoDB和Redis区别
简介 MongoDB更类似Mysql,支持字段索引.游标操作,其优势在于查询功能比较强大,擅长查询JSON数据,能存储海量数据,但是不支持事务. Mysql在大数据量时效率显著下降,MongoDB更多 ...
- ZabbixServer安装
Zabbix服务端安装主要分二种一直yum在线安装,一种离线安装,在线安装只需简单命令自己便可安装离线安装得自定义路径等等...比较繁琐不过便于文件管理.这里简单配置一下在线安装. https://w ...
- Spring Security 无法登陆,报错:There is no PasswordEncoder mapped for the id “null”
编写好继承了WebSecurityConfigurerAdapter类的WebSecurityConfig类后,我们需要在configure(AuthenticationManagerBuilder ...
- a = a + 1, a++, ++a ,a+=1区别在哪
a = a +1; 即最普通的写法,将a的值加1再赋给a:a+=1; 相当于 a = a+1; a++; 是先将a的值赋给一个变量, 再自增: ++a:是先自增, 再把a的值给一个变量
- windows下零基础gulp构建
在学习前,先谈谈大致使用gulp的步骤,给读者以初步的认识.首先当然是安装nodejs,通过nodejs的npm全局安装和项目安装gulp,其次在项目里安装所需要的gulp插件,然后新建gulp的配置 ...
- 洛谷P1220关路灯题解
题目 此题是一个状态转移方程还算比较多的一个区间DP,这个题也能启示我们如果某个状态不能够很好地解决问题,那么不妨试试再加一维,而且如果转移顺序不确定的话,可以试试记忆化搜索,说不定就可以比较容易的写 ...