Django rest api validate data sent in the requests - django

I am trying to build a Django rest api to allow my clients to send requests with data, so that I can save them to a db. I have done that part but other than the format validation achieved through Serializers I also want check for data validation.... for example
UnitOfMeasureName = ["Each", "Grams", "Ounces", "Pounds", "Kilograms", "Metric Tons"]
UnitOfMeasureName should be one of the above in the list,
So if a user sends {..., 'UnitOfMeasureName': 'invalid_one', ...} in request data I want to send a bad request.
(This will pass the serializer as the type is string)
Any ideas please, If you need any clarification please ask in the comments. And thanks in advance.. :)

In your serializer class add a method to validate UnitOfMeasureName like following:
def validate_unitofmasurename(self, value):
UnitOfMeasureName = ["Each", "Grams", "Ounces", "Pounds", "Kilograms", "Metric Tons"]
if value in UnitOfMeasureName:
return True
else:
return ValidationError('Invalid masure name')

Related

Django - RawPostDataException: You cannot access body after reading from request's data stream

I'm really stuck at this problem for a couple of days now.
While I understand, what's happening here, I don't really know the best workaround/solution for this.
Problem:
I'm trying to create a user login endpoint using Django and DRF in general.
My login API needs to support a login via password as well as login via OTP.
My LoginView looks like:
def post(self, request, **kwargs):
"""
post
Method to handle user login
:param request:
:param args:
:param kwargs:
:return:
"""
request_data = request.data
login_using_password = request_data.get('login-with-password') is True
login_using_otp = request_data.get('login-with-otp') is True
if request_data is not None:
if all((login_using_password, login_using_otp)):
raise accounts_exceptions.InvalidLoginRequestError()
if login_using_password:
return Response(self._login_with_password(request))
elif login_using_otp:
return Response(self._login_with_otp(request))
raise accounts_exceptions.InvalidLoginRequestError()
return Response(self._login_with_password(request))
Also my _login_with_password looks like:
def _login_with_password(self, request, **kwargs):
"""
_login_with_password
A utility method to handle login with password
:param request:
:return:
"""
return getattr(ObtainJSONWebToken.as_view()(request=request._request, ), 'data')
When I try to login, Django complains saying RawPostDataException You cannot access body after reading from request's data stream
I'm using JWT to authenticate requests. ObtainJSONWebToken is a view provided by DRF-JWT to obtain access tokens to authenticate requests.
What is the workaround/solution for this?
Is there a better way to support such a login requirement?
Thanks in advance!
Resolved this.
There's no concrete way to solve the problem above.
Django disallows access to request.data multiple times.
It could be done only once for the entire request lifetime.
So, this left me with two solutions:
Move my request payload to query params.
Move my request payload to url context.
I ended up using a mix and match of both.
So, basically I used request.query_params and self.context to fetch data from the request and changed my URL and request structure accordingly.

Django Rest Framework: Without Model

I'm working on creating a RESTAPI using DRF(Django Rest Framework). API just receives the users twitter handle and returns his twitter data.
Here, I'm not using model here because it's not required.
Should I use a serializer here? If so why? Now I'm able to return the data without using a serializer.
Moreover, My API is not web-browsable. How should I make it web-browsable: which is one of the best features of DRF.
Edit:1
I'm using Functions in Views.
#api_view(['GET'])
#csrf_exempt
def getdetails(request):
call the twitter api
receive the data
return HttpResponse(JsonResponse( {'data': {'twitter_id':id,'tweets':count,'Followers':followers,'following':count_follow}}))
In the browser I'm just seeing JSON data like this.
{"data": {"twitter_id": 352525, "tweets": 121, "Followers": 1008, "following": 281}}
You can use Serializer for the result
class SampleSerializer(serializers.Serializer):
field1 = serializers.CharField()
field2 = serializers.IntegerField()
# ... other fields
Usage
my_data = {
"field1": "my sample",
"field2": 123456
}
my_serializer = SampleSerializer(data=my_data)
my_serializer.is_valid(True)
data = my_serializer.data
You'll get serialized data in data variable (you can use my_serializer.data directly)
Should I use a serializer here?
It's up to you, because if you wish to show the response JSON without any modification from the Twitter API, you can go without DRF serializer. And If you wish to do some formatting on the JSON, my answer will help you
My API is not web-browsable. How should I make it web-browsable?
Maybe you followed wrong procedure. Anyway we can't say more on this thing without seeing your code snippets
Update-1
from rest_framework.response import Response
#api_view(['GET'])
#csrf_exempt
def getdetails(request):
call the twitter api
twitter_api = get_response_from_twitter() # Json response
return Response(data=twitter_api)

Django REST framework non-model serializer and BooleanField

I seem to have hit a wall full of puzzling results when trying to deal with the following use case:
URL: '^api/event/(?P<pk>[0-9]+)/registration$'
payload: {"registered": "true"} or {"registered": "false"}
I retrieve the event object corresponding to the given pk, and then based on that I want:
in a GET request to retrieve whether the authenticated user is registered or not
in a PUT to change the registration state.
Everything works fine until the point where I want to process the incoming payload in the PUT request. I've tried creating a serializer like this:
class RegistrationSerializer(serializers.Serializer):
registered = fields.BooleanField()
and call it from an APIView's put method with:
serializer = RegistrationSerializer(data=request.DATA)
but it doesn't work and serializer.data always contains `{"registered": False}
From a shell I tried another isolated test:
>>> rs = RegistrationSerializer(data={'registered':True})
>>> rs
<app.serializers.RegistrationSerializer object at 0x10a08cc10>
>>> rs.data
{'registered': False}
What am I doing wrong? What would be the best way to handle this use case?
You need to call rs.is_valid() first, before accessing rs.data.
Really the framework ought to raise an exception if you don't do so.

How to receive POST data in django

I've been trying to integrate a payment gateway into my site in django.
I'm having trouble getting the response data from the payment gateway.
The payment gateway has sample docs for php which looks like this :
$ErrorTx = isset($_POST['Error']) ? $_POST['Error'] : ''; //Error Number
$ErrorResult = isset($_POST['ErrorText']) ? $_POST['ErrorText'] : ''; //Error message
$payID = isset($_POST['paymentid']) ? $_POST['paymentid'] : ''; //Payment Id
In the view for the url that the payment gateway is redirecting to after entering card details etc, I'm checking if it's a GET if request.method == "GET" and then passing the request to a function. When I debug the request, I can see an empty query dict. and if I try something like res = request.GET['paymentid'] I get an error that says there's no key called paymentid.
Am I missing something obvious? I'm still pretty new to django, so I'm sure I'm doing something wrong.
res = request.GET['paymentid'] will raise a KeyError if paymentid is not in the GET data.
Your sample php code checks to see if paymentid is in the POST data, and sets $payID to '' otherwise:
$payID = isset($_POST['paymentid']) ? $_POST['paymentid'] : ''
The equivalent in python is to use the get() method with a default argument:
payment_id = request.POST.get('payment_id', '')
while debugging, this is what I see in the response.GET: <QueryDict: {}>, request.POST: <QueryDict: {}>
It looks as if the problem is not accessing the POST data, but that there is no POST data. How are you are debugging? Are you using your browser, or is it the payment gateway accessing your page? It would be helpful if you shared your view.
Once you are managing to submit some post data to your page, it shouldn't be too tricky to convert the sample php to python.
You should have access to the POST dictionary on the request object.
for class based views, try this:
class YourApiView(generics.ListAPIView):
"""
API endpoint
"""
def post(self, request, *args, **kwargs):
print("request data")
print(request.data)

Control a function (E.g send mail) from a users profile page

I have a profile page like so: http://i.stack.imgur.com/Rx4kg.png . In management I would like a option "Notify by mail" that would control my send_email functions in every application I want. As example I'm using django-messages and it sends private messages aswell as emails when you send a message. I would like for the user to be able to specify if he wants emails aswell when he gets a message.
messages/utils.py
def new_message_email(sender, instance, signal,
subject_prefix=_(u'New Message: %(subject)s'),
template_name="messages/new_message.html",
default_protocol=None,
*args, **kwargs):
"""
This function sends an email and is called via Django's signal framework.
Optional arguments:
``template_name``: the template to use
``subject_prefix``: prefix for the email subject.
``default_protocol``: default protocol in site URL passed to template
"""
if default_protocol is None:
default_protocol = getattr(settings, 'DEFAULT_HTTP_PROTOCOL', 'http')
if 'created' in kwargs and kwargs['created']:
try:
current_domain = Site.objects.get_current().domain
subject = subject_prefix % {'subject': instance.subject}
message = render_to_string(template_name, {
'site_url': '%s://%s' % (default_protocol, current_domain),
'message': instance,
})
if instance.recipient.email != "":
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
[instance.recipient.email,])
except Exception, e:
#print e
pass #fail silently
Apparently instance.recipient.email is the email for the recipient user. So my questions are: How do I go about creating an option in my profile management that can be used in my new_message_email to check if the user wants emails or not? My own thoughts are that I need to save a value in the database for the user and then check for that value in new_message_email function. How I do that isn't clear though. Do I create a new function in my userprofile/views.py and class in userprofile/forms.py? And have my userprofile/overview.html template change them? Some specifics and thoughts if this is the right approach would help alot!
You probably want to start off by creating a user profile so that you have a good way to store weather or not the user wants these emails sent to them. This is done using the AUTH_PROFILE_MODULE setting in your settings.py.
Once you have the data stored, you should be able to access it from instance.recipient (assuming that instance.recipient is a User object). So you could change your code to:
if instance.recipient.get_profile().wants_emails and instance.recipient.email != "":
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
[instance.recipient.email,])
Done and done.