Using APIs within Django and how to display the data - django

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 }}.

Related

DJANGO VIews: how can I add another view already developed to a new one?

I have written a view ("nos_agences" view) in Django which works fine. I would like to add this view to another one in the developing process.
The new one includes "nos_agences" view plus other attributes to write.
the existing view
def nos_agences (request):
""""display nos agences"""
try:
agence = Agence.objects.all()
except Agence.DoesNotExist:
raise Http404
return render(request, 'visitor/nos_agences.html', {'agences':agence})
path('nos_agences',views.nos_agences, name= 'nos_agences'),
The new one which must include nos_agences:
def reservation (request):
return render(request, 'visitor/reservation.html')
Use HTTP request method (GET, POST etc.) and in your views.py check the request is GET or POST.
If HTTP request method is GET then call one view, if HTTP request method is POST then call another view.

Calling Class Based View (POST) method without going through url.py

We are developing an application backend API end points using Django Rest Framework with class based view approach.
have an end point like BASE_URL/api/v1/users/posts/ , when called with post method all it does is saves (posts data, ex: {"post_id" : "1", "post_content" : "some_text" ,"posted_username": "user_name"}) into back-end database.
The above end point will be processed by application urls.py file and will redirect to corresponding view to save the data.
I want co call the corresponding end point view class post method but not through regular end point [ requests.post(url,data,headers) ], I need to invoke this class post method from another python file (within the same application), without going through urls.py.
All I want to eliminate is network call. Please don't suggest saving directly to database by opening a connection to database, I want to save data through REST API end point only (class view post method only) but not actually calling end point.
Sample code:
class PosttView( mixins.CreateModelMixin, mixins.ListModelMixin, generics.GenericAPIView):
authentication_classes = []
permission_classes = []
serializer_class = PostSerializer
queryset = Posts.objects.all()
def post(self,request,*args,**kwargs):
return self.create(request,*args,**kwargs)

How to safely access request object in Django models

What I am trying to do:
I am trying to access request object in my django models so that I can get the currently logged in user with request.user.
What I have tried:
I found a hack on this site. But someone in the comments pointed out not to do it when in production.
I also tried to override model's __init__ method just like mentioned in this post. But I got an AttributeError: 'RelatedManager' object has no attribute 'request'
Models.py:
class TestManager(models.Manager):
def user_test(self):
return self.filter(user=self.request.user, viewed=False)
class Test(models.Model):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(Test, self).__init__(*args, **kwargs)
user = models.ForeignKey(User, related_name='test')
viewed = models.BooleanField(default=False)
objects = TestManager()
I trying to access request object in my Django models so that I can get the currently logged in user with request.user.
Well a problem is that models are not per se used in the context of a request. One for example frequently defines custom commands to do bookkeeping, or one can define an API where for example the user is not present. The idea of the Django approach is that models should not be request-aware. Models define the "business logic" layer: the models define entities and how they interact. By not respecting these layers, one makes the application vulnerable for a lot of problems.
The blog you refer to aims to create what they call a global state (which is a severe anti-patten): you save the request in the middleware when the view makes a call, such that you can then fetch that object in the model layer. There are some problems with this approach: first of all, like already said, not all use cases are views, and thus not all use cases pass through the middleware. It is thus possible that the attribute does not exist when fetching it.
Furthermore it is not guaranteed that the request object is indeed the request object of the view. It is for example possible that we use the model layer with a command that thus does not pass through the middleware, in which case we should use the previous view request (so potentially with a different user). If the server processes multiple requests concurrently, it is also possible that a view will see a request that arrived a few nanoseconds later, and thus again take the wrong user. It is also possible that the authentication middleware is conditional, and thus that not all requests have a user attribute. In short there are more than enough scenario's where this can fail, and the results can be severe: people seeing, editing, or deleting data that they do not "own" (have no permission to view, edit, or delete).
You thus will need to pass the request, or user object to the user_test method. For example with:
from django.http import HttpRequest
class TestManager(models.Manager):
def user_test(self, request_or_user):
if isinstance(request_or_user, HttpRequest):
return self.filter(user=request_or_user.user, viewed=False)
else:
return self.filter(user=request_or_user, viewed=False)
one thus has to pass the request object from the view to the function. Even this is not really pure. A real pure approach would only accept a user object:
class TestManager(models.Manager):
def user_test(self, user):
return self.filter(user=user, viewed=False)
So in a view one can use this as:
def some_view(request):
some_tests = Test.objects.user_test(request.user)
# ...
# return Http response
For example if we want to render a template with this queryset, we can pass it like:
def some_view(request):
some_tests = Test.objects.user_test(request.user)
# ...
return render(request, 'my_template.html', {'some_tests': some_tests})

Context Aware Browsable API Rendering in Django REST

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

Getting Google App Engine blob info in Django view

This is a follow up question for Django on Google App Engine: cannot upload images
I got part of the upload of images to GAE Blobstore working. Here's what I did:
In models.py I created a model PhotoFeature:
class PhotoFeature(models.Model):
property = models.ForeignKey(
Property,
related_name = "photo_features"
)
caption = models.CharField(
max_length = 100
)
blob_key = models.CharField(
max_length = 100
)
In admin.py I created an admin entry with an override for the rendering of the change_form to allow for insert of the correct action to the Blobstore upload url:
class PhotoFeatureAdmin(admin.ModelAdmin):
list_display = ("property", "caption")
form = PhotoFeatureForm
def render_change_form(self, request, context, *args, **kwargs):
from google.appengine.ext import blobstore
if kwargs.has_key("add"):
context['blobstore_url'] = blobstore.create_upload_url('/admin/add-photo-feature')
else:
context['blobstore_url'] = blobstore.create_upload_url('/admin/update-photo-feature')
return super(PhotoFeatureAdmin, self).render_change_form(request, context, args, kwargs)
As I use standard Django, I want to use the Django views to process the result once GAE has updated the BlobStore in stead of BlobstoreUploadHandler. I created the following views (as per the render_change_form method) and updated urls.py:
def add_photo_feature(request):
def update_photo_feature(request):
This all works nicely but once I get into the view method I'm a bit lost. How do I get the Blob key from the request object so I can store it with PhotoFeature? I use standard Django, not Django non-rel. I found this related question but it appears not to contain a solution. I also inspected the request object which gets passed into the view but could not find anything relating to the blob key.
EDIT:
The Django request object contains a FILES dictionary which will give me an instance of InMemoryUploadedFile. I presume that somehow I should be able to retrieve the blob key from that...
EDIT 2:
Just to be clear: the uploaded photo appears in the Blobstore; that part works. It's just getting the key back from the Blobstore that's missing here.
EDIT 3:
As per Daniel's suggestion I added storage.py from the djangoappengine project which contains the suggested upload handler and added it to my SETTINGS.PY. This results in the following exception when trying to upload:
'BlobstoreFileUploadHandler' object has no attribute 'content_type_extra'
This is really tricky to fix. The best solution I have found is to use the file upload handler from the djangoappengine project (which is associated with django-nonrel, but does not depend on it). That should handle the required logic to put the blob key into request.FILES, as you'd expect in Django.
Edit
I'd forgotten that django-nonrel uses a patched version of Django, and one of the patches is here to add the content-type-extra field. You can replicate the functionality by subclassing the upload handler as follows:
from djangoappengine import storage
class BlobstoreFileUploadHandler(storage.BlobstoreFileUploadHandler):
"""Handler that adds blob key info to the file object."""
def new_file(self, field_name, *args, **kwargs):
# We need to re-process the POST data to get the blobkey info.
meta = self.request.META
meta['wsgi.input'].seek(0)
fields = cgi.FieldStorage(meta['wsgi.input'], environ=meta)
if field_name in fields:
current_field = fields[field_name]
self.content_type_extra = current_field.type_options
super(BlobstoreFileUploadHandler, self).new_file(field_name,
*args, **kwargs)
and reference this subclass in your settings.py rather than the original.