Help us understand the problem. What is going on with this article?

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

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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした