Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
21
Help us understand the problem. What is going on with this article?
@sin_tanaka

Django REST Frameworkで再帰的なリレーションを持つモデルを返すAPIを作成したい

More than 3 years have passed since last update.

この記事のゴール

再帰的なリレーションを持つモデル(木構造など)の場合を考える。
DjangoRESTFrameworkを用いて再帰的にjsonを返すAPIを作成する。

環境

MacOS(10.11.6 / ElCapitane)
Python 3.5.2
Django 1.10.4
DjangoRESTFramework 3.5.3

Model

再帰的なリレーションを持つモデルを定義します。カテゴリの中に、更にサブカテゴリを持つモデルを考えます。逆リレーション名はsubcategoriesとします。

models.py
class Category(models.Model):
    parentCategory = models.ForeignKey('self', blank=True, null=True, related_name='subcategories')
    name = models.CharField(max_length=200)
    description = models.CharField(max_length=500)

Serializer

ModelSerializerを継承したSubCategorySerializerを定義し、Categoryの逆リレーションであるsubcategoriesをSubCategorySerializerで上書きします。このときparentCategoryはserializers.PrimaryKeyRelatedField()を返すようにします。

serializers.py
class SubCategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = ('name', 'description')

class CategorySerializer(serializers.ModelSerializer):
    parentCategory = serializers.PrimaryKeyRelatedField()
    subcategories = serializers.SubCategorySerializer()

    class Meta:
        model = Category
        fields = ('parentCategory', 'name', 'description', 'subcategories')

View

ViewでSerializerにCategorySerializerを指定してあげればOKです。クエリセットは一番上の階層を一意に取得できるようなクエリにしましょう。get()だとオブジェクトを取得してしまうので、クエリを返すfilter()などにしましょう。(例えば、is_rootをモデルに定義しておいて、filter(is_root=True)とかで取得する)

view.py
class CategoryListViewSet(viewsets.ModelViewSet):
    queryset = Category.objects.filter(id='e.g...root_category_id').prefetch_related('subcategories__subcategories')
    serializer_class = CategorySeriarizer

別の方法

次のようなRecursiveFieldを定義して返してあげることでも実現可能です。こちらも融通が効くのでよいかなと思います。

serializers.py
class RecursiveField(serializers.Serializer):
    def to_representation(self, value):
        serializer = self.parent.parent.__class__(value, context=self.context)
        return serializer.data

class CategorySerializer(serializers.Serializer):
    subcategories = RecursiveField(many=True)

    class Meta:
        model = Comment
        fields = ('parendCategory','name', 'description', 'subcategories')

結果

上記の例とは別モデルなのですが、単純な木構造かつ再帰的なリレーションを持つファイルシステムモデルだとこんなjsonが返ってきます。

hoge.json
[
    {
        "name": "RootDirectory",
        "is_root": true,
        "is_dir": true,
        "parent_file": null,
        "child_files": [
            {
                "name": "root_file",
                "is_root": false,
                "is_dir": false,
                "parent_file": 34,
                "child_files": []
            },
            {
                "name": "TestDir",
                "is_root": false,
                "is_dir": true,
                "parent_file": 34,
                "child_files": [
                    {
                        "name": "test.zip",
                        "is_root": false,
                        "is_dir": false,
                        "parent_file": 35,
                        "child_files": []
                    },
                    {
                        "name": "test_dir_file.png",
                        "is_root": false,
                        "is_dir": false,
                        "parent_file": 35,
                        "child_files": []
                    }
                ]
            }
        ]
    }
]

みんなもDjangoで楽してREST API作りましょう!!

参考

http://www.django-rest-framework.org/api-guide/relations/#custom-relational-fields
http://stackoverflow.com/questions/13376894/django-rest-framework-nested-self-referential-objects

21
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
sin_tanaka
音楽全般好きで、Pythonで音声解析をしていました。 サーバーサイドを経験後、現在はフロントエンドを中心に開発しています。 持ち前の傲岸不遜・平身低頭で乗り切っています。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
21
Help us understand the problem. What is going on with this article?