Validate body of a POST request in django - python

I have a view in django that sign's up a client and I have a model for the client and a form that looks like this:
from django.forms import ModelForm
from api.models.client import Client
class SignUpForm(ModelForm):
class Meta:
model = Client
fields = ['first_name', 'last_name']
In my view I would like to validate the data in the request but my problem is that the paramters in the request are camelCase and not snake_case so when I try to validate the data it doesn't work.
def sign_up(request):
body = json.loads(request.body)
form = SignUpForm(body)
print(form.is_valid())
return HttpResponse('this is a test response')
Is there a clean way of making this work? Also is this the correct way to do what I'm trying to do?

You can iterate through the body keys, use regex to rename the key and adding to a new dictionary.
def camel_to_snake(val):
return re.sub('([A-Z]+)', r'_\1', val).lower()
body = json.loads(request.body)
new_body = {camel_to_snake(k): v for k, v in body.items()}

Related

Django rest framework serializer fields using single API call

I have a route that uses a ViewSet
router.register(
r'mapping_details',
GlobalMappingDetailsViewSet,
base_name='global-store-product-mappings'
)
This view set contains a get_queryset method and a serializer.
class GlobalMappingDetailsViewSet(viewsets.ModelViewSet):
serializer_class = GlobalMappingDetailsSerializer
def get_queryset(self):
return models.Mappings.objects.all().select_related('my_column')
class GlobalMappingDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = models.Mappings
fields = ('column1', 'column2')
I want to add 2 fields that are populated using a single API call in the response of the request. I could use serializers.Field but will have to make separate calls for both the fields.
Does someone know the right way to handle this use case?
The way to do this is by overriding the Django Viewset actions. In my case I needed to just populate the response in the fields, I overrode the list and retrieve actions and added the fields to the response.
Some sample code:
def retrieve(self, request, *args, **kwargs):
response = super(GlobalMappingDetailsViewSet, self).retrieve(request, *args, **kwargs)
data = response.data
api_response = MyService.make_api_call(data.get('id'))
data['my_new_field'] = api_response.get('response_field', '')
return response

What is the DRF way to return a list of items from a third party api?

I have an endpoint where I need to make make a request to a third party API to get a list of items and return the results to the client.
Which of the following, or any other approach would be better suited to DRF?
Make input parameter validation and the call to the third party API and in the view method, pass the list of items in the response to a serializer for serialization and return serializer data to the client
Pass the request parameters to the serializer as write-only fields, make the field validation, api call and serialization in the serializer
A mixture of 1 and 2; use 2 different serializers, one that takes request parameters as write only fields, validates input parameters and makes the request to the 3rd party api, and another serializer that takes the resulting list from the first serializer and serializes the items for use of client
Since your question not mentioning anything about writing data into DB, undoubtedly you can go with Method-1.
Let's look into this sample api, which return a list of items (a list api).
Case - 1 : We need show the same response as we got from third-party api
In that case, we don't need any serializer or serialization process, all we need is pass the data from third-party API to the client.
from rest_framework.decorators import api_view
from rest_framework.response import Response
import requests
#api_view()
def my_view(request):
tp_api = "https://jsonplaceholder.typicode.com/posts"
response = requests.get(tp_api)
return Response(data=response.json())
Case - 2 : If you don't need complete data, but few parts (id and body)
In this particular situation, you can go with pythonic loop or DRF serializer.
# using DRF serializer
from rest_framework import serializers
# serializer class
class Myserializer(serializers.Serializer):
id = serializers.CharField()
body = serializers.CharField()
#api_view()
def my_view(request):
tp_api = "https://jsonplaceholder.typicode.com/posts"
response_data = requests.get(tp_api).json()
my_serializer = Myserializer(data=response_data, many=True)
my_serializer.is_valid(True)
return Response(data=my_serializer.data)
#Python loop way
#api_view()
def my_view(request):
tp_api = "https://jsonplaceholder.typicode.com/posts"
response_data = requests.get(tp_api).json()
data = [{"id": res['id'], "body": res['body']} for res in response_data]
return Response(data=data)
In case-2, I would reccomend to use DRF serializer, which does lots of things like validation, etc
When coming into your second approch, doing validation of the input data would depends on your requirement. As you said in comments, you need to provide some inputs to the third-party api. So, the validation should be carried out before accessing the third-party api
# Validation example
class MyInputSerializer(serializers.Serializer):
postId = serializers.IntegerField(max_value=10)
class MyOutputSerializer(serializers.Serializer):
id = serializers.CharField()
body = serializers.CharField()
#api_view()
def my_view(request):
input = MyInputSerializer(data=request.GET)
input.is_valid(True)
tp_api = "https://jsonplaceholder.typicode.com/comments?postId={}".format(input.data['postId'])
response_data = requests.get(tp_api).json()
my_serializer = MyOutputSerializer(data=response_data, many=True)
my_serializer.is_valid(True)
return Response(data=my_serializer.data)
Conclusion
The DRF is flexible enough to get desired output format as well as taking data into the system. In short, It all depends on your requirements

Django Rest Framework: How to pass extra argument to django serializer?

I have a variable named email in view.
I want to access this in ManageSerializer.
How can I pass this argument in serializer and get there?
views.py
email = 'xyz#gmail.com'
interviewData = Manage.objects.
filter(catcher_id = userCheck['id'], acceptation = '1').
filter(invitation_date__gte = dateToday)[:5];
serializer = ManageSerializers(interviewData, many = True)
Maybe pass it as kwargs:
ManageSerializers(interviewData, many = True, email= email)
You can access this in the init of the Serializer, using something like:kwargs.pop('email')
OR
You can pass the context to the Serializer like this.
ManageSerializers(interviewData, many = True, context={'email': email})
and you can access the context in Serializer like self.context['email']
You should pass it in context variable:
serializer = ManageSerializers(interviewData, many=True, context={'email': email})
Docs: Including extra context

When POSTing a Django form, my changes to the form's data are lost

In my Django app, I have a page.html that includes forms from multiple models like so:
# models.py
class OneModel(models.Model):
foo = models.CharField(...)
bar = models.CharField(...)
# forms.py
class OneForm(forms.ModelForm):
class Meta:
model = OneModel
On save, in addition to the POST data, I add a prefix to all the forms, for example:
form1 = OneForm(prefix=OneForm.Meta.model._meta.module_name)
Then, I send the POST request to be saved and send the next data to the __init__ form:
# views.py
form_data = {'foo': 'fo-fo-fo', # from request.POST
'bar': 'bar-bar-bar'} # -//-//-
form1 = OneForm(form_data) # without prefix
Afterwards, when the template is being put together, I get input from the form with my data (fo-fo-fo, bar-bar-bar), but without the prefix in input names, which is not what I want.
But if I add the prefix in the __init__ part instead I get back the form without my data:
# views.py
form1 = OpenForm(prefix=OneForm.Meta.model._meta.module_name, form_data)
How can I get the results of the form with both the data I need and the prefix I added?
You don't need to create form_data from request.POST. You can create the form instance as form1 = OneForm(request.POST, prefix=OneForm.Meta.model._meta.module_name).

Is there a way to capture URL parameters with django in urls.py?

I am trying to write something elegant where I am not relying on Request object in my code. All the examples are using:
(r'^hello/(?P.*)$', 'foobar.views.hello')
but it doesn't seem like you can post to a URL like that very easily with a form. Is there a way to make that URL respond to ..../hello?name=smith
Absolutely. If your url is mapped to a function, in this case foobar.views.hello, then that function might look like this for a GET request:
def hello(request):
if request.method == "GET":
name_detail = request.GET.get("name", None)
if name_detail:
# got details
else:
# error handling if required.
Data in encoded forms, i.e. POST parameters, is available if you HTTP POST from request.POST.
You can also construct these yourself if you want, say, query parameters on a POST request. Just do this:
PARAMS = dict()
raw_qs = request.META.get('QUERY_STRING', '') # this will be the raw query string
if raw_qs:
for line in raw_qs.split("&"):
key,arg = line.split("=")
PARAMS[key] = arg
And likewise for form-encoded parameters in non POST requests, do this:
FORM_PARAMS = QueryDict(request.raw_post_data)
However, if you're trying to use forms with Django, you should definitely look at django.forms. The whole forms library will just generally make your life easier; I've never written a html form by hand using Django because this part of Django takes all the work out of it. As a quick summary, you do this:
forms.py:
class FooForm(forms.Form):
name = fields.CharField(max_length=200)
# other properties
or even this:
class FooForm(forms.ModelForm):
class Meta:
model = model_name
Then in your request, you can pass a form out to the template:
def pagewithforminit(request):
myform = FooForm()
return render_to_response('sometemplate.html', {'nameintemplate': myform},
context_instance=RequestContext(request))
And in the view that receives it:
def pagepostingto(request):
myform = FooForm(request.POST)
if myform.is_valid(): # check the fields for you:
# do something with results. if a model form, this:
myform.save()
# creates a model for you.
See also model forms. In short, I strongly recommend django.forms.
You can't catch GET parameters in a URL pattern. As you can see in django.core.handlers.base.BaseHandler.get_response, only the part of the URL that ends up in request.path_info is used to resolve an URL:
callback, callback_args, callback_kwargs = resolver.resolve(
request.path_info)
request.path_info does not contain the GET parameters. For handling those, see Ninefingers answer.

Categories

Resources