LoginSignup
19
23

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-01-15

この記事のゴール

再帰的なリレーションを持つモデル(木構造など)の場合を考える。
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

19
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
23