Framework Django REST: sérialiseur sans modèle

158

Je suis débutant dans le framework Django REST et j'ai besoin de vos conseils. Je développe un service web. Le service doit fournir une interface REST à d'autres services. L'interface REST, que je dois implémenter, ne fonctionne pas directement avec mes modèles (je veux dire les opérations get, put, post, delete). Au lieu de cela, il fournit d'autres services avec des résultats de calcul. Sur demande, mon service effectue des calculs et renvoie simplement les résultats (ne stocke pas les résultats dans sa propre base de données).

Voici ma compréhension de la façon dont cette interface REST pourrait être implémentée. Corrigez-moi si je me trompe.

  1. Créez une classe qui effectue les calculs. Nommez-le «CalcClass». CalcClass utilise les modèles dans son travail.
    • Les paramètres nécessaires aux calculs sont transmis au constructeur.
    • Implémentez l'opération calc. Il renvoie les résultats sous la forme «ResultClass».
  2. Créez ResultClass.
    • Dérivé de l'objet.
    • Il n'a que des attributs contenant les résultats de calcul.
    • Une partie des résultats de calcul est représentée sous forme de tuple de tuples. Si je comprends bien, il serait préférable pour une sérialisation ultérieure d'implémenter une classe distincte pour ces résultats et d'ajouter une liste de ces objets à ResultClass.
  3. Créez un sérialiseur pour ResultClass.
    • Dériver des sérialiseurs.Serializer.
    • Les résultats de calcul sont en lecture seule, utilisez donc principalement la classe Field pour les champs, au lieu de classes spécialisées, telles que IntegerField.
    • Je ne devrais pas impliquer la méthode save () ni sur ResultClass, ni sur Serializer, car je ne vais pas stocker les résultats (je veux juste les renvoyer sur demande).
    • Sérialiseur d'impl pour les résultats imbriqués (rappelez-vous le tuple de tuples mentionné ci-dessus)
  4. Créer une vue pour renvoyer les résultats du calcul.
    • Dérivé d'APIView.
    • Besoin juste d'obtenir ().
    • Dans get (), créez CalcClass avec les paramètres extraits de la requête, appelez son calc (), récupérez ResultClass, créez Serializer et passez-lui la ResultClass, retournez Response (serializer.data).
  5. URL
    • Il n'y a pas de racine api dans mon cas. Je devrais juste avoir des URL pour obtenir divers résultats de calcul (calc avec des paramètres de diff).
    • Ajoutez un appel à format_suffix_patterns pour la navigation dans l'API.

Ai-je oublié quelque chose? L'approche est-elle correcte en général?

Zakhar
la source
Cette approche est correcte et me semble en fait plus élégante que la réponse acceptée (données de résultats encapsulées dans un type de résultat réutilisable). Mais en fin de compte, c'est surtout une question de préférence personnelle et les deux approches font l'affaire.
zepp.lee

Réponses:

157

Django-rest-framework fonctionne bien même sans le lier à un modèle. Votre approche semble correcte, mais je pense que vous pouvez réduire certaines étapes pour que tout fonctionne.

Par exemple, le cadre de repos est livré avec quelques moteurs de rendu intégrés. Prêt à l'emploi, il peut renvoyer JSON et XML au consommateur d'API. Vous pouvez également activer YAML en installant simplement le module python requis. Django-rest-framework affichera n'importe quel objet de base comme dict, list et tuple sans aucun travail supplémentaire de votre part.

Donc, fondamentalement, vous n'avez qu'à créer la fonction ou la classe qui prend les arguments, effectue tous les calculs requis et renvoie ses résultats dans un tuple à la vue API REST. Si JSON et / ou XML répond à vos besoins, django-rest-framework se chargera de la sérialisation pour vous.

Vous pouvez ignorer les étapes 2 et 3 dans ce cas et utiliser simplement une classe pour les calculs et une pour la présentation au consommateur d'API.

Voici quelques extraits qui peuvent vous aider:

Veuillez noter que je n'ai pas testé cela. Cela ne sert qu'à titre d'exemple, mais cela devrait fonctionner :)

La CalcClass:

class CalcClass(object):

    def __init__(self, *args, **kw):
        # Initialize any variables you need from the input you get
        pass

    def do_work(self):
        # Do some calculations here
        # returns a tuple ((1,2,3, ), (4,5,6,))
        result = ((1,2,3, ), (4,5,6,)) # final result
        return result

La vue REST:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

from MyProject.MyApp import CalcClass


class MyRESTView(APIView):

    def get(self, request, *args, **kw):
        # Process any get params that you may need
        # If you don't need to process get params,
        # you can skip this part
        get_arg1 = request.GET.get('arg1', None)
        get_arg2 = request.GET.get('arg2', None)

        # Any URL parameters get passed in **kw
        myClass = CalcClass(get_arg1, get_arg2, *args, **kw)
        result = myClass.do_work()
        response = Response(result, status=status.HTTP_200_OK)
        return response

Vos urls.py:

from MyProject.MyApp.views import MyRESTView
from django.conf.urls.defaults import *

urlpatterns = patterns('',
    # this URL passes resource_id in **kw to MyRESTView
    url(r'^api/v1.0/resource/(?P<resource_id>\d+)[/]?$', login_required(MyRESTView.as_view()), name='my_rest_view'),
    url(r'^api/v1.0/resource[/]?$', login_required(MyRESTView.as_view()), name='my_rest_view'),
)

Ce code doit générer une liste de listes lorsque vous accédez à http://example.com/api/v1.0/resource/?format=json . Si vous utilisez un suffixe, vous pouvez le remplacer ?format=jsonpar .json. Vous pouvez également spécifier l'encodage que vous souhaitez récupérer en ajoutant "Content-type"ou "Accept"aux en-têtes.

[
  [
    1, 
    2, 
    3
  ], 
  [
    4, 
    5, 
    6
  ]
]

J'espère que cela vous aidera.

Gabriel Samfira
la source
2
Salut Gabriel! Merci pour votre réponse! J'ai déjà mis en œuvre ce dont j'ai besoin selon mon plan. Fonctionne très bien! J'ai utilisé le sérialiseur pour une meilleure sortie json.
Zakhar
3
J'ai essayé de suivre cette suggestion mais j'obtiens: "Impossible d'appliquer DjangoModelPermissions sur une vue qui n'a .modelni .querysetpropriété.". J'ai essayé l'exemple exact fourni. Serait-ce quelque chose avec la version récente de django-rest-framework?
Orlando
Cet exemple a été écrit il y a quelque temps. Depuis, je n'ai plus eu la chance de travailler avec Django. mais vous pouvez trouver quelque chose d'utile ici: django-rest-framework.org/api-guide/routers
Gabriel Samfira
1
Cet exemple est exactement ce dont j'avais besoin pour mon besoin, c'est un service qui effectue certaines opérations sans sérialiseur non modèle!
Khalil TABBAL
@Orlando: Regardez ici comment implémenter une logique d'autorisation spécifique pour une vue non-modèle avec djang-restframework 3: stackoverflow.com/a/34040070/640916
djangonaut
-1

Dans urls.py, la fonction login_required nécessite

from django.contrib.auth.decorators import login_required
Ymorvan
la source
si ce n'est qu'un commentaire au lieu d'une réponse, pensez à utiliser la add a commentsession
lucascavalcante