2013-02-12 10 views
18

Ich versuche zu ermitteln, die beste Möglichkeit zum Hinzufügen eines Root-Elements zu allen JSON-Antworten mit Django und Django-Rest-Framework.Hinzufügen von Root-Element zu JSON-Antwort (Django-Rest-Framework)

Ich denke, eine benutzerdefinierte Renderer Zugabe ist der beste Weg zu erreichen, was ich erreichen will, und das ist, was ich mit so weit gekommen sind:

from rest_framework.renderers import JSONRenderer 

class CustomJSONRenderer(JSONRenderer): 
#override the render method 
def render(self, data, accepted_media_type=None, renderer_context=None): 
    #call super, as we really just want to mess with the data returned 
    json_str = super(CustomJSONRenderer, self).render(data, accepted_media_type, renderer_context) 
    root_element = 'contact' 

    #wrap the json string in the desired root element 
    ret = '{%s: %s}' % (root_element, json_str) 

    return ret 

Der schwierige Teil nun dynamisch die root_element Einstellung basiert auf der Ansicht, dass render() von aufgerufen wird.

Alle Hinweise/Ratschläge wäre sehr dankbar,

Beifall

Antwort

13

Für die Nachwelt unten ist die endgültige Lösung. Es ist leicht vom Original gewachsen, da es nun auch paginierte Ergebnisse neu formatiert.

Auch sollte ich zuvor angegeben haben, dass der Grund für das JSON-Root-Element für die Integration mit einer Ember-Front-End-Lösung ist.

Serializer:

from rest_framework.serializers import ModelSerializer 
from api.models import Contact 

class ContactSerializer(ModelSerializer): 
    class Meta: 
     model = Contact 
     #define the resource we wish to use for the root element of the response 
     resource_name = 'contact' 
     fields = ('id', 'first_name', 'last_name', 'phone_number', 'company') 

Renderer:

from rest_framework.renderers import JSONRenderer 

class CustomJSONRenderer(JSONRenderer): 
    """ 
     Override the render method of the django rest framework JSONRenderer to allow the following: 
     * adding a resource_name root element to all GET requests formatted with JSON 
     * reformatting paginated results to the following structure {meta: {}, resource_name: [{},{}]} 

     NB: This solution requires a custom pagination serializer and an attribute of 'resource_name' 
     defined in the serializer 
    """ 
    def render(self, data, accepted_media_type=None, renderer_context=None): 
     response_data = {} 

     #determine the resource name for this request - default to objects if not defined 
     resource = getattr(renderer_context.get('view').get_serializer().Meta, 'resource_name', 'objects') 

     #check if the results have been paginated 
     if data.get('paginated_results'): 
      #add the resource key and copy the results 
      response_data['meta'] = data.get('meta') 
      response_data[resource] = data.get('paginated_results') 
     else: 
      response_data[resource] = data 

     #call super to render the response 
     response = super(CustomJSONRenderer, self).render(response_data, accepted_media_type, renderer_context) 

     return response 

Paginierung:

from rest_framework import pagination, serializers 

class CustomMetaSerializer(serializers.Serializer): 
    next_page = pagination.NextPageField(source='*') 
    prev_page = pagination.PreviousPageField(source='*') 
    record_count = serializers.Field(source='paginator.count') 

class CustomPaginationSerializer(pagination.BasePaginationSerializer): 
    # Takes the page object as the source 
    meta = CustomMetaSerializer(source='*') 
    results_field = 'paginated_results' 
+0

Dank ever.wakeful für eine Idee, ich schrieb auch einen Beitrag darüber, wie ich es mit ExtJs Frontend verwenden (Dort muss ich das Wurzelelement nicht anders benennen. Und ich muss paginator.count im Wurzelelement haben) http://kaygorodov.github.io/2014/02/21/extjs-django-rest-framework -root-element.html –

5

Credit ever.wakeful für mich 95% des Weges dorthin zu gelangen.

Persönlich wollte ich Metadaten zu jeder API-Anfrage für ein bestimmtes Objekt hinzufügen, unabhängig davon, ob es paginiert wurde oder nicht. Ich wollte auch einfach ein Diktat-Objekt übergeben, das ich manuell definiert habe.

Gezwickt Individuelle Renderer

class CustomJSONRenderer(renderers.JSONRenderer): 
    def render(self, data, accepted_media_type=None, renderer_context=None): 
     response_data = {} 
     # Name the object list 
     object_list = 'results' 
     try: 
      meta_dict = getattr(renderer_context.get('view').get_serializer().Meta, 'meta_dict') 
     except: 
      meta_dict = dict() 
     try: 
      data.get('paginated_results') 
      response_data['meta'] = data['meta'] 
      response_data[object_list] = data['results'] 
     except: 
      response_data[object_list] = data 
      response_data['meta'] = dict() 
      # Add custom meta data 
      response_data['meta'].update(meta_dict) 
      # Call super to render the response 
      response = super(CustomJSONRenderer, self).render(response_data, accepted_media_type, renderer_context) 
     return response 

Eltern Serializer und anzeigen Beispiel

class MovieListSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = Movie 
     meta_dict = dict() 
     meta_dict['foo'] = 'bar' 

class MovieViewSet(generics.ListAPIView): 
    queryset = Movie.objects.exclude(image__exact = "") 
    serializer_class = MovieListSerializer 
    permission_classes = (IsAdminOrReadOnly,) 
    renderer_classes = (CustomJSONRenderer,) 
    pagination_serializer_class = CustomPaginationSerializer 
    paginate_by = 10 
+0

Ist es möglich, dieses Meta_Dict zu verwenden, um einen Wert aus Sicht zu übergeben, möchte ich request.user in Meta_Dict übergeben –

Verwandte Themen