Cant handle a response error on django-rest-framework - error-handling

I am trying to return 400 bad requets response when a user with a company created tries to create another one on my API
I tried with a Response but it does not work, it seems that it does not enter on the if
class CompanyViewSet(generics.ListCreateAPIView):
serializer_class = CompanySerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return UserCompany.objects.filter(owner=self.request.user.id)
def perform_create(self, serializer):
queryset = UserCompany.objects.filter(owner=self.request.user.id)
if queryset.exists():
content = {'API response error:': 'Can have only a one company for every user'}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
else:
serializer.save(owner=self.request.user)
When I create a company in user that already has one returns state 200 but don't create it, I expected it to return state 400

Because perform_create only save serializer . If you want control status response, you should override create. Try like this:
def create(self, request, *args, **kwargs):
queryset = UserCompany.objects.filter(owner=self.request.user.id)
if queryset.exists():
content = {'API response error:': 'Can have only a one company for every user'}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
serializer = self.get_serializer(data=request.data)
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)

Related

Is passing additional data to save() the right way to store the results of logic triggered by a POST?

I've got a django rest framework view that passes data from a POST to another function that returns a result.
I want to store that result in the database so that you can GET a previous result or results.
Currently, I've got the "nornir_result" defined in my serializer and model.
The serializer field is set to required=False (because the result isn't known when the payload is passed in).
Then, to populate/save this information for later GETs, save(nornir_result=nornir_result) gets called during the POST.
Is my approach correct? Passing additional data to save() to store the results of logic triggered by a POST so it can be viewed later?
# views.py
class F5AuditList(APIView):
def get(self, request, format=None):
audits = F5Audit.objects.all()
serializer = F5AuditSerializer(audits, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = F5AuditSerializer(data=request.data)
if serializer.is_valid():
nornir_result = audit.django_result(request.data)
serializer.save(
audit_id=create_unique_number(), nornir_result=nornir_result
)
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# serializers.py
class F5AuditSerializer(serializers.Serializer):
audit_id = serializers.IntegerField(required=False)
created = serializers.DateTimeField(required=False)
devices = serializers.DictField()
audit_type = serializers.CharField(
max_length=None, style={"base_template": "textarea.html"}, required=False
)
nornir_result = serializers.CharField(
max_length=None, style={"base_template": "textarea.html"}, required=False
)
# models.py
class F5Audit(Document):
audit_id = fields.IntField(primary_key=True)
created = fields.DateTimeField(default=datetime.datetime.utcnow)
audit_type = fields.StringField(max_length=100, default="type of f5 audit")
devices = fields.DictField()
nornir_result = fields.StringField(max_length=1000)

Flask - How can I use my functions on the URL

I'm starting to learn Flask and maybe I'm just using the wrong words to search, but here's the problem.
I have this class:
class Video(Resource):
#marshal_with(resource_fields)
def get(self, video_id):
result = VideoModel.query.filter_by(id=video_id).first()
if not result:
abort(404, message="Could not find video with that id")
return result
#marshal_with(resource_fields)
def put(self, video_id):
args = video_put_args.parse_args()
result = VideoModel.query.filter_by(id=video_id).first()
if result:
abort(409, message="Video id taken...")
video = VideoModel(id=video_id, name=args['name'], views=args['views'], likes=args['likes'])
db.session.add(video)
db.session.commit()
return video, 201
#marshal_with(resource_fields)
def patch(self, video_id):
args = video_update_args.parse_args()
result = VideoModel.query.filter_by(id=video_id).first()
if not result:
abort(404, message="Video doesn't exist, cannot update")
if args['name']:
result.name = args['name']
if args['views']:
result.views = args['views']
if args['likes']:
result.likes = args['likes']
db.session.commit()
return result
And I'm trying to make so when I have an URL like http://127.0.0.1/5000/video/put/1/Test/80/2, I can use the funtion from the URL by typing it. Is there any way to do this? Thanks in advance!

permission_classes doesn't work . where is problem?

I'm trying to found how Restframework's permission work , so i try write some code that response a simple Json. But the existence or absence of a ā€¨permission_classes does not affect the execution of the code and endpoint will response all request without checking any permission.
here is my code:
class TeacherStatisticPost(generics.RetrieveAPIView):
permission_classes = (ClassOwnerPermission)
queryset = ClassRoom.objects.all()
lookup_field = "id"
lookup_url_kwarg = 'classRoom_id'
def get_klass(self):
class_id = self.kwargs['classRoom_id']
return ClassRoom.objects.get(id=classRoom_id)
def get(self, request, *arg, **kwargs):
klass = self.get_klass()
response ={
'class_room_grade' : klass.grade,
'class_room_name' : klass.name,
}
return JsonResponse(response, safe=False)
and here is my permission.py:
class ClassOwnerPermission(permissions.BasePermission):
def has_perm(self, user, klass):
print("now in class perm") # never print out any thing!
return klass.owner == user
def has_object_permission(self, request, view, obj): # where is come from 'obj' ?
return self.has_perm(request.user, obj)
im try to set permission that just owner of ClassRoom can access to this endpoint.
Firstly, DRF expects permission_classes to be list or tuple. permission_classes at the line permission_classes = (ClassOwnerPermission) is not neither tuple not list. Put comma after the ClassOwnerPermission.
The line permission_classes = (ClassOwnerPermission, ) should work.
Secondly, you do not use get_object method which checks the permissions. Remove def get_klass(self): method and use get_object
Your view should look like following:
class TeacherStatisticPost(generics.RetrieveAPIView):
permission_classes = (ClassOwnerPermission, )
queryset = ClassRoom.objects.all()
lookup_field = "id"
lookup_url_kwarg = 'classRoom_id'
def get(self, request, *arg, **kwargs):
klass = self.get_object()
response ={
'class_room_grade' : klass.grade,
'class_room_name' : klass.name,
}
return JsonResponse(response, safe=False)

apollo-upload-client and graphene-django

I have a question about using apollo-upload-client and graphene-django. Here I've discovered that apollo-upload-client adding operations to formData. But here graphene-django is only trying to get query parameter. And the question is, where and how it should be fixed?
If you're referring to the data that has a header like (when viewing the HTTP from Chrome tools):
Content-Disposition: form-data; name="operations"
and data like
{"operationName":"MyMutation","variables":{"myData"....}, "query":"mutation MyMutation"...},
the graphene-python library interprets this and assembles it into a query for you, inserting the variables and removing the file data from the query. If you are using Django, you can find all of the uploaded files in info.context.FILES when writing a mutation.
Here's my solution to support the latest apollo-upload-client (8.1). I recently had to revisit my Django code when I upgraded from apollo-upload-client 5.x to 8.x. Hope this helps.
Sorry I'm using an older graphene-django but hopefully you can update the mutation syntax to the latest.
Upload scalar type (passthrough, basically):
class Upload(Scalar):
'''A file upload'''
#staticmethod
def serialize(value):
raise Exception('File upload cannot be serialized')
#staticmethod
def parse_literal(node):
raise Exception('No such thing as a file upload literal')
#staticmethod
def parse_value(value):
return value
My upload mutation:
class UploadImage(relay.ClientIDMutation):
class Input:
image = graphene.Field(Upload, required=True)
success = graphene.Field(graphene.Boolean)
#classmethod
def mutate_and_get_payload(cls, input, context, info):
with NamedTemporaryFile(delete=False) as tmp:
for chunk in input['image'].chunks():
tmp.write(chunk)
image_file = tmp.name
# do something with image_file
return UploadImage(success=True)
The heavy lifting happens in a custom GraphQL view. Basically it injects the file object into the appropriate places in the variables map.
def maybe_int(s):
try:
return int(s)
except ValueError:
return s
class CustomGraphqlView(GraphQLView):
def parse_request_json(self, json_string):
try:
request_json = json.loads(json_string)
if self.batch:
assert isinstance(request_json,
list), ('Batch requests should receive a list, but received {}.').format(
repr(request_json))
assert len(request_json) > 0, ('Received an empty list in the batch request.')
else:
assert isinstance(request_json, dict), ('The received data is not a valid JSON query.')
return request_json
except AssertionError as e:
raise HttpError(HttpResponseBadRequest(str(e)))
except BaseException:
logger.exception('Invalid JSON')
raise HttpError(HttpResponseBadRequest('POST body sent invalid JSON.'))
def parse_body(self, request):
content_type = self.get_content_type(request)
if content_type == 'application/graphql':
return {'query': request.body.decode()}
elif content_type == 'application/json':
return self.parse_request_json(request.body.decode('utf-8'))
elif content_type in ['application/x-www-form-urlencoded', 'multipart/form-data']:
operations_json = request.POST.get('operations')
map_json = request.POST.get('map')
if operations_json and map_json:
operations = self.parse_request_json(operations_json)
map = self.parse_request_json(map_json)
for file_id, f in request.FILES.items():
for name in map[file_id]:
segments = [maybe_int(s) for s in name.split('.')]
cur = operations
while len(segments) > 1:
cur = cur[segments.pop(0)]
cur[segments.pop(0)] = f
logger.info('parse_body %s', operations)
return operations
else:
return request.POST
return {}

ApiKey authentication in tastypie with mongoengine

Has anybody had success implementing ApiKey for User from mongoengine.django.auth for use with tastypie ApiKeyAuthentication?
I'm aware of the previous posts on the matter, but they address ORM only, while i'm trying to set it up for mongoengine. Also, it seems that tastypie's own ApiKey class heavily relies on relational structure (using related field api_key of User)
thanks in advance!
following this thread https://github.com/mitar/django-tastypie-mongoengine/issues/25 i've created MongoUser class with api_key field
# models.py (or documents.py)
from mongoengine.django.auth import User
class MongoUser(User):
"""
Subclass of mongoengine.django.auth.User with email as username
and API key for authentication.
"""
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['password']
api_key = StringField(max_length=256, default='')
api_key_created = DateTimeField(help_text=_(u'Created'))
def save(self, *args, **kwargs):
if not self.api_key:
self.set_api_key()
return super(MongoUser, self).save(*args, **kwargs)
def set_api_key(self):
self.api_key = self.generate_key()
self.api_key_created = datetime.now()
def generate_key(self):
new_uuid = uuid.uuid4()
return hmac.new(str(new_uuid), digestmod=sha1).hexdigest()
added a signal (the usual):
# resources.py
from mongoengine import signals
from myapp import models
signals.post_save.connect(create_api_key, sender=models.MongoUser)
and then subclassed tastypie.ApiKeyAuthentication with the following:
# resources.py
class CustomApiKeyAuthentication(ApiKeyAuthentication):
"""
Authenticates everyone if the request is GET otherwise performs
ApiKeyAuthentication.
"""
def is_mongouser_authenticated(self, request):
"""
Custom solution for MongoUser ApiKey authentication.
ApiKey here is not a class (as it is realized in ORM approach),
but a field MongoUser class.
"""
username, api_key = super(CustomApiKeyAuthentication,
self).extract_credentials(request)
try:
models.MongoUser.objects.get(username=username, api_key=api_key)
except models.MongoUser.DoesNotExist:
return False
return True
def is_authenticated(self, request, **kwargs):
"""
Custom solution for `is_authenticated` function: MongoUsers has got
authenticated through custom api_key check.
"""
if request.method == 'GET':
return True
try:
is_authenticated = super(CustomApiKeyAuthentication,
self).is_authenticated(request, **kwargs)
except TypeError as e:
if "MongoUser" in str(e):
is_authenticated = self.is_mongouser_authenticated(request)
else:
is_authenticated = False
return is_authenticated