How to get the param PK in django restframework permission? - python

I have this permission in Django that look like this:
class PermissionName(BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in SAFE_METHODS:
return True
#I want to extract the pk or id params in here.
return False
Can anybody help me get the params of request I tried using self.kwargs["pk"], request.POST['pk'] but they all return saying object has no attribute.

If pk is a parameter of the POST request, you can access it with request.POST.get('pk'). In your post, you are not using get(), so that could be the issue.
If the object is provided as an argument to the function, I'm assuming you want the ID of it. That can also be achieved with obj.id.

lets say your path is:
path('listings/get/category/<str:pk>/', endpoints.HousesWithSameCategory.as_view()),
Your request: api/listings/get/category/1/
In your class method view use:
def get_queryset(self):
print(self.kwargs)
This will return {'pk': '1'}
self.kwargs will return all your dynamic params

If you have only one pk in your url and you use has_object_permission then.
class PermissionName(BasePermission):
def has_object_permission(self, request, view, obj):
obj.pk # To get object id.
But what if you have more then two pk in your url
urls.py
path("buildings/<int:pk>/units/<int:unit_pk>/")
Then you can get pk's from view attribute.
class PermissionName(BasePermission):
def has_object_permission(self, request, view, obj):
print(view.kwargs)
Results:
{'pk': 13, 'unit_pk': 71}

Related

Why does `HyperlinkedRelatedField` in Django rest framework needs its view to return `request` paramater as context?

I'm new to DRF. While defining a HyperlinkedRelatedField in serializer class like this:
class JournalistSerializer(serializers.ModelSerializer):
articles = serializers.HyperlinkedRelatedField(view_name="article-
detail")
im getting the following error:
`HyperlinkedRelatedField` requires the request in the serializer
context. Add `context={'request': request}` when instantiating the
serializer.
when i add context={'request': request} in the related APIView class:
class JournalistListCreateAPIView(APIView):
def get(self,request):
journalist = Journalist.objects.all()
serializer = JournalistSerializer(journalist,many=True,context=
{'request':request})
return Response(serializer.data)
the HyperLink in the APIView works fine. But i dont understand why request has to be sent while instantiating the serializer. Please help me understand.
It does require request in context as it builds absolute URL
to be more concrete it is used get_url serializer method
def get_url(self, obj, view_name, request, format):
...
return self.reverse(view_name, kwargs=kwargs, request=request, format=format)

LoginRequiredMixin, How to get and use the redirect_field_name?

I don't know whether this is the correct way to use this, if not, please let me know:
I have a view protected with the mixin like this:
class ControlPanel_vw(LoginRequiredMixin, DetailView):
template_name = 'controlPanel.html'
login_url = '/login/'
redirect_field_name = 'next'
And my login view receiving like this:
class Login_vw(FormView):
form_class = EmailAndPassword_fr
success_url = '/'
template_name = 'login.html'
def get(self, request, *args, **kwargs):
self.next = request.GET.get('next')
return super(Login_vw, self).get(request, *args, **kwargs)
def form_valid(self, form):
formData_d = form.clean()
email_f = formData_d.get("email")
password_f = formData_d.get("password")
print self.next
return super(Login_vw, self).form_valid(form)
When I print self.next in the get method it works good.
I used the "self" before "next" with the intention to can use it from the other function "form_valid", so when the user do login I can send him to his last url.
But when I tried to print the self.next variable just to test, I get:
'Login_vw' object has no attribute 'next'
I don't know what I am doing wrong, can you tell me what and why? I don't have clear the use of the self between functions, thanks!
form_valid() is called from the FormViews post() method, which it runs only when there is POSTed form data.
You are saving self.next in the get() method which will not be called when there is a POST request - hence the value is not set when you try to access it in form_valid().
You don't need to split this logic between two methods anyway - why not just fetch the next GET parameter when you need it?
def form_valid(self, form):
formData_d = form.clean()
email_f = formData_d.get("email")
password_f = formData_d.get("password")
next = self.request.GET.get('next')
# Do something with next...
return super(Login_vw, self).form_valid(form)
I removed the get method and added the get_context_data instead:
def get_context_data(self, **kwargs):
context = super(Login_vw, self).get_context_data(**kwargs)
context['next'] = self.request.GET.get('next')
return context
and then in my tamplate form added this:
<input type="hidden" name="next" value="{{ next }}" />
then within the form_valid method retrieve the "next" variable like this:
next = self.request.POST.get('next')
And thats it, in a brief explanation, get_context_data receives the next url, send it to the template form, and the template form sends it back to the form_valid method.

How to create 2 actions with same path but different HTTP methods DRF

So I'm trying to have different actions under the same method, but the last defined method is the only one that work, is there a way to do this?
views.py
class SomeViewSet(ModelViewSet):
...
#detail_route(methods=['post'])
def methodname(self, request, pk=None):
... action 1
#detail_route(methods=['get'])
def methodname(self, request, pk=None):
... action 2
The most rational method I've found here:
class MyViewSet(ViewSet):
#action(detail=False)
def example(self, request, **kwargs):
"""GET implementation."""
#example.mapping.post
def create_example(self, request, **kwargs):
"""POST implementation."""
That method provides possibility to use self.action inside of another viewset methods with correct value.
Are you trying to have actions based on HTTP request type? like for post request execute action 1 and for get request execute action 2? If that's the case then try
def methodname(self, request, pk=None):
if request.method == "POST":
action 1..
else
action 2..

How to pass more than one variables to modelViewSet in django rest framework?

I am using http://www.django-rest-framework.org/
I have the scenario where I want to pass two or more variables based on that I need to fetch data from database. In the following code only pk is there which I want to replace with two other fields in database.
Also please suggest how can I write my urlconfig the same.
Views.py
class ExampleViewSet(viewsets.ReadOnlyModelViewSet):
model = myTable
def list(self, request):
queryset = myTable.objects.all()
serializer = mySerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = myTable.objects.all()
s = get_object_or_404(queryset, pk=pk)
serializer = mySerializer(s)
return Response(serializer.data)
Serializer.py
class Serializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = myTable
fields = ('attr1', 'attr2', 'attr3')
Here is how you would do it with the recent Django REST Framework.
Assuming your variables are in the resource URLs like so:
GET /parent/:id/child/
GET /parent/:id/child/:id/
Then:
urls.py:
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'parent/(?P<parent_id>.+)/child', views.ExampleViewSet)
urlpatterns = router.urls
views.py:
class ExampleViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = Serializer
def get_queryset(self):
parent = self.kwargs['parent']
return myTable.objects.filter(parent=parent)
Where the 'parent' in the queryset part is your parent object. You may need to adjust it a little, of course, but the idea is encapsulated in the kwargs.
This solution will also save you a little code and you can make it into a full blown ModelViewSet just by subclassing it.
Hope that helps.
More here: DRF Filtering against the URL.
Here is an example of how you might implement what you want:
class ExampleViewSet(viewsets.ReadOnlyModelViewSet):
# This code saves you from repeating yourself
queryset = myTable.objects.all()
serializer_class = mySerializer
def list(self, request, *args, **kwargs):
# Get your variables from request
var1 = request.QUERY_DICT.get('var1_name', None) # for GET requests
var2 = request.DATA.get('var2_name', None) # for POST requests
if var1 is not None:
# Get your data according to the variable var1
data = self.get_queryset().filter(var1)
serialized_data = self.get_serializer(data, many=True)
return Response(serialized_data.data)
if var2 is not None:
# Do as you need for var2
return Response(...)
# Default behaviour : call parent
return super(ExampleViewSet, self).list(request, *args, **kwargs)
def retrieve(self, request, *args, **kwargs):
# Same for retrieve
# 1. get your variable xyz from the request
# 2. Get your object based on your variable's value
s = myTable.objects.get(varX=xyz)
# 3. Serialize it and send it as a response
serialized_data = self.get_serializer(s)
return Response(serialized_data.data)
# 4. Don't forget to treat the case when your variable is None (call parent method)
As for the urlconf, it depends on how you want to send your variables (get, post or through the url).
Hope this helps.
urls.py
url(
regex=r'^teach/(?P<pk>\d+?)/(?P<pk1>\d+?)/$',
view=teach_update.as_view(),
name='teach'
)
Templates
<td><a href="/teach/{{tid}}/{{i.id}}"><button type="button" class="btn
btn-warning">Update</button></a></td>
Views.py
class teach_update(view):
def get(self,request,**kwargs):
dist=self.kwargs['pk']
ddd=self.kwargs['pk1']

403 with django's class based view

So this is a simple view that I have written.
class PostTestView(View):
def post(self, request, *args, **kwargs):
print request.POST
return HttpResponse("Hello there")
my urls.py has this line for the above view :
url(r'^test/create$',PostTestView.as_view(), name='app.views.create_test')
But I get an 405 Http error when I try to hit http://127.0.0.1:8000/app/test/create
This apparently means that my method post is not in the defined methods list . But I have defined it as above in my view.
What could possibly be wrong here ? I am clueless
Try defining the get method.
The "post" method is commonly used in forms, but when you just point your browser to an url the used method is "get"

Categories

Resources