django rest framework find url kwarg in APIView - django-rest-framework

I have a url that looks like this:
url(r'^client_profile/address/(?P<id>.+)/$', views.ClientProfileAddressView.as_view())
And an APIView:
class ClientProfileAddressView(APIView):
renderer_classes = (JSONRenderer,)
permission_classes = (IsAuthenticated,)
def put(self, request):
....
def get(self, request):
....
In both put and get, I need to access the id url kwarg, the first one to update the object, the second to update it. How can I access the url argument in those methods?

This should work:
def put(self, request, *args, **kwargs):
id = kwargs.get('id', 'Default Value if not there')
def get(self, request, *args, **kwargs):
id = kwargs.get('id', 'Default Value if not there')

Related

DRF request.data issue with POST method

I'm using DRF to test a api,when i POST a data,DRF return status_code 500 and error 'MacList' object has no attribute 'data'. but the GET is not problem.
Views:
class MacList(mixins.ListModelMixin,mixins.CreateModelMixin,generics.GenericAPIView):
queryset = Testable.objects.all()
serializer_class = TestableSerializer
def get(self,request,*args,**kwargs):
return self.list(self, request, *args, **kwargs)
def post(self,request,*args,**kwargs):
return self.create(self, request, *args, **kwargs)
URLs:
path(r'api/maclist',views.MacList.as_view())
I'm checking the mixins.CreateModelMixin source code
class CreateModelMixin:
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data) **#### problem here**
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save()
def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}
if i use APIView to POST success.
class MacList(APIView):
def post(self,request):
serializer = TestableSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data,status=status.HTTP_201_CREATED)
return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST)
Please help me!
Django-->3.0.8
djangorestframework-->3.11.0
Don't need to pass derived class instance into inherited class method while calling it from derived class, when we use self python automatically pass this instance
It will works if you change your code to
.
.
def get(self,request,*args,**kwargs):
return self.list(request, *args, **kwargs)
def post(self,request,*args,**kwargs):
return self.create(request, *args, **kwargs)

DRF - How to get created object in CreateAPIView

My goal very much resembles to what has been asked in this question but from the perspective of DRF, rather than forms.
So basically the question is, how can I get the newly created object in the following code snippet:
TestSerializer(serializers.ModelSerializer)
class Meta:
fields = '__all__'
model = TestModel
class TestView(generics.CreateAPIView):
serializer_class = TestSerializer
def create(self, request, *args, **kwargs):
response = super(TestView, self).create(request, *args, **kwargs)
created_model_instance = .... ?
print(created_model_instance.id)
return response
You can override perform_create and use serializer.save to get the created object, like:
class TestView(generics.CreateAPIView):
serializer_class = TestSerializer
def perform_create(self, serializer):
"""Some doc here!"""
obj = serializer.save()
print(obj.id)

I'm trying to add a dynamic queryset to a model-form/formset field from a url kwarg <int:url_kwarg>, no luck so far

Edit: It appears that the problem I'm experiencing is directly related to the formset_factory() call. For some reason or other, I have been unable to access the kwargs from the view if I pass the form through the function.
I'm building a web app that utilizes high normalization in the data-structure. There are many many-to-many and one-to-many relationships to prevent excess null records and database-bloat. I want to add more entries to a model while excluding existing entries from the model.choice field.
my code looks like this:
the form:
class ExtraAddForm(forms.ModelForm):
def __init__(self, url_kwarg, *args, **kwargs):
super(ExtraAddForm, self).__init__(self, *args, **kwargs)
list_to_exclude = []
query_target = models.Model.objects.get(fk_id=url_kwarg)
for object in query_target:
list_to_exclude.append(object.fk_id.id)
new_queryset = models.Model.objects.exclude(fk_id__in=list_to_exclude)
self.fields['fk_id'].queryset= new_queryset
class Meta:
model = models.Model
fields= ['fk_id','field_b'}
the view:
class AddOjbectsView(FormView):
formset = formset_factory(ExtraAddForm(url_kwarg), can_delete=True)
model = models.Model
url_kwarg = 'url_kwarg'
form_class = formset
template_name = 'some-template.html'
extra_context = {'some_object': models.Model2.objects.all,
'model_object': models.Model.objects.all,
'formset': formset,
'view_type_create': True
}
def __init__(self, *args, **kwargs):
kwargs['url_kwarg']= self.kwargs.get(self.url_kwarg)
super().__init__(self,*args,**kwargs)
def get(self, request, *args, **kwargs):
request.session['url_kwarg'] = self.kwargs.get(self.url_kwarg)
return super().get(self, request, *args, **kwargs)
def post(self, request, *args, **kwargs):
#this works so I'm not re-typing it
def get_context_data(self, **kwargs):
"""Insert the form into the context dict."""
if 'url_kwarg' not in kwargs:
kwargs['url_kwarg'] = self.kwargs.get(self.url_kwarg)
return super().get_context_data(**kwargs)
#this works, but only in the get_context. Its not working as a solution to my problem.
def get_success_url(self):
#this works, not re-typing
My template has Javascript to handle multiple formsets, and I've tested it with a non-dynamic queryset. The only piece I'm having trouble with is taking the keyword argument from the URL and passing it to the form at init.
Have you tried using FormView.get_form_kwargs method?
Its described in docs
class AddOjbectsView(FormView):
def get_form_kwargs(self):
form_kwargs = super().get_form_kwargs()
form_kwargs['url_kwarg'] = self.kwargs['url_kwarg']
return form_kwargs
It would seem that the answer to my problem is to deprecate the process in favor of session variables at form validation, making use of the clean data functions. My methods above were generated from ignorance of the order of django operations, and shouldn't preoccupy anyone any further.

Access user object in decorator from request object for a logged in user

In my DRF app driven with APIView(), I want to add a single decorator. The decorator is:
from django.core.exceptions import PermissionDenied
from payment.models import Purchase
def client_has_paid(function):
'''
Has the client paid or have active subscription fee?
'''
def wrap(request, *args, **kwargs):
iccd = request.user.user_profile.iccd
filters = {'iccd': iccd , 'active': 1 }
try:
purchase = Purchase.objects.get(**filters)
return function(request, *args, **kwargs)
except:
raise PermissionDenied
wrap.__doc__ = function.__doc__
wrap.__name__ = function.__name__
return wrap
The error is in line request.user.user_profile.iccd which states user_profile don't exist (it does exist). doing
print(request.user)
gives out AnnonymousUser
Without the decorator, the API does print the correct user information as long as the passed token is valid.
The API that uses it is:
#method_decorator(client_has_paid, name='dispatch')
class AddIngredient(APIView):
permission_classes = [TokenHasReadWriteScope]
def post(self, request, cropped_land_id, format=None):
You can directly create drf style permission class and use it in your decorator, which would be more convenient. Just try this:
from rest_framework import permissions
class CustomPermission(permissions.BasePermission):
def has_permission(self, request, view):
iccd = request.user.user_profile.iccd
filters = {'iccd': iccd , 'active': 1 }
try:
purchase = Purchase.objects.get(**filters)
return True
except:
raise False
and use it in your view like:
class AddIngredient(APIView):
permission_classes = [CustomPermission]
def post(self, request, cropped_land_id, format=None):

DRF-Haystack SearchIndex returning from ALL index classes?

I'm using haystack with elasticsearch backend in django, and drf-haystack to implement serializers for results.
I first created a StudentIndex, which indexes over StudentProfiles to use in searches for students in search_indexes.py
class StudentIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(
document=True,
use_template=True,
template_name="search/indexes/user_search_text.txt")
//other indexes or indices or whatever
def get_model(self):
return StudentProfile
def index_queryset(self, using=None):
return StudentProfile.objects.filter(user__is_active=True)
Which is passed to the serializer and viewset in views.py:
class StudentSerializer(HaystackSerializer):
class Meta:
index_classes = [StudentIndex]
class StudentSearchView(ListModelMixin, HaystackGenericAPIView):
index_models = [StudentProfile]
serializer_class = StudentSerializer
pagination_class = None
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
and everything was fine and dandy....UNTIL I added two more indexes over other profiles and a single view/serializer to handle them. All exist in the same respective files:
search_indexes.py
class TeacherIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(
document=True,
use_template=True,
template_name="search/indexes/user_search_text.txt")
//other indexes or indices or whatever
def get_model(self):
return TeacherProfile
def index_queryset(self, using=None):
return TeacherProfile.objects.filter(user__is_active=True)
class StaffIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(
document=True,
use_template=True,
template_name="search/indexes/user_search_text.txt")
//other indexes or indices or whatever
def get_model(self):
return StaffProfile
def index_queryset(self, using=None):
return StaffProfile.objects.filter(user__is_active=True)
and added to views.py:
class StaffSerializer(HaystackSerializer):
class Meta:
index_classes = [StaffIndex, TeacherIndex]
class StaffSearchView(ListModelMixin, HaystackGenericAPIView):
index_models = [StaffProfile, TeacherProfile]
serializer_class = StaffSerializer
pagination_class = None
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
NOW....each view goes to its own url endpoint (/student-search and /staff-search respectively), but ONLY the staff-search endpoint properly returns Staff and TeacherProfile results, while the student-search, in a seperate endpoint with seperate view and models and indexes all explicitly stated, is return StudentProfiles AND Teacher and StaffProfiles and I can not for the life of me figure out why.
If anyone has run into this before, I'd really appreciate help solving, and more importantly, understanding, this issue.
Thanks in advance!
Well, for future people with the same issues, here's all i needed to do to solve it.
I'm using the DRF-Haystack genericviewapi, and on the view, I simply defined the filter_queryset to use the proper haystack connection (which ignored other indices). So for example:
class StudentSearchView(ListModelMixin, HaystackGenericAPIView):
index_models = [StudentProfile]
serializer_class = StudentSerializer
pagination_class = None
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def filter_queryset(self, queryset):
queryset = super(HaystackGenericAPIView, self).filter_queryset(queryset)
return queryset.using('student')
Everything now works properly :)
I have the same problem, but when i use the queryset.using('dynamic'), it raises django.core.exceptions.ImproperlyConfigured: The key 'dynamic' isn't an available connection.
class DynamicSearchView(ListModelMixin, HaystackGenericAPIView):
serializer_class = serializers.DynamicSearchSerializer
filter_backends = [HaystackHighlightFilter]
#swagger_auto_schema(
manual_parameters=[text, type]
)
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def filter_queryset(self, queryset):
return super(DynamicSearchView, self).filter_queryset(queryset).using('dynamic')

Resources