Is it okay to call django functions through an API? - django

I'm building a project and now I'm new to VueJS I'm currently learning it. and I found that you can make HTTP Requests on APIs using axios. And to make my project easy, Can I call functions on my views.py thru axios?
Like I'm fetching urls in urls.py to execute some functions on my backend.
Is it okay? I mean for security and best practices. etc.
Thanks

Absolutely ok, that's what Django is for:
urls.py:
urlpatterns = [
...
path('my_view_function/', views.my_view_function, name='my_view_function'),
...
]
views.py:
def my_view_function(request):
# unpack data:
my_value = request.GET['my_key']
# some logic:
...
# pack response:
response = json.dumps({
'some_other_key' : 'some_other_value'
})
return HttpResponse(response)

Another option is that you use signals in django, some time ago I used signals that for when a new record is created without a field, it will be completed with the algorithm you want, for example an order and automatically placed a code, you can apply it only by pointing to your models, when you want something more punctual.
#receiver(pre_save, sender=MyModel)
def my_handler(sender, **kwargs):
...
The my_handler function will only be called when an instance of MyModel is saved.
Here I leave the documentation in case you want to review it
https://docs.djangoproject.com/en/3.1/topics/signals/

Related

django-rest-swagger UI doesn't have form for POST request body (function based view)

I have this function-based view with django-rest-swagger decorated. However, I can't find place in UI that allow me to post the payload (request.body).
I saw a couple solutions about doing it with class-based view, but I was wondering if there is a way to do it with function-based view.
Thank you in advance!
#renderer_classes([JSONRender])
#api_view(['POST'])
def some_method(request):
body = json.loads(request.body)
return JsonResponse({'status': 'ok'})
I am gonna answer my question since the django-rest-swagger was deprecated in June 2019 and I just found out 2 feasible solutions.
First one will change the UI globally.
In ping.views (or any other location you wish) add following class.
from rest_framework.schema import AutoSchema
class CustomSchema(AutoSchema):
def __init__(self):
super(CustomSchema, self).__init__()
def get_manual_fields(self, path, method):
extra_fields = [
coreapi.Field('command', required=True, location='form', schema=String(), description='', type='', example='',
coreapi.Field('params', required=False, location='form', schema=String(), description='', type='', example='',
]
manual_fields = super().get_manual_fields(path, method)
return manual_fields + extra_fields
Add following settings in settings.py of your Django project.
REST_FRAMEWORK = {
# Corresponding path to where you added the class
'DEFAULT_SCHEMA_CLASS': 'ping.views.CustomSchema',
}
Second solution can be applied on a per-view basis. You may check here for official guide
Use #schema from rest_framework.decorators.schema to overwrite DEFAULT_SCHEMA_CLASS.
#api_view(['POST'])
#schema(CustomSchema())
def your_view(request):
print(request.body)
return JsonResponse({'task_status': 200'})
Basically, the idea is to overwrite DEFAULT_SCHEMA_CLASS. The word schema is the term that they used to refer to swagger UI for each view in rest_framework.
When you use #api_view() to decorate your function-based view, it will assign your function an attribute schema with value APIView.schema from rest_framework.views.APIView.
rest_framework.views.APIView will in further call DefaultSchema() to load the DEFAULT_SCHEMA_CLASS from your REST_FRAMEWORK configuration in settings.py.
Without other specifying, DEFAULT_SCHEMA_CLASS is rest_framework.schemas.openapi.AutoSchema by this official announcement. You might want to change it to rest_framework.schemas.coreapi.AutoSchema since it is the one that compatible with django_rest_swagger.
Hope this tutorial helps people who use django-rest-swagger (2.2.0) with function-based views for their Django project.
Please leave comments if there are anything I can help on this issue.

Django How to add user info to Sentry's report when an error happened?

I use the django-restful framework and I want to add user info to Sentry's report when an error happened in the ModelViewSet.
I find this doc of Sentry:
https://docs.sentry.io/enriching-error-data/context/?_ga=1.219964441.1220115692.1472094716%3F_ga&platform=python#capturing-the-user
It gives some code as follows:
from sentry_sdk import configure_scope
with configure_scope() as scope:
scope.user = {"email": "john.doe#example.com"}
But I can not figure out how to properly use it. I think there exists a better way than the following:
#list_route()
def fun_xxx(self, request, *args, **kwargs):
user = request.user
with configure_scope() as scope:
scope.user = {"id": user.id,......}
...some code may cause an error...
return Response({...})
Can anyone give me some suggestions? :)
As mentioned in the comments, the Django integration will attach this particular data automatically.
As for the question on how to generally add data in a Django app, you are basically looking for something to run before each view. A Django middleware suits this:
def sentry_middleware(get_response):
def middleware(request):
with configure_scope() as scope:
...
response = get_response(request)
return response
return middleware
https://docs.djangoproject.com/en/2.2/topics/http/middleware/
As mentioned here: https://docs.sentry.io/platforms/python/guides/django/#configure
Set:
# If you wish to associate users to errors (assuming you are using
# django.contrib.auth) you may enable sending PII data.
send_default_pii=True,
while configuring the sentry in Django.
PII: Personal Identifiable Information
P.S. Don't forget to include django.contrib.auth in your installed_apps.

How does interacting with the django rest api through the url work?

I get that the Django rest framework is for interacting with the Django server programmatically but one thing I still don't understand is how.what i want to do is have my client app (mobile app) send data (somehow) to the Django server in order to create/retrieve data based on variables and obviously this has to be done through the URL since there will be no direct GUI interaction with the API. (unless I'm mistaken, which I probably am) I have gone through the official documentation and followed the tutorial to the end and still don't understand how this is supposed to work.all I ask for is a quick and simple explanation because I have searched everywhere and haven't found a simple enough explanation to grasp the core concept of how this is all supposed to work.
I think what you're looking for is JSONResponse and related objects:
This will allow you to send JSON in response to a request.
from django.http import JsonResponse
def my_view_json(request):
response = JsonResponse({'foo': 'bar'})
return response
If your templates or webpages need to make a request to a view and specify different parameters, they can do so by adding POST variables (examples). These can be parsed in the view like so:
def myView(request):
my_post_var = request.POST.get('variable_name', 'default_value')
my_get_var = request.GET.get('variable_name', 'default_value')
You can then parse what was sent any way you like and decide what you want to do with it.
Basically,
You define the URLS upon which you perform Get/POST/PUT Requests and You can Send Data to that.
Eg:
urls.py
from django.conf.urls import url,include
from app import views
urlpatterns = [
url(r'^(?i)customertype/$',views.CustomerViewSet.as_view()),
url(r'^(?i)profile/$', views.Save_Customer_Profile.as_view()),
url(r'^(?i)customer_image/$', views.Save_Customer_Image.as_view()),
]
Now Whenever User would send a Request to:
example.com/profile ==> This would be received in the Save_Customer_Profile View based on the Method Type, Save_Customer_Profile is as follows:
class Save_Customer_Profile(APIView):
"""Saves and Updates User Profile!"""
def get(self, request, format=None):
return AllImports.Response({"Request":"Method Type is GET Request"})
def post(self, request, format=None):
return AllImports.Response({"Request":"Method Type is Post Request"})
def put(self,request, format=None):
return AllImports.Response({"Request":"Method Type is Put Request"})
I think the OP was referring to how to do GET/POST request programmatically. In that case, it is enough to do (values are dummy):
GET:
import requests
r = requests.get('http://localhost:8000/snippets/')
print(r.json())
print(r.status_code, r.reason)
POST:
data = {'code': 'print(" HELLO !!!")', 'language': 'java','owner': 'testuser'}
r = requests.post('http://localhost:8000/snippets/', data=data, auth=('testuser', 'test'))

Django: How to provide context to all views (not templates)?

I want to provide some context to all my function-based views (FBV) similar to the way TEMPLATE_CONTEXT_PROCESSORS (CP) provides context to all of one's templates. The latter doesn't work for me because I need that context prior to rendering the templates.
In particular, on my site I have a function which takes a request and returns the model for the Category of item in focus. My CP provides this for all templates, but I find myself making the same call from my FBV's and would like to remove this redundancy.
This question is similar but it presupposes the approach of accessing the output of the CP from the views. This seems hacky, and I'm not sure it's the best approach.
What's the Django way to do this?
Use Middleware...
class MyModelMiddleware(object):
def process_request(self, request):
request.extra_model = self.get_model(request.user)
Based on mwjackson 's answer and on docs, for Django 1.11, I think the middleware should be:
# middleware/my_middleware.py
class MyModelMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
# TODO - your processing here
request.extra_model = result_from_processing
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
In settings.py, add the path to your Middleware on MIDDLEWARE = () . Following the tips from this site, I had created a folder inside my app called middleware and added a new file, say my_middleware.py, with a class called, say, MyModelMiddleware. So, the path that I had added to MIDDLEWARE was my_app.middleware.my_middleware.MyModelMiddleware.
# settings.py
MIDDLEWARE = (
...
'my_app.middleware.my_middleware.MyModelMiddleware',
)

django rest post return 405 error code

I am using django with rest framework and I try to test POST on existing object but I keep getting 405. my ViewSet looks like this:
class Agents(viewsets.ModelViewSet):
serializer_class = serializer.AgentSerializer
model = serializer_class.Meta.model
....
and in the urls:
router = routers.SimpleRouter()
router.register(r'rest/agents', api_views.Agents, "Agent")
...
urlpatterns += router.urls
I call the post request from within APITestCase class (rest testing), my post request looks like this:
response = self.client.post(url, {'available': True, 'online':True}, format='json')
and printing url shows "/chat/rest/agents/1910_1567/", while 1910_1567 is the valid id of an existing agent (I create the agent during the setup and use its id).
I've seen other questions about rest post getting 405, but all the solutions there were url-related and in my case the url is correct. I even ran the setup outside the test and accessed the url via browser to get the object and the object indeed exist. but when I try to post to it - 405.
any ideas?
thanks!
Most probably your url is somehow matching with some different url's regex, and the request is being dispatched to some other view which disallows post request. Could you mention others urls in your urls.py? Infact you can verify this by adding pdb(debugger) in dispatch method of your view. If you made it to the pdb, then you can assume I was wrong.
If that is not the case, then you can evaluate the issue from dispatch method with debugger. Just in case you have any doubt about how to do that -
class Agents(viewsets.ModelViewSet):
serializer_class = serializer.AgentSerializer
model = serializer_class.Meta.model
def dispatch(self, *args, **kwargs):
import ipdb; ipdb.set_trace()
return super(Agents, self).dispatch(*args, **kwargs)
Solution Found:-
You are passing id in url, so this request would be routed to detail view and detail view only allow GET, PUT, DELETE operations on resource since resource already exists. So to create resource, don't provide the id. Otherwise use PUT request and provide support for creation in PUT.
POST are supposed to be for creation purpose. If you want to update with a ViewSet you'll need to PUT or PATCH (partial update) instead.
Edit:
For more about this, here's some explanation on the HTTP methods used for REST API:
http://restful-api-design.readthedocs.org/en/latest/methods.html
This is also described in the DRF documentation at:
http://www.django-rest-framework.org/api-guide/routers/#simplerouter