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)
Related
I am using ModelViewSet and Modelserializer for a blog like project.
It could be my difficulty in understanding the implementation; I can't get the update action to work via calling it through router, only the list action is working with the route I have defined.
When I put the url : 127.0.0.1:8000/api/blogs/1, to return the blog with ID 1 to edit, it returns {"Detail": "Not Found."}.
This is my view:
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
I have also overridden the save and update methods in the serializer class, don't know whether it was needed for ModelViewSet in ModelSerializer.
class ArticleSerializer(serializers.ModelSerializer):
def create(self, validated_data):
article = Article.objects.create(
article_title = self.validated_data['article_title'],
article_content = self.validated_data['article_content'],
...
)
return article
def update(self, instance, validated_data):
instance.article_title = validated_data.get('article_title', instance.article_title)
instance.article_content = validated_data.get('article_content', instance.article_content)
...
instance.save()
return instance
class Meta:
model = Article
fields = ...
And the urls.py file:
router = DefaultRouter()
router.register(r'blogs', ArticleViewSet, basename='articles-list')
urlpatterns = router.urls
My question is:
1. How do I specify urls for the ModelViewSet actions (in my case the update action)?
2. Will defining only one url suffice all my needs with every ModelViewSet actions? if so how?
What am I doing wrong? I'm new to DRF.
Regarding your questions:
1) Upon registering ModelViewSet in api router, it will create all required urls for the following actions. In your case it would be following:
list (GET request to /api/blogs/)
retrieve (GET request to
/api/blogs/{pk}/)
create (POST request to /api/blogs/)
update (PUT request to /api/blogs/{pk}/) (it will validate all fields of the model)
partial update (PATCH request to /api/blogs/{pk}/) (it will run no validation - you can send only
fields you've decided to change)
delete (DELETE request to /api/blogs/{pk}/)
So, basically router does most of the job for you about registering viewset actions.
2) I don't completely get it, but if my guess is correct - answer is the same as to first question.
About what you are doing wrong - I am not sure, but did you try appending slash at the end of your request (i.e not /api/blogs/1 but /api/blogs/1/)
Found the issue. I was trying the url localhost/api/blogs/1. It was returning this: "Detail": "Not Found".
It was because there were no instance saved with the id 1. All my saved intance had different ids which i didn't notice before. After putting available ids, it returned desired result.
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)
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 }}.
So this is my ViewSet:
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = (IsAuthenticated, IsLikeOrOwnerDeleteOrReadOnly,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user, location=self.request.user.userextended.location)
#detail_route(methods=['post'], permission_classes=[IsFromLoactionOrReadOnly])
def like(self, request, pk=None):
post = self.get_object()
post.usersVoted.add(request.user)
return Response(status=status.HTTP_204_NO_CONTENT)
and this is my URL / router:
router.register(r'posts', views.PostViewSet)
Now, when I go to this URL:
/posts
DRF sends all the posts and serializers (or so I think.. I don't have many posts yet so I'm assuming it sends all of them). What I want to do is I want to be able to limit the number of posts my ViewSet serializes to 10. The 10 objects I want to serialize depends on the page number which I want to force API users to send with the URL. For example, I want to force users to send a number with the URL like so:
/posts/x
and on the backend, I want to serialize posts with the pk x to x+9 (so if we assume x=1, then I want to serialize posts with pk=1, pk=2, pk=3... pk=10.). Is this possible with DRF? I'm guessing I use Pagination because when I read the documentation, it kind of looks like what I need but I can't fully wrap my head around what pagination exactly is and how I can use it to accomplish what I want. Here is the documentation for pagination: http://www.django-rest-framework.org/api-guide/pagination/
Thanks in advance.
You'll want to use the pagination controls that Django Rest Framework provides just like you guessed.
This snippit might help you wrap your head around it:
{
"count": 1023
"next": "https://api.example.org/accounts/?page=5",
"previous": "https://api.example.org/accounts/?page=3",
"results": [
…
]
}
It is showing a response that, in addition to the normal 'results' that you would expect also includes next and previous values. These are links to the end points that can be used to return the results for the previous page worth of data, or the next page worth of data. It is the front end's responsibility to map these links to the appropriate user controls so that the pagination navigation can occur.
You are right. You need Pagination to achieve this. Just include pagination_class like you wrote serializer_class etc. Also, create a class which will have the number, with which you wish to paginate.
from rest_framework.pagination import PageNumberPagination
class StandardResultsSetPagination(PageNumberPagination):
page_size = 10
page_size_query_param = 'page_size'
max_page_size = 1000
and set pagniation_class = StandardResultsSetPagination.
I am trying to call an api made with Django Rest Framework drf. My case is that I want to call the api from another view get the response and display the data in a template. I have referred to this SO post, but could not make a successful call since my viewset requires authentication. Viewset works perfect if called using urls, the default way. Viewset is as below
class ViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows Objects to be viewed or edited.
"""
permission_classes = (permissions.IsAuthenticated,)
queryset = Myclass.objects.all()
serializer_class = MyclassSerializer
....
....
return response(json)
On calling this api from another view the response that I get is a 401 page from drf api.
ipdb>view = Myview.as_view({'get':'list'}
ipdb>obj=view(request, *args, **kwargs)
ipdb> obj.data
{u'detail': u'Authentication credentials were not provided.'}
ipdb>obj.has_header('Authentication')
False
I tried passing Authentication headers also, but I dont know whether its the right way.
view = MyViewSet.as_view({'get':'list'},{'token':'token'})
This returned an error argument token is not accepted. And I tried
view = MyViewSet.as_view({'get':'list'},Authentication={'token':'token'})
But this Also returned me errors. How is it possible to call an api viewset from another view by passing auth params?
TIA