I am relatively new to DRF, but found viewsets an amazing abstraction technique for writing RESTful API. I am having a hard time correlating Viewsets with REST methods though. Let's say I have a viewset for Userprofiles and somebody new creates a profile on client.
Should this send a PUT or a POST ?
Which url should this request go to, http://user or http://user/new_id ?
If I want this profile only accessible to the user or admin(all CRUD operations), then where should I handle the code for making it inaccessible to others ?
Should I create a new permission ? If yes, should I handle rest methods in has_permission/has_object_permission ?
I have gone through the tutorial, and know how permissions/mixins works, but I am not able to connect these dots here.
1/ In general, POST is for creating new, PUT is for updating. See the docs on the SimpleRouter to show how the various types of Http methods align with various actions in your Django backend.
2/ You'll find that different situations call for different routing methods. If yours is standard, you may want to use a SimpleRouter like the example above. In that case, creating a new user would be a POST request to /user/ and updating a user would be a PUT request to /user/{{user_id}}/.
3/ To limit access to various API methods, you want to use Permissions. It's possible that you could use one of DRF's Custom Permissions. I've found that in general, if you want only the user to access his/her own profile, it's easier to either use conditional logic within a view (i.e., return a DRF PermissionDenied exception in the view logic if the request.user.pk is not the pk of that REST url. More often than not, I've used the UserPassesTestMixin from Django Braces, that works pretty well to filter user permissions.
4/ I guess the Django Braces mixin above answers this question. You should include a test_func method if you're using Django Braces that returns True if you want to grant the user access, and False otherwise.
Hope this helps! I agree that these are difficult parts of DRF and perhaps some of these could more effectively get incorporated into the source. Good luck!
Related
guys!
I'm doing a test that asks me to make an API and I don't have experience with that.
The test says:
"In order for us to know who the debtors are, the status of their credit recovery and
more information, create an API to save and provide information about the cases and
the people involved(credtors and debtors)."
I'm confusing about the "to save" part. Do I have to make routes to save new creditors, debitors and cases, or the admin panel of Django is responsible for that and I have to just create routes to return information about the cases, creditors and debitors in json?
Yes, you should create a route which allows to make a POST request. You can save new creditors, debitors and cases to your model by this.
Also you can create a route which makes a GET request, you can get list creditors, debitors and cases informations and their single object details.
You can do this easily by Django Rest Framework.
DRF quick start project helps you to understand. For details: https://www.django-rest-framework.org/tutorial/quickstart/
To build your own project(custom models, serializers, views, etc.) you can follow tutorial series: https://www.django-rest-framework.org/tutorial/1-serialization/
Goal
Is to disable OPTIONS method globally.
Background
According to the official Django REST docs (https://www.django-rest-framework.org/api-guide/metadata/), the proper way to do it is to set DEFAULT_METADATA_CLASS to None.
This resolves the problem. After trying to send the OPTIONS curl request, the server responds with the 405.
Problem
However the API Browser would still show methods under Allow that are actually not allowed:
Question
How to hide not-supported methods under Allow in Django API Browser?
After checking the Disable a method in a ViewSet, django-rest-framework, it turned out there are at least 3 good approaches to address this:
Use Specific ViewSets instead of just inheriting ModelViewSet
Overwrite _allowed_methods()
Define http_method_names()
It was decided to:
Set DEFAULT_METADATA_CLASS to None (to make sure non-defined methods are not exposed).
Define http_method_names for each ViewSet (to hide them in the Browser API).
The django rest framework does allow per view authentication schemes:
http://www.django-rest-framework.org/api-guide/authentication/#setting-the-authentication-scheme
I think this is handy and I ask myself why this gets reinvented in the django rest framework.
"Why does django not provide this?" is a question which would get closed here on stackoverflow soon.
That's why I ask:
How to get per view authentication schemes in django?
"Why does django not provide this?"
It's an opinion. I don't see why someone should limit a specific view to some authentication backends. Whatever my/your opinion is, the decision is made in Django/DRF. Contact their mailing list and search their bug repo to find out why a specific decision made.
How to get per view authentication schemes in django?
Before anything be sure you know how Django works. Read all authentication related documents like this and this.
You could go the DRF way: Completely ditch Django authentication and write it yourself. Replace Django authentication backend abstract with your desired one and make a way to configure each view (e.g. Use a decorator to set attributes on you view function or use class based views + some fields). Then add a middleware to authenticate users and replace Django's AuthenticationMiddleware to provide request.user. You should also make sure that things like login_required work without modification or you should provide an alternative.
I have a django app that is not live yet and I just got a request to use API and i ended up selecting DRF. But am really lost on some of its components. I was able to login, get token and pass the token to get something. It will use angular.as on front end now, which needs all to be JSON.
While i will rewrite the views, i want to reuse as much as possible especially modules. E.g. I have a validation that validates registration of forms (it used from three different views because a member can be registered in three different ways with some conditions applied i.e. self-registration, company registration and company-agent registration).
def validate_member(data,editing=False,directregistration=True):
#data is passsed from forms.py. Return any error message and the cleaned data to the calling forms.py
return [errmsg,data]
forms.py clean method example:
class NewMemberForm(forms.Form):
firstName=forms.CharField(max_length=25,required=True,widget=forms.TextInput())
def clean(self):
self.cleaned_data=super(NewMemberForm,self).clean()
validate_member_form=validate_member(self.cleaned_data,directregistration=True)
#do we have error messages now?
if len(validate_member_form[0])>0: #yes we have error messages
raise forms.ValidationError(_(validate_member_form[0]),code='invalid')
return validate_member_form[1]
and my views.py:
if request.method=='POST':
err_found=False
go_id=''
form=NewMemberForm(request.POST or None)
if form.is_valid():
#create user
pass
I see forms.py is useless in DRF APIView (i failed to get an example at least). How do I convert such an app without so much pain?
Here's the thing - you can't (at the very least not easily). DRF programmers have decided to implement serializers as completely different still parallel structure from django forms. Infact they don't even share any ancestory. You can certainly share code in forms and serializers using mixins, but then again there are subtle differences like forms have clean methods, while serializers have validate ones.
Here are the few strategies that can be adopted to minimize the code repetition -
Skinny views and fat models - Try to keep most of the business logic either in models (if they are related to models), or in separate domain classes. Views should just be a connection between your models and templates, and shouldn't contain business or model related logic. This way the code you write can be reused among django's normal views and DRF views. If you want to take this a little further, try to encapsulate global validations and constraints (i.e. the validations that always holds true) at model level, and then you can write certain serializer mixins that catch validation error from models and display it to user.
Service Oriented Architecture - Try to write everything as services or REST APIs, and use those services in your normal views. My company had certain performance concerns, so I end up writing a custom request client (on the lines of django test client) that doesn't actually make an http request to an api, but still provides adequate abstraction. This approach might seem lucrative at first, but in long term it creates really messy code. Infact if you are willing to go to such extents, I'd recommend the next approach.
Frontend framework - Provided you don't have certain SEO constraints, then you can certainly move to frontend javascript framework based architecture. Then you only need to write DRF APIs, and all the other code should be handled by client framework. It also accounts for better user experience.
What is the proper way to post json to Django? I have tried to use views, but I am not certain how to handle csrf. Is there another way to bypass views and simply accept a post of json?
Views are what handle the post data. There is no concept of "bypass views" because that is where the work of processing a request is done.
This is probably what your are looking for:
https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
This shows you how to handle csrf tokens with ajax (namely by using cookies).
I also might suggest you slow down and try to work through the tutorial found here:
https://docs.djangoproject.com/en/dev/intro/tutorial01/
You will likely have an easier time with django if you undertstand how the pieces (Models, Views, Templates, urls, Forms, etc) fit together.
Since you've added that these are API calls the simplest thing to do would be to mark these views as csrf_exempt. Additionally, as you might guess creating an API from models is a common task (I'm assuming that your API maps to models as that's the common case and you haven't specified) you may want to not reinvent the wheel and instead use piston or tastypie to make this easier on you: http://djangopackages.com/grids/g/api/
Use the #csrf_exempt decorator on any API views.