For some reason, I'm getting some weird problems with sending a POST request.
This is my URL settings:
http://host/api/user/1/edit/
http://host/api/address/search/
Where /api/ is the API-Root, user is a model, 1 is user ID, and edit and search are custom functions.
This is inside views.py:
class UserViewSet(viewsets.ModelViewSet):
...
def post(self, request, pk, *args, **kwargs):
... (processing)
#detail_route(methods=['post'])
def edit(self, request, pk, *args, **kwargs):
... (processing)
class AddressViewSet(viewsets.ModelViewSet):
...
def post(self, request, *args, **kwargs):
... (processing)
#detail_route(methods=['post'])
def search(self, request, *args, **kwargs):
... (processing)
This is inside urls.py:
router = DefaultRouter()
router.register(r'user', views.UserViewSet)
router.register(r'address', views.AddressViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^authentication/', include('rest_framework.urls', namespace='rest_framework'))
]
The strange thing I'm encountering is this:
If I'm using httpie and sending a POST edit request like this:
http POST http://host/api/user/1/edit/ name="john" address="google.com"
Then the stuff inside def post will never get executed. This is the same if I make the browser send a POST request.
However, if I send a POST search request by doing:
http POST http://host/api/address/search/ name="john"
Then in this case the stuff inside def search will never get executed, while only the stuff inside def post will get executed.
The only difference I can see is that for edit, there's an extra pk (value of 1 for this example), while there are no pk for `search.
Does anyone know why this is happening? It's really confusing for me
Defining post() in a ViewSet doesn't do anything, this is for APIView derived classes. If you want to override default object creation in a ViewSet you can either
def create(self, request, *args, **kwargs):
... do stuff ...
or even better
def perform_create(self, serializer):
... do stuff ...
serializer.save()
As for the address, you need to use list_route decorator instead of detail_route. detail_route is for operating on an individual object and list_route is for lists. So /address/search/ should be list route and /address/1/search/ would be a detail_route. Mind you that I don't think your code in post() runs in either case.
Here is a relevant piece of docs http://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing
When submitting a post request to rest framework the request is mapped to methods on the ViewSet, unlike Django views that simply call the post method.
When posting to user/{pk}/ your request is mapped to the UserViewSet.create method. A post request to user/{pk}/edit/ maps to your UserViewSet.edit method. In both cases, UserViewSet.post is not called.
It seems that when you post to a custom url that doesn't exist the post method is called instead (probably as a fallback). In your case address/search/ isn't a valid url, and instead you've defined an endpoint as address/{pk}/search/.
If you want to access address/search/ you need to update your search method to use a list_route decorator instead of detail_route:
class AddressViewSet(viewsets.ModelViewSet):
...
#list_route(methods=['post'])
def search(self, request, *args, **kwargs):
... (processing)
Hope this helps to clear things up for you.
Related
i have a django project with 50 url (in 1 application).
i would set a custom middleware for 20 urls.
when add middleware to MIDDLEWARE_CLASSES , this set for all urls .
How should this be done?
(i use last version of python and djnago)
You can utilize decorators, view's decorators behave similarly to middlewares, simple ex:
from functools import wraps
def custom_view_decorator(view_function):
#wraps(view_function)
def wrap(request, *args, **kwargs):
# Any preprocessing conditions..etc.
return view_function(request, *args, **kwargs)
return wrap
Usage in views.py:
#custom_view_decorator
def my_view(request, *args, **kwargs):
# view code, return response
you can either add a check condition to your middleware which is not recommended(since you cannot determine whether this middleware is working or not without reading it's code). or create a decorator for those views, decorator job is exactly this.
for starters you could do something like this:
class BlockUrlsMiddleware(MiddlewareMixin):
REDIRECT_URL = reverse("not_allowed")
WHITELISTED_URLS = [
"/your/custom/views/urls",
"/your/custom/apis/urls",
]
def process_request(self, request):
if request.path in self.WHITELISTED_URLS:
return None
else:
return HttpResponseRedirect(self.REDIRECT_URL)
few points to Note:
return None means everything is good and serve this request.
return HttpResponseRedirect means URLs are accessed which are not allowed and block this request by redirecting it to your default URLs (that you need to set) in this case REDIRECT_URL.
I have a Serializer in my code like this
class SampleSerializer(serializers.ModelSerializer):
class Meta:
model = Model
and Viewset like this
class SampleViewSet(GenericAPIView):
serializer_class = SampleSerializer
def get(self, request, *args, **kwargs):
pass
def post(self, request, *args, **kwargs):
pass
def put(self, request, *args, **kwargs):
pass
I have url like this for this viewset
Url #1:
url(r'^sample/$', SampleViewSet.as_view())
This makes url for all methods I have in my viewset like get, post and put etc. I want to make separate url for my get method but using same serializer. This url will look like this
Url #2:
url(r'^sample/(?P<model_id>\d+)/$', SampleViewSet.as_view())
How can I do this using same Viewset and Serializer? When I write Url #2 in urls.py, Swagger shows me all three type (get, post and put) of methods for that Url.
You could use require_GET decorator from django.views.decorators.http for this, and use it in your URL config:
urlpatterns = [
url(r'^sample/$', SampleViewSet.as_view()),
url(r'^sample/(?P<model_id>\d+)/$', require_GET(SampleViewSet.as_view())),
]
for more fine tuning there is also a require_http_method decorator which receives allowed methods in its parameters, e.g.:
url(r'^sample/(?P<model_id>\d+)/$', require_http_method(['GET', 'DELETE'])(SampleViewSet.as_view()))
See https://docs.djangoproject.com/en/dev/topics/class-based-views/intro/#decorating-in-urlconf for details.
Why don't you inherit ViewSet from viewsets.ViewSet and map your urls view DefaultRouter?
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'sample', SampleViewSet)
urlpatterns = router.urls
It will handle all urls for your. /sample/:id now will be available for GET, PUT and DELETE methods.
Also if it plain CRUD for your Sample model, there is a better solution to use a viewsets.ModelViewset.
I am using djang-braces
I would like to use a different login_url for each mixin called on a view.
for instance I would like all LoginRequiredMixin calls to redirect to 'accounts/login' and all MultiplePermissionsRequiredixin calls to redirect to 'accounts/permissions'.
As the 2 mixins are subclassed from AccessMixin setting the 'login_url' on a view will affect both mixins, I cannot see how I can specify a redirect for each Mixin.
I currently have views like this:
class View(LoginRequiredMixin, MultiplePermissionsRequiredMixin, View):
permissions = {
"all": (<modelmethods>),
}
login_url = '/accounts/permissions'
def get(self, request, *args, **kwargs):
cool view stuff
def post(self, request, *args, **kwargs):
cool view stuff
but of course this redirects both mixins to '/accounts/permissions'
I am wanting to achieve something like:
class View(LoginRequiredMixin, MultiplePermissionsRequiredMixin, View):
permissions = {
"all": (<modelmethods>),
}
Login_Redirect = '/accounts/login'
MultiplePermissions_Redirect = '/accounts/permissions'
def get(self, request, *args, **kwargs):
cool view stuff
def post(self, request, *args, **kwargs):
cool view stuff
I'm not sure if I understood well, but one solution I can think is:
Set login_url to "/accounts/login"
override check_permissions method, and set login_url to "/accounts/permissions" inside it.
This way, if login check fail it never reaches check_permissions (I suppose), and if it fallback to check_permissions you change the redirect url, so it goes to the page you want.
thought I never tried it.
Here is my code. I was just trying to implement this but could not.I am in a stage of leaning Django
class BCD(View):
def start(self):
return HttpResponse("Huray Finally I called")
urls.py
urlpatterns = patterns('',
url(r'^login1/$',BCD.as_view(),)
And even i tried
urlpatterns = patterns('',
url(r'^login1/$',BCD.start.as_view(),)
It throws error.I have read the docs of django methods such as get and post ,head can be written they will be called directly according to the request made.But what I is need to make my own methods in class based views
Thanks in Advance
You need to call methods in get(), post() or whatever the HTTP request is. There isn't a start HTTP request so Django won't call that.
You can do the following in your view:
def get(self, request, *args, **kwargs):
return self.start()
This return the result of self.start() whenever you visit your view with a HTTP GET request.
I want a way to automatically route GET and POST requests to subsequent methods in a centralized way.
I want to create my handler in the following way.
class MyHandler(BaseHandler):
def get(self):
#handle get requests
def post(self):
#handle post requests
This is what webapp2 does and I very much like the style, is it possible to do in Django?
I also want the view in Class-method style. What kind of BaseHandler and router should I write.
HINT: Use django generic views.
This is supported in Django as class based views. You can extend the generic class View and add methods like get(), post(), put() etc. E.g. -
from django.http import HttpResponse
from django.views.generic import View
class MyView(View):
def get(self, request, *args, **kwargs):
return HttpResponse('This is GET request')
def post(self, request, *args, **kwargs):
return HttpResponse('This is POST request')
The dispatch() method from View class handles this-
dispatch(request, *args, **kwargs)
The view part of the view – the
method that accepts a request argument plus arguments, and returns a
HTTP response.
The default implementation will inspect the HTTP method and attempt to
delegate to a method that matches the HTTP method; a GET will be
delegated to get(), a POST to post(), and so on.
By default, a HEAD request will be delegated to get(). If you need to
handle HEAD requests in a different way than GET, you can override the
head() method. See Supporting other HTTP methods for an example.
The default implementation also sets request, args and kwargs as
instance variables, so any method on the view can know the full
details of the request that was made to invoke the view.
Then you can use it in urls.py -
from django.conf.urls import patterns, url
from myapp.views import MyView
urlpatterns = patterns('',
url(r'^mine/$', MyView.as_view(), name='my-view'),
)