Django Rest Framework how to post data on the browsable API - python

Im kind of new to Django Rest Framework. I know it is possible to post data using the Browsable API, I just don't know how. I have this simple view:
class ProcessBill(APIView):
def post(self, request):
bill_data = request.data
print(bill_data)
return Response("just a test", status=status.HTTP_200_OK)
When I go to the url that points to this view, I get the rest_framework browsable api view with the response from the server method not allowed which is understandable cause I am not setting a def get() method. But ... how can I POST the data? I was expecting a form of some kind somewhere.
EDIT
This is a screenshot of how the browsable API looks for me, it is in spanish. The view is the same I wrote above but in spanish. As you can see ... no form for POST data :/ .

Since you are new I will recommend you to use Generic views, it will save you lot of time and make your life easier:
class ProcessBillListCreateApiView(generics.ListCreateAPIView):
model = ProcessBill
queryset = ProcessBill.objects.all()
serializer_class = ProcessBillSerializer
def create(self, request, *args, **kwargs):
bill_data = request.data
print(bill_data)
return bill_data
I will recommend to go also through DRF Tutorial to the different way to implement your endpoint and some advanced feature like Generic views, Permessions, etc.

Most likely the user has read-only permission. If this is the case make sure the user is properly authenticated or remove the configuration from the projects settings.py if it is not necessary as shown below.
```#'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
#],```
Read more on permissions here.

Related

Make REST Framework require authentication for GET method

I'm working on an Django app that uses REST Framework together with Swagger. Also added some models, and one of them is called Example. Added some views based on mixins in views.py for the model previously mentioned.
In views.py, I've created two classes: ExampleList (that uses GET to get all the objects made out from that model and POST to add a new model) and ExampleIndividual, that uses methods such as individual GET, PUT and DELETE.
Anyways, this is how my ExampleList class looks like:
class ExampleList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
permission_classes = [IsAuthenticated]
queryset = ExampleModel.objects.all()
serializer_class = ExampleSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
In the settings.py file, in the REST_FRAMEWORK configuration, I've set:
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
]
Everything works fine at this moment. What I want to do, is, whenever I want to get the list of all objects from the Example model (access the get() method from the ExampleList() class, I want it to work only if I am authenticated (using the Authorize option from Swagger). If not, a status code like "FORBIDDEN" should be returned.
I tried using permission_classes = [IsAuthenticated] at the beginning of the method, but it didn't work. It seems that I can still GET all the objects without being authenticated into Swagger.
Any advice on how I can do that properly? Thank you.
Did you tried it with Token Authentication? With this method in each request made to the API, the token must be included in the Header Field Authorization. https://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication to test it in Browser you can install an extension called Mod Header https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj there you can include the token in the header field.

DjangoRestAPI - Using ModelViewSet to perform database management while running your own function (like a event handler)

I am used to using flask for apis and Django for databases and I am trying to learn the DjangoRestAPI. I followed the official Quickstart and another tutorial from medium and did a lot of research but I still struggle to understand how to implement my own function (eg: Doing some calculations or making a request to the Twilio api to send a message after receiving an api request) in a ModelViewSet. I have read that APIView can also be another option but it will making accessing the database quite difficult? When I run a print function in a ModelViewSet class in views.py, or a #action function, it prints but only once at the start of the programme when the server is starting. This is my version of the code in Flask and what I have tried in Django.
Which class should I inherit (APIView or Viewset or ModelViewSet and where can I look for more information about this?
Thank you very much for your kind help in advance: - please note that the flask implementation is a get request
#app.route('/api/upload/<uuid>/<major>/<minor>/<rssi>',methods=['GET'])
def beacon_info_upload(uuid,major,minor,rssi):
if 'uuid' in request.args:
uuid = str(request.args['uuid'])
if 'major' in request.args:
major = str(request.args['major'])
if 'minor' in request.args:
minor = str(request.args['minor'])
if 'rssi' in request.args:
rssi = str(request.args['rssi'])
qualify = send_twilio_message(uuid,major,minor,rssi)
return jsonify(uuid,major,minor,qualify)
Attempt in Django (views.py)
class beacon_occuranceViewSet(viewsets.ModelViewSet):
queryset = beacon_occurance.objects.all().order_by('from_location')
serializer_class = beacon_occuranceSerializer
print("event occ beacon occ views.py L32 success experiment") #only prints once at the beginning
#action(detail=True, methods=['post'])
def register(self, request, pk=None):
print("hello!!!!! L38") #only prints once at the beginning
beacon = self.get_object()
print(beacon)
class beacon_occuranceSerializer(serializers.HyperlinkedModelSerializer):
print("hello") #also only prints once when booting the server
class Meta:
model = beacon_occurance
fields = ('from_location','uuid','major','minor','rssi','time')
Solved - use
def create(self, request):
#do what you want - do access data- do request.data (its a dictionary)
return super().create(request)
refer to Custom function which performs create and update on DRF modelViewSet

Python Django RestFramework route trigger

I'am building an API using python 2.7 and django 1.7 and I'm facing a problem. I'm not an expert in Rest Framework but I understand the basic mechanisms.
I can resume my problem by giving you an example. I have a route lets say
/api/project/
Django Rest Framework provides me all basic operations for this route and I don't need to write them e.g:
POST, GET, PUT, DELETE => /api/project/
The fact is, I want to do some operation when I create a new project. I want to add the id of the user who has created the new project.
I want to add some kind of trigger/callback to the create function:
class ProjectViewSet(viewsets.ModelViewSet):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
def create(self, request, *args, **kwargs):
I want to keep the internal behavior of Rest Framework (I don't want to rewrite the route functions), but I want the route to do extra stuff and I need the request object in my trigger/callback. Something like
def callback(request, instance):
instance.created_by = request.user
instance.save()
Do you have any ideas?
Thank you.
You need to add creator_id field to your serializer as well as to model represented by the resource. Then you can do something like this in your view:-
import copy
class ProjectViewSet(viewsets.ModelViewSet):
...
def create(self, request, *args, **kwargs):
data = copy.deepcopy(request.data)
data['creator_id'] = request.user.id
request._data = data
return super(ProjectViewSet, self).create(request, *args, **kwargs)

Nested resources in Django REST Framework

I wish to implement my new API with a nested resource.
Example: /api/users/:user_id/posts/
Will evaluate to all of the posts for a specific user. I haven't seen an working example for this use case, maybe this isn't the right way for implementing rest API?
As commented by Danilo, the #link decorator got removed in favor of #list_route and #detail_route decorators.
Update: #detail_route & #list_route got deprecated in favor of #action.
Here's the alternate solutions:
Solution 1:
#detail_route()
def posts(self, request, pk=None):
owner = self.get_object()
posts = Post.objects.filter(owner=owner)
context = {
'request': request
}
post_serializer = PostSerializer(posts, many=True, context=context)
return Response(post_serializer.data)
Solution 2:
Try drf-nested-routers. Haven't tried this out yet, but looks promising, many are already using it. Looks like an advanced version of what we are already trying to achieve.
Hope this helps.
To map /api/users/:user_id/posts/ you can decorate a posts method inside your ViewSet with #link()
from rest_framework.decorators import link
from rest_framework.response import Response
class UserViewSet(viewsets.ModelViewSet):
model = User
serializer_class = UserSerializer
# Your regular ModelViewSet things here
# Add a decorated method like this
#link()
def posts(self, request, pk):
# pk is the user_id in your example
posts = Post.objects.filter(owner=pk)
# Or, you can also do a related objects query, something like:
# user = self.get_object(pk)
# posts = user.post_set.all()
# Then just serialize and return it!
serializer = PostSerializer(posts)
return Response(serializer.data)
As commented by Danilo Cabello earlier you would use #detail_route or #list_route instead of #link(). Please read the documentation for "Routers", section "Extra link and actions" and "ViewSets", section "Marking extra actions for routing" for detailed explanations.

Django RSS Feed Authentication

I am looking into adding RSS feeds to one of my Django apps and I would like to be able to have them be authenticated.
I want to use the new syndication framework in Django 1.2. I've read the docs on how to do this and have the basic feeds setup.
I am new to authenticating feeds, so I am not sure what the best approach to take is or what my options really are.
Each user has a unique sub domain and I would like the URL structure to look something like this: http://mysubdomain.mysite.com/myapp/rss/ if possible.
I don't want the feeds to be publicly available, is it possible to use the users username and password for the authentication? Have you found that most feed readers support this? If it's not possible to authenticate for each user, should I try to use a uuid to give them a unique url or is that not secure enough?
As you can probably tell I am not sure what direction to take with this, so any advice on the best way to do this would be very much appreciated.
Thanks
This is an old thread, but I recently encountered the same question. I solved it by overloading the __call__ method of the Feed object:
from django.http import HttpResponse
class ArticleFeed(Feed):
"snip [standard definitions of title, link, methods...]"
def __call__(self,request,*args,**kwargs):
if not request.user.is_authenticated():
return HttpResponse(status=401)
else:
return super().__call__(request,*args,**kwargs)
Have you tried wrapping the syndication view django.contrib.syndication.views.feed into a view that requires login? RSS feeds should normally be fetched over HTTP, so this should work!
# Import Django's standard feed view.
from django.contrib.auth.decorators import login_required
from django.django.contrib.syndication.views import feed
# Wrap it in a new feed view that requires authentication!
private_feed = login_required(feed)
Caveat: I've never tried this!
Edit!
To be safe with RSS readers that don't support redirection, return a HTTP 401 status code with the following:
authentication_url = '/accounts/login'
def feed_safe_login_required ( view ):
def _ ( request, *args, **kwargs ):
if not request.user.is_authenticated:
return HttpResponseNotAuthorized, authentication_url
return _
feed = feed_safe_login_required(django.contrib.syndication.views.feed)
Where HttpResponseNotAuthorized is as defined in this django snippet.

Categories