Validating url in POST parameters in Django - python

I am trying to validate that the parameters in the POST request sent are valid URLs.
This is my views.py
views.py
def post(self, request):
if url_validator(request) == 400:
return Jsonresponse(status=400)
This is my utils.py. This file will contain all general methods and classes.
def url_validator(request, ext):
for key, value in request.data.items():
value = request.data[key]
try:
URLValidator(value)
except ValidationError:
return 400
When I call the function url_validator from views, it executes but doesn't return the exception when either of the request parameters doesn't contain URLs.
For example, if I pass a parameter param1: "some string", it doesn't go through the ValidationError path.
How do I go about getting the correct return from the function?

A validator class doesn't take the input to be validated in its instantiation, it takes it when you call the instantiated object:
validator = URLValidator()
validator(value)
But this really isn't how to do validation in Django. Either use a form, or if you're processing submitted JSON, use a django-rest-framework serializer.

Related

Passing a header value to a get request in django

I want to pass a value through the Headers of a get request.
Im trying the below but it doesn't work,
class ListCategoriesView(generics.ListAPIView):
"""
Provides a get method handler.
"""
serializer_class = CategorySerializer
def get(self, request, *args, **kwargs):
token = request.data.get("token", "")
if not token:
"""
do some action here
"""
if not UserAccess.objects.filter(accessToken=token).exists():
"""
do some action here
"""
else:
"""
do some action here
"""
I want to pass the token in the headers like that :
can anyone help me with this issue,
thanks a lot in advance.
You said it yourself, you're passing it in the headers, so you need to get it from there. DRF does not do anything special to access the headers, so it proxies to the underlying Django HttpRequest object, which makes them available via the META attribute, converted to uppercase and prefixed by HTTP_:
token = request.META.get("HTTP_TOKEN", "")

ValidationError in Django Rest Framework JWT does not use custom exception handler

I am using Django Rest Framework 3.2.3 (DRF) and Django Rest Framework JWT 1.7.2 (DRF-JWT, https://github.com/GetBlimp/django-rest-framework-jwt) to create Login tokens.
I need to change the Status Code for invalid credentials when issuing a JWT from 400 to 202 (FYI: my client can't read the body of non-200 responses). I use a custom exception handler as described by Django Rest Framework to achieve it: http://www.django-rest-framework.org/api-guide/exceptions/#custom-exception-handling
restapi/custom_exception.py
from rest_framework.views import exception_handler
from rest_framework import status
def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
print ('Exception raised: ' + str(response.status_code))
# Now add the HTTP status code to the response.
if response is not None:
if response.status_code != status.HTTP_403_FORBIDDEN:
response.status_code = status.HTTP_202_ACCEPTED
return response
And in the config:
'EXCEPTION_HANDLER': 'restapi.custom_exception.custom_exception_handler',
The DRF-JWT should raise ValidationError, when invalid credentials are used. I still get a 400 Bad Request response code, when posting invalid credentials to JWT token-auth interface.
With every other DRF interface I am getting the 202 status code as expected.
How can I get DRF-JWT to use the custom exception handler for their ValidationErrors?
Why we are not able to use custom exception handler here?
This is happening because raise_exception flag has not been passed to the JSONWebTokenSerializer when calling .is_valid() on it. ( JSONWebTokenSerializer is the serializer class used to validate a username and password.)
DRF-JWT source code for post() method:
def post(self, request):
serializer = self.get_serializer(
data=get_request_data(request)
)
if serializer.is_valid(): # 'raise_exception' flag has not been passed here
user = serializer.object.get('user') or request.user
token = serializer.object.get('token')
response_data = jwt_response_payload_handler(token, user, request)
return Response(response_data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Now, we can see that raise_exception flag has not been passed to is_valid(). When this happens, ValidationError is not raised thereby resulting in non-execution of your custom_exception_handler code.
As per the DRF section on Raising an exception on invalid data:
The .is_valid() method takes an optional raise_exception flag that
will cause it to raise a serializers.ValidationError exception if
there are validation errors.
These exceptions are automatically dealt with by the default exception
handler that REST framework provides, and will return HTTP 400 Bad
Request responses by default.
SOLUTION:
If you pass the raise_exception flag as True when calling the .is_valid() function, the code for custom_exception_handler will be executed.
You will need to create a CustomObtainJSONWebToken view which will inherit from the default ObtainJSONWebToken view. In this, we will override the .post() method to pass the raise_exception flag. Then will specify this view in our urls.
my_app/views.py
from rest_framework_jwt.views import ObtainJSONWebToken
class CustomObtainJSONWebToken(ObtainJSONWebToken):
def post(self, request):
serializer = self.get_serializer(
data=get_request_data(request)
)
serializer.is_valid(raise_exception=True) # pass the 'raise_exception' flag
user = serializer.object.get('user') or request.user
token = serializer.object.get('token')
response_data = jwt_response_payload_handler(token, user, request)
return Response(response_data)
urls.py
# use this view to obtain token
url(r'^api-token-auth/', CustomObtainJSONWebToken.as_view())
I think maybe a cleaner way to do it that doesn't involve overriding the post method is
serializers.py
from rest_framework_jwt import serializers as jwt_serializers
class JSONWebTokenSerializer(jwt_serializers.JSONWebTokenSerializer):
"""
Override rest_framework_jwt's ObtainJSONWebToken serializer to
force it to raise ValidationError exception if validation fails.
"""
def is_valid(self, raise_exception=None):
"""
If raise_exception is unset, set it to True by default
"""
return super().is_valid(
raise_exception=raise_exception if raise_exception is not
None else True)
views.py
from rest_framework_jwt import views as jwt_views
from .serializers import JSONWebTokenSerializer
class ObtainJSONWebToken(jwt_views.ObtainJSONWebToken):
"""
Override the default JWT ObtainJSONWebToken view to use the custom serializer
"""
serializer_class = JSONWebTokenSerializer
urls.py
from django.conf.urls import url
from .views import ObtainJSONWebToken
urlpatterns = [
...
url(r'^api-token-auth/', ObtainJSONWebToken.as_view(), name='jwt-create'),
]

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"

Django CRUD admin view: return error message

I'm using the CRUD admin view to edit some text.
I override the save() method of my model to run some validation before. that is, if the input string is not a well-formed xml, it doesn't save it. I'd like to notify the user.
However, I can only find solutions that need the request object and the messages framework but, as far as I'm concerned, I can not access request from save()
def save(self, *args, **kwargs):
try:
from xml.dom.minidom import parseString
doc = parseString(self.content)
super(Screen, self).save(*args, **kwargs)
except Exception, e:
from django.contrib import messages
# messages.error(request, "This is a bad bad message")
print("this is a bad bad string")
return
How can I send an error message?
After hitting "save" the user is redirected again to the list of instances of that model. is there a way to redirect it to the form? are these problems related?
I think you may use the clean method inside your models, in that way you will validate your data in the admin, just like the other admin fields
Inside your Model:
def clean(self):
try:
from xml.dom.minidom import parseString
doc = parseString(self.content)
except Exception, e:
from django import forms
raise forms.ValidationError(u"It's not a XML")
super(YourModel,self).clean()
def full_clean(self, exclude=None):
return self.clean()
reference: https://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.clean_fields
Thanks to #FernandoFreitasAlves I could write a solution.
I realised that my model can also be loaded from a file and then stored in the DB, without the CRUD admin page, so I also overide the save() method.
def save(self, *args, **kwargs):
xml = "<screen>" + self.content + "</screen>"
parseString(xml.encode("utf-8"))
super(Screen, self).save(*args, **kwargs)
def clean(self):
try:
from xml.dom.minidom import parseString
doc = parseString(self.content)
except Exception, e:
from django import forms
raise forms.ValidationError("It's not a XML")
super(Screen,self).clean()
I think I don't want to overide the full_clean() method. I can't see a reason for that. The docs (https://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.full_clean) say
This method calls Model.clean_fields(), Model.clean(), and
Model.validate_unique(), in that order and raises a ValidationError
that has a message_dict attribute containing errors from all three
stages.

Flask Python REST API set optional JSON param when receiving POST

I'm building a REST API with Flask which add a photo to a database. The database is abstracted in PhotoModel Class. The API receives a JSON formated HTTP POST which contain the picture in a bin string an the name, all the other parameters are optional.
How to construct "photo" object if some param aren't present in the JSON posted?
On the database Model (PhotoModel) I have specify only two compulsory items, so the logic to only take into account params present in the JSON should be in the function bellow.
def add_photo():
"""Add photo to database"""
if request.method == 'POST' and request.headers['Content-Type'] == 'application/json':
photo = PhotoModel(
name = request.json['name'],
device_version = request.json['device_version'],
date = request.json['date'],
picture = request.json['picture'],
comment = request.json['comment']
)
try:
photo.put()
return "200"
except CapabilityDisabledError:
return "500 DB read-only"
else:
return "415 Unsupported Media Type"
I can't figure out how to do it, any pointer would help
Take a look at peewee it comes with a RESTful API in JSON. It's also an light ORM engine.
I've discovered JSON Schema and it works fantastic to validate JSON requests.
Create a decorator which you can use for all views:
from functools import update_wrapper
from jsonschema import validate as jsonschema_validate
def validate(schema):
def decorator(f):
def wrapped_function(*args, **kwargs):
# Validate request Content Type
if request.json is None:
raise ValidationError("Content Type must be JSON")
# Validate document
jsonschema_validate(request.json, schema)
return f(*args, **kwargs)
return update_wrapper(wrapped_function, f)
return decorator
Use decorator for your views:
#app.route('/', methods=['POST'])
#validate(schema)
def insert_document():
# now your request.json object is validated against the specified schema
data = request.get_json() #You can use any method here.
#Below the required parameters outside the try
email=data['email']
role=data['role']
try:
#Here are the optional json parameters inside a try
firstname = data['firstname']
lastname = data['lastname']
except KeyError:
#Here handle the exception, maybe parse some default values.
pass

Categories

Resources