Django Tastypie: 贴士,技巧和故障排除

时间:2022-09-25 23:46:50

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增加字段。如果你有需要,可以查看下面的一些文章资源:

排除故障

'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: 贴士,技巧和故障排除的更多相关文章

  1. JVMTI 中间JNI系列功能,线程安全和故障排除技巧

    JVMTI 中间JNI系列功能,线程安全和故障排除技巧 jni functions 在使用 JVMTI 的过程中,有一大系列的函数是在 JVMTI 的文档中 没有提及的,但在实际使用却是很实用的. 这 ...

  2. FIDDLER的使用方法及技巧总结(连载五)FIDDLER的一些故障排除

    五.FIDDLER的一些故障排除

  3. 《DevOps故障排除:Linux服务器运维最佳实践》读书笔记

    首先,这本书是Linux.CN赠送的,多谢啦~ http://linux.cn/thread-12733-1-1.html http://linux.cn/thread-12754-1-1.html ...

  4. Kubernetes Deployment故障排除图解指南

     个人K8s还在学习中,相关博客还没有写,准备学第二遍再开始学,发现这篇文章挺好,先转载一下. 原创: 白明的赞赏账户 下面是一个示意图,可帮助你调试Kubernetes Deployment(你可以 ...

  5. 使用JDK工具进行Java服务器应用程序故障排除

    Java性能调优指南–有关提高Java代码性能的各种技巧. 最近又学到了很多新知识,感谢优锐课老师细致地讲解,这篇博客记录下自己所学所想. 1. 介绍 在Java世界中,我们大多数人习惯于在Java应 ...

  6. Apache服务器故障排除攻略

    Apache服务器故障排除攻略 应用服务器Apache浏览器配置管理网络应用  随着网络技术的普及.应用和Web技术的不断完善,Web服务已经成为互联网上重要的服务形式之一.原有的客户端/服务器模式正 ...

  7. 理解 OpenStack + Ceph (7): Ceph 的基本操作和常见故障排除方法

    本系列文章会深入研究 Ceph 以及 Ceph 和 OpenStack 的集成: (1)安装和部署 (2)Ceph RBD 接口和工具 (3)Ceph 物理和逻辑结构 (4)Ceph 的基础数据结构 ...

  8. 细化如何安装LNMP + Zabbix 监控安装文档以及故障排除

    1.LNMP所需安装包: 上传如下软件包到/soft目录中 mysql- (centos6. 64位自带)也可根据版本自行挑选,前提你了解这个版本 pcre-8.36.tar.gz nginx-.ta ...

  9. 第十篇 Replication:故障排除

    本篇文章是SQL Server Replication系列的第十篇,详细内容请参考原文. 复制故障排除是一项艰巨的任务.在任何复制设置中,都涉及到很多移动部件,而可用的工具并不总是很容易识别问题.Th ...

随机推荐

  1. RabbitMQ学习系列(五): RPC 远程过程调用

    前面讲过一些RabbitMQ的安装和用法,也说了说RabbitMQ在一般的业务场景下如何使用.不知道的可以看我前面的博客,http://www.cnblogs.com/zhangweizhong/ca ...

  2. 无废话SharePoint入门教程一[SharePoint概述]

    一.前言 听说SharePoint也有一段时间了,可一直处在门外.最近被调到SharePoint实施项目小组,就随着工作一起学习了一下实施与开发.但苦于网上SharePoint入门的东西实在太少,导致 ...

  3. Leetcode OJ : Compare Version Numbers Python solution

    Total Accepted: 12400 Total Submissions: 83230     Compare two version numbers version1 and version2 ...

  4. Win2003部署Framework 4.5框架的MVC4项目

    [一篮饭特稀原创,转载请注明出自http://www.cnblogs.com/wanghafan/p/4554672.html]  Win2003中IIS6部署Framework 4.5框架的MVC4 ...

  5. chrome浏览器插件开发实例

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code   具体步骤: 1.新建 manifest.json 文件 { "name& ...

  6. php配置可被设定范围

    PHP中的每个指令都有其所属的模式,这些模式决定这一个PHP指定在何时何地.是否能被设定.例如有些指令可以在 PHP 脚本中用 ini_set() 来设定,而有些则只能在php.ini 或 httpd ...

  7. 高通 sensor 从native到HAL

    app注册传感器监听 Android Sensor Framework 的整体架构如下图所示: 前几篇sensor相关的文章介绍了sensor的hal的知识,以press_sensor实时显示气压坐标 ...

  8. 逆向---01.Nop、中文字符串搜索、保存修改后程序

    基础知识:(Nop:删除跳转) gcc编译链接命令: gcc -o 生成文件名 源文件名  gcc编译成汇编代码:gcc -o 生成文件名 -S 源文件名 VS查看汇编代码:(调试模式下,Ctrl+F ...

  9. 使用ABP框架踩过的坑系列1

        企业级(例如ERP)应用, 一遍一遍的在重复:认证.验证.异常处理.日志.国际化和本地化.数据库连接管理.配置管理. 审计记录等,同时.NET有很多最佳实践:分层.模块化.DDD领域驱动.DI ...

  10. jQuery find()方法 eq()方法

    find() 使用: 返回 <ul> 后代中所有的 <span> 元素: $(document).ready(function(){$("ul").find ...