Comment éviter les "Import cyclic" avec Django rest framework

7 octobre 2016
Aymeric

Je vais être franc, je ne suis pas sûr de proposer une solution élégante à ce problème, mais je n'ai pas réussi à trouver mieux.

Pour illustrer le problème, il suffit de prendre le cas d'un model Utilisateur et d'un model Article qui sont chacun dans une application différente.

On souhaite créer un UserSerializer qui inclut tous les Articles de l'auteur et un ArticleSerializer qui inclut l'auteur de l'article. Personellement j'aime bien séparer mes serializers, avec dans ce cas 2 fichiers : users/serializers.py et articles/serializers.py

# users/serializers.py
from .articles import ArticleSerializer

class UserSerializer(ModelSerializer):
    articles = ArticleSerializer(many=True)
    class Meta:
        model = User
        fields = ('__all__')

# articles/serializers.py
from .users import UserSerializer

class ArticleSerializer(ModelSerializer):
    user = UserSerializer()
    class Meta:
        model = Article
        fields = ('__all__')

Cependant, avec ce système, vous allez avoir une importation cyclic pour la simple et bonne raison que Python va exécuter le code des fichiers inclus lors de l'inclusion, et donc par conséquence faire les importations des fichiers importés !

Utiliser un seul fichier

Commençons par la solution la plus simple : utiliser un seul fichier pour tous les serializers de votre application.

Cependant c'est exactement ce qui est fait dans notre exemple, nous avons deux applications users et articles , avec chacun un serializer. Cette solution est en effet intéressante par exemple si nous avions un modèle Categorie pour les articles. Son serializer serait alors définis dans le même fichier que le serializer de Article évitant ainsi l'importation circulaire.

Mais cela ne résout pas notre problème, avec deux applications différentes, chacun ayant son fichier serializers.py .

Importer localement

La seule solution trouvée est d'utiliser une importation locale en utilisant SerializerMethodField :

# serializers/users.py
class UserSerializer(ModelSerializer):
    articles = serializers.SerializerMethodField()

    def get_articles(self, o):
        from .articles import ArticleSerializer # On import ici, ce qui évite une inclusion circulaire
        return ArticleSerializer(o.articles).data

    class Meta:
        model = User
        fields = ('__all__')

Je ne suis pas très fier de cette solution, mais je n'ai pas trouvé mieux pour l'instant.

Articles récents

Catégories

Tags