How to send parameter in AJAX (rest-api) using django? - django-rest-framework

I utilities rest-api in django,
and I don't succeed to send a "GET" parameter through ajax:
In rest-api app in django I have in the urls.py:
urlpatterns = patterns('',
url(r'^titles/(?P<author_id>\d+)/$', login_required(views.TitlesViewSet.as_view()) ),
)
In views.py I wrote:
class TitlesViewSetViewSet(ListCreateAPIView):
serializer_class = TitleSerializer
def get_queryset(self):
aouther_id = self.request.GET.get('aouther_id', None)
return Title.objects.filter(auther = auther_id)
when the code insert to the get_queryset above it doesn't recognize any GET parameter and the aouther_id is set to None.
Does anybody know what I should do?

First, you have a typo in urls, you are using author_id and in view you are trying to get the aouther_id key. Second, you are trying to get the value from the query parameters, but you are not actually using them. Third, you are using named url parameters and those are being stored in the kwargs property of your class based view.
You can access them this way:
class TitlesViewSetViewSet(ListCreateAPIView):
serializer_class = TitleSerializer
def get_queryset(self):
# try printing self.kwargs here, to see the contents
return Title.objects.filter(author_id=self.kwargs.get('author_id'))

you should replace a line of the auther_id setting to:
auther_id=self.kwargs['auther_id']
update:
I now see jbub answer... thanks man! I just discovered it...

Related

is creating serializer mandatory for each View in django rest frame work

I am working on app where I didnt use serializer in one view I just want to ask am I doing something wrong.
I am getting a committee id in the url and in the body I am getting the usr_id whos status I want to change if someone sends a post request to this end point.
This is my url
path('committee/<int:id>/accept-request/', RequestAcceptView.as_view(), name="accept-request"),
this is my view.py
class RequestAcceptView(APIView):
def post(self, request, id):
user_id = request.data['user']
print("iddddd",user_id )
try :
approve_request = Member.objects.filter(
Q (com=Committee.objects.get(id=id))
).update(mem_status="fully_approved")
return Response(SuccessResponse(msg="Request accepted"))
except:
return Response(ErrorResponse(msg="invalid))
I want to know I am not using serializer here for any purpose, is it fine? should I remove serializer file?
No, it is not when you are using ApiView. Generic and ModelView requires them. You should also think about if you want to have auto generated documentation you need the serializer and it will also perform validation (because you are not using the id field, but request.data.
If request.data and I'd are the same, then you might want to delete the serializer

How to integrate/install drf-yasg to Django Rest project? Redoc/Swagger appears empty

I'm trying to integrate the drf-yasg to my Django Rest project. I installed the library via pip and added these code lines to the url.py as below.
schema_view = get_schema_view(
openapi.Info(
title="Costifier API",
default_version='v1',
description="Costifier API'ye hoşgeldiniz.",
terms_of_service="https://costifier.sfmyazilim.com",
contact=openapi.Contact(email="info#sfmyazilim.com"),
),
public=True,
permission_classes=(permissions.AllowAny,),
)
urlpatterns = [
path('', index),
path('admin/', admin.site.urls),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), #<-- Here
path('api/', include('sfmAPI.urls')),
]
One of my views is;
class PredictionView(views.APIView):
permission_classes = [AllowAny]
throttle_classes = [AnonymousUserThrottle]
queryset = Prediction.objects.all()
serializer_class = PredictionSerializer
def post(self, request, format=None):
serializer = PredictionSerializer(data=request.data)
if serializer.is_valid():
input_map_dict = json.loads(serializer.validated_data['input_map'])
username = serializer.validated_data['customer_name']
prediction_results = SmartRegression.smart_predict(username,
serializer.validated_data['model_name'],
input_map_dict,
isMember(username))
result = {
'inputs': serializer.data,
'error': '0',
'message': 'Successful',
'predicted_value': prediction_results[0],
'confidence': prediction_results[1],
'feature_importance': prediction_results[2]
}
return Response(status=status.HTTP_200_OK, data=result)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
My /redoc page is created. However, it has no content. It just has the APIView names such as below.
How can I fill the documentation?
The reason is you're using APIView instead of a generic view.
In the Django-Rest-Framework docs for schemas it mentions the below:
Note: The automatic introspection of components, and many operation parameters relies on the relevant attributes and methods of GenericAPIView: get_serializer(), pagination_class, filter_backends, etc. For basic APIView subclasses, default introspection is essentially limited to the URL kwarg path parameters for this reason.
A work around for this if you want to stick to using APIView instead of a generic view, is to use #swagger_auto_schema decorator (from drf-yasg) above each of your views. It's a bit of a pain but you should be able to find a few post on stackoverflow around using this. An example is:
DRF YASG CustomizingDRF YASG Customizing
Django-Rest-Framework does have a manual method of setting the parameters but most people would prefer to stick to the AutoSchema doing the work. I haven't been able to find much on how to use it for a beginner either. Hence drf-yasg seems a good way to go.
I believe for your post view above the below decorator should be what you're looking for just above the post method:
#swagger_auto_schema(
request_body=PredictionSerializer,
responses={
'200': 'OK Request',
'400': "Bad Request"
},
)
Also you want to add something like the below to the very top of your view since it will pull through as the description in the schema/documentations.
'''
def post(self, request, format=None):
"Description of your View"
'''
...

POSTing to URL of one resource to create another, different resource

In my REST API I have two entities: Test and TestRun. I want to be able to send a POST request to create a TestRun (with the appropriate TestRun fields), but the URL of this request must be api/v1/test/{id}/start instead of api/v1/testrun.
I know that using #detail_route I can customise the URL, but then the request is still sent to api/v1/test/{id}:
class TestViewSet(viewsets.ModelViewSet):
queryset = Test.objects.all()
serializer_class = TestSerializer
#detail_route(methods=['post'], url_path='start')
def start_test(self, request, pk=None):
pass
class TestRunViewSet(viewsets.ModelViewSet):
queryset = TestRun.objects.all()
serializer_class = TestRunSerializer
Perhaps some highly customised router is needed here?
OK, I have the basic example. I think you have few problems, so first things first:
My views:
class TestViewSet(viewsets.ModelViewSet):
queryset = Test.objects.all()
serializer_class = TestSerializer
#detail_route(methods=['post'], url_path='start', serializer_class=TestRunSerializer)
def start_test(self, request, pk=None):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
# add here TestRun object
return Response(serializer.data, status=status.HTTP_200_OK)
class TestRunViewSet(viewsets.ModelViewSet):
queryset = TestRun.objects.all()
serializer_class = TestRunSerializer
My urls:
router = SimpleRouter()
router.register('test', TestViewSet)
router.register('test-run', TestRunViewSet)
urlpatterns = router.urls
and settings urls:
urlpatterns = [
url(r'^api/v1/', include('droute.urls'))
]
In this scenario you have full CRUD for Test and TestRun models - one is under api/vi/test and second in api/v1/test-run;
The detail_route decorator creates for you additional route: /api/v1/test/:id/start
But this do not mean that CRUD under api/v1/test-run is no longer accessible.
If you do not want to do not allow creation on api/v1/test-run you should use there ReadOnlyModelViewSet as a base for TestRunViewSet - this will allow only GET on the list endpoint: api/v1/test-run and on the details endpoint: api/v1/test-run//
You do not need to make magic in routers - as in example SimpleRouter is enough for that case.
Things are getting little bit more complicated if you want to make nested routers. You can search stackoverflow - there were many articles about that. But to be honest I would discourage you to use nested routers, I never feel that working with this is a pleasure :) You can check here:
https://github.com/alanjds/drf-nested-routers
I think (but I have little or nono information) that the best API for you would be something like this:
/api/v1/test -> CRUD for TEST
/api/v1/test/:id/start -> start the test POST
/api/v1/test/:id/runs -> get the runs list GET (list_route on TestViewSet or Nested router)
/api/v1/test/:id/runs/:run_id -> get the run details GET (and here is a problem - because it implies that you need nesting :(, or some custom view attached to the urls;)
Happy coding, hope this helps.

get_queryset method and ViewSets in django rest framework

I am doing exactly as the example states
here is my method
class FeedViewSet(viewsets.ModelViewSet):
model = Feed
serializer_class = FullFeedSerializer
def get_queryset(self):
user = request.user
queryset = Feed.objects.get_nearby(user)
return queryset
when i execute it, it says request not defined .. which actually isn't. the example at the rest framework's site also haven't defined request. what am i doing wrong?
The request object is available (on either REST framework's class based views, or Django's standard class based views) as self.request. You're missing the self. part of that.

How to subclass django's generic CreateView with initial data?

I'm trying to create a dialog which uses jquery's .load() function to slurp in a rendered django form. The .load function is passed the pk of the "alert" object. Also available in the class functions are things like self.request.user so I can pre-fill those fields, shown below in the Message model (models.py):
class Message(models.Model):
user = models.ForeignKey(User)
alert = models.ForeignKey(Alert)
date = models.DateTimeField()
message = models.TextField()
Subclassing django's CreateView makes it pretty easy to generate a context with an instance of the ModelForm (views.py):
class MessageDialogView(CreateView):
""" show html form fragment """
model = Message
template_name = "message.html"
def get_initial(self):
super(MessageDialogView, self).get_initial()
alert = Alert.objects.get(pk=self.request.POST.get("alert_id"))
user = self.request.user
self.initial = {"alert":alert.id, "user":user.id, "message":"test"}
return self.initial
def post(self, request, *args, **kwargs):
super(MessageDialogView, self).post(request, *args, **kwargs)
form_class = self.get_form_class()
form = self.get_form(form_class)
context = self.get_context_data(form=form)
return self.render_to_response(context)
The problem here is that self.initial does not get rendered with the form. I have insured that the form is indeed calling get_initial and the form instance has the proper initial data in post, but when the form is rendered in the template message.html it doesn't grab any of the initial data like I would expect. Is there a special trick to get this to work? I've scoured the docs (seems to be lacking examples on generic based class views) and source but I can't see what I'm missing.
get_initial() should just return a dictionary, not be bothered with setting self.initial.
Your method should look something like this:
def get_initial(self):
# Get the initial dictionary from the superclass method
initial = super(YourView, self).get_initial()
# Copy the dictionary so we don't accidentally change a mutable dict
initial = initial.copy()
initial['user'] = self.request.user.pk
# etc...
return initial
you can use like :
from django.shortcuts import HttpResponseRedirect
class PostCreateView(CreateView):
model = Post
fields = ('title', 'slug', 'content', 'category', 'image')
template_name = "create.html"
success_url = '/'
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return HttpResponseRedirect(self.get_success_url())
that's work for me
(Edited because what you're trying does actually work)
I ran into the same problem yesterday, but it's working now – I think I was returning an object instead of a dict in get_initial.
In terms of fixing your problem, I'm a little suspicious of how much you seem to be doing in post() – could you try it with the default (non-overrided) post()?
You could also use pdb (or print statements) to check the value of self.get_form_kwargs make sure that initial is being set.

Resources