Context Aware Browsable API Rendering in Django REST - django

Is there an easy way to create hyperlinks in the Django Rest Browsable API, but not in the other API renderings. To be clear I would like to render certain fields as hyperlinks when viewing the page through the browsable API but to only render the text component when rendering through JSON.
An example of this use case is to render the pk in the list view as hyperlink to the detail view (similar to: http://chibisov.github.io/drf-extensions/docs/#resourceurifield) but to do this only when viewing the list view in browsable API mode. In regular json GET, I would like to render just the pk.
My hope is to make the browsable API more useable/navigable when accessing through a browser.
Is this in any way relevant: http://www.django-rest-framework.org/api-guide/renderers#browsableapirenderer?
More generally, is there anyway to set the excludes to be dependent on the rendering mode?

You can return different serializers in different context, by overriding the get_serializer method on GenericAPIView or any of its subclasses.
Something like this would be about right...
def get_serializer(self, ...):
if self.request.accepted_renderer.format == 'api':
# Browsable style
else:
# Standard style
If you code that behaviour as a mixin class you'd then be able to easily reuse it throughout your views.

I created this mixin to use the serializer_class_api when in API mode:
class SerializerAPI(object):
def get_serializer_class(self, *args, **kwargs):
parent = super(SerializerAPI, self).get_serializer_class(*args, **kwargs)
if (hasattr(self.request, 'accepted_renderer') and
self.request.accepted_renderer.format == 'api'):
return self.serializer_class_api
else:
return parent

Related

How to call a class based generic API view from a custom function based view

I am having some problems and doubts about how to call my own API within my app.
So I have an API to which I can send data. What I want to do in a different view is calling this sent data so I can visualize it in a template.
First I was trying to call the API with the library requests inside of my view. Even though that works I am having problems with authentication. So I was thinking I could call my class based API view from my custom function based view.
But I don't know if that is possible, nor do I know if that is recommendable. I was also thinking that it might be better to do that with javascript? I don't know.... So my question is twofold:
a) What is the best practice to call an API view/get API data from my own app so that I can manipulate and visualize it
b) If this is good practice, how can I call my class based generic API view from my custom function based view?
Here is what I am trying so far:
my generic view
class BuildingGroupRetrieveAPIView(RetrieveAPIView):
"""Retrieve detail view API.
Display all information on a single BuildingGroup object with a specific ID.
"""
authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication, BasicAuthentication]
serializer_class = BuildingGroupSerializer
queryset = BuildingGroup.objects.all()
my function based view with which I try to call it:
def visualize_buildings(request, id):
returned_view = BuildingGroupRetrieveAPIView.as_view()
return returned_view
my url
path('data/<int:pk>/', BuildingGroupRetrieveAPIView.as_view(),
name="detail_buildings_api"),
When I call my class based view I get AttributeError: 'function' object has no attribute 'get'
Help is very much appreciated! Thanks in advance!
What you can do if you want is to call your CBV after its declaration inside its file for the sake of easiness when declaring its URL.
views.py
class BuildingGroupRetrieveAPIView(RetrieveAPIView):
.....
visualize_buildings = BuildingGroupRetrieveAPIView.as_view()
Then on your URLs, you use that name.
urls.py
from . import views
path('data/<int:pk>/', views.visualize_buildings, name="detail_buildings_api"),
Correct way:
from django.url import reverse, resolve
def get_view_func(name_of_your_view):
return resolve(reverse(name_of_your_view)).func
# if your are in a drf view:
get_view_func()(request._request)
# if you are in normal django view:
get_view_func()(request)

Using APIs within Django and how to display the data

I am trying to test how to display API information within a view on my Django project. I know you may have to add some installed APIs into the settings INSTALLED APPS block.
This api is a simple geo one.
I am new to Django and new to using APIs within it. I have managed to get my app the way I need it using Youtube videos. But now I am on my own. I have many different view classes to display differents of my app.
The view below is the view Id like to place the data on.
is this how I would potentially do it? Then call {{ base }} within the HTHL to display it?
class PostDetailView(DetailView):
model = Post
template_name = 'clients/post_detail.html'
def api_test(request):
# This is where the APIs are going to go.
requests.get('https://api.coindesk.com/v1/bpi/currentprice.json')
data = response.json()
return render(request, 'clients/post_detail.html', {
'base': data['disclaimer']
})
I am currently getting no errors within my app, but the country element isnt displaying.
I have tested the following in just a simple python file
import requests
import json
response = requests.get('https://api.coindesk.com/v1/bpi/currentprice.json')
data = response.json()
print(data['disclaimer'])
which gets the desired result. So I guess now my issue is...how do i get this into the HTML? So i can display the results from the API
You can write like this:
class PostDetailView(DetailView):
model = Post
template_name = 'clients/post_detail.html'
def call_geo_api(self):
# This is where the APIs are going to go.
response = requests.get('https://api.coindesk.com/v1/bpi/currentprice.json')
data = response.json()
return data['disclaimer']
def get_context_data(self, *args, **kwargs):
context = super(PostDetailView, self).get_context_data(*args, **kwargs)
context['base'] = self.call_geo_api()
return context
Here, I have overridden get_context_data() method, which is responsible for sending context data from view to template.
Here I have changed your api method, so that it will return data['disclaimer'] from the API, and inside get_context_data method, I have injected it inside context. That should do the trick, so that you will be able to see data in template with {{ base }}.

Add extra context variables into django oscar emails

Trying to add extra context variables for all django oscar email templates. The only way I've got it to work is overriding specific views like ProfileUpdateView. This method seems very messy, I'd have to override a lot of files. Is there a better way of doing this?
From checking the source code, the ProfileUpdateView uses Django's Class Based View FormView, which in turn implements the get_context_data method allowing the injection of extra data in the view.
You can simply create a view inehiriting ProfileUpdateView and override the get_context_data:
class MyProfileUpdateView(ProfileUpdateView):
...
def get_context_data(self, **kwargs):
context = super(MyProfileUpdateView, self).get_context_data(**kwargs)
context['somevar'] = SomeQueryMaybe.objects.all()
return context
Ended up making a custom management command to do it manually since the need to change the emails templates would be really rare.

How to generate form in browsable API without using a serializer_class?

Lets assume I've an API that expects a couple GET args: 'foo' and 'bar'. And I haven't defined serializer_class due to it isn't tied to any specific model:
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
class myAPI(GenericAPIView):
def get(self, request, format=None):
foo = request.GET.get('foo')
bar = request.GET.get('bar')
# do something with foo & bar
return Response({'fooplusbar': _something(foo,bar)})
Is there any way to tell to djangorestframework to build the form in this kind of situation?
The Django REST Framework only supports forms out of the box for POST, PUT and PATCH requests.
In those cases, the form is generated from the serializer. There is nothing wrong with defining a serializer which does not return Model objects, nor with using the serializer for form display only, and write e.g. a custom POST handler which does not make use of it. But for Django REST Framework to show a form, you must define a serializer.
It would be quite possible to add support for proper GET forms, but you'd need to extend rest_framework.renderers.BrowsableAPIRenderer.render to add the new form to the context, and write your own templates in rest_framework/api.html.

How many actions should Django view process?

I looked through the Django built-in Auth app and noticed that views are split in many like:
password_reset,
password_reset_confirm,
password_reset_done,
password_reset_complete
Here every simple action has a distinct view. Should all the apps be written like that or is it fine for one view to manage more URLs?
The rule of thumb is that different URLs should be handled by different views.
Prior to the introduction of class-based views in Django 1.3, this could lead to a mess if your view function tried to handle many cases. But now you can create class-based views which allow you to subclass existing views and reuse behaviour of those views.
For example (for a site that has multiple games).
class GameView(View):
def game_method(self):
# compute things here
return data
class PuzzleGameView(GameView):
def get(self, request, *args, **kwargs):
data = self.game_method()
# do other things here
return HttpResponse("")
class SudokuGameView(PuzzleGameView):
pass
class ActionGameView(GameView):
pass
Methods defined in GameView can be called and reused in subclasses.