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. 分步骤讲解Deployment故障排除

    背景假设 当你希望在Kubernetes中部署应用程序时,你通常会定义三个组件: 一个Deployment - 这是一份用于创建你的应用程序的Pod副本的"食谱": 一个Servi ...

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

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

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

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

随机推荐

  1. ZOJ3774 Power of Fibonacci 斐波那契、二项式定理

    传送门--Vjudge 要求\(S = \sum\limits_{i=1}^n fib_i^k \mod 10^9+9\) 将通项公式\(fib_i = \frac{1}{\sqrt{5}} ((\f ...

  2. Please check that your locale settings问题

    问题描述: perl: warning: Setting locale failed. perl: warning: Please check that your locale settings: L ...

  3. 【转】详解springboot-修改内置tomcat版本

    1.解析Spring Boot父级依赖 <parent> <groupId>org.springframework.boot</groupId> <artif ...

  4. Redis数据结构之简单动态字符串SDS

    Redis的底层数据结构非常多,其中包括SDS.ZipList.SkipList.LinkedList.HashTable.Intset等.如果你对Redis的理解还只停留在get.set的水平的话, ...

  5. iOS 简易型标签的实现(UICollectionView)

    https://blog.csdn.net/sinat_39362502/article/details/80900984 2018年07月03日 16:49:05 Recorder_MZou 阅读数 ...

  6. apply和call与this

    函数本身的apply方法,改变this指向哪个对象: function getAge() { var y = new Date().getFullYear(); return y - this.bir ...

  7. Jmeter二次开发代码(1)

    package org.apache.jmeter.functions; import java.util.Collection;import java.util.LinkedList;import ...

  8. python之正则表达式和re模块一

    摘要:正则表达式 re模块 一.正则表达式:只和字符串打交道,是一种用来约束字符串的规则 1.应用场景: 1,判断某一个字符串是否符合规则:注册页-判断手机号.身份证号 是否合法 注册某个账号的时候, ...

  9. 使用jar包格式化Docker 容器日志

    前面使用JS格式化textarea中的日志内容,但局限于JS语言性能,在日志内容较多时效率无法接受,建议日志内容大于5000行时转投本java程序,文末提供jar包下载. LogsFormat.jav ...

  10. Django 信号

    信号 Django 提供一个“信号分发器”,允许解耦的应用在框架的其它地方发生操作时会被通知到. 简单来说,信号允许特定的sender通知一组receiver某些操作已经发生. 这在多处代码和同一事件 ...