简体   繁体   中英

Return JsonResponse from function in Django REST

I have a query in which all fields are required. I want to make sure they're full. It turns out a lot of duplicate code. So I decided to create a function to which I pass values from a field. But the request continues rather than sending a response code 400.

My views.py

def pay(request):
    body = request.body.decode('utf-8')
    if not body:
        return JsonResponse({
            'status': 'failed',
            'errors': {
                'code': 400,
                'message': 'empty query'
            },
        })
    body = json.loads(body)
    phone = body.get('phone')
    amount = body.get('amount')
    merch_name = body.get('merchant_name')
    #check_field(phone)
    if not phone:
        return JsonResponse({
            'status': 'failed',
            'errors': {
                'code': 400,
                'message': 'phone field is empty'
            },
        })
    if not amount:
        return JsonResponse({
            'status': 'failed',
            'errors': {
                'code': 400,
                'message': 'amount field is empty'
            },
        })
    if not merch_name:
        return JsonResponse({
            'status': 'failed',
            'errors': {
                'code': 400,
                'message': 'merch_name field is empty'
            },
        })

My function:

def check_field(field):
    if not field:
        logger.info('its work')
        return JsonResponse({
            'status': 'failed',
            'errors': {
                'code': 400,
                'message': '{} field is empty'.format(field)
            },
        })

How i can fix it?

I think your code is almost right, but it should return a JsonResponse object if check_field function call's return value is not None . (If the function call does not return any value, it actually return None )

def pay(request):
    ...
    check_result = check_field(phone)
    if check_result is not None:
        return check_result
    ... (repeat ...)

Anyway, I suggest you should try Django REST Framework's Serializer . It makes these parameter checking problems easily.

That's because the returned value from the called function ( check_field(phone) ) is not propagated beyond the caller -- the pay function. It's pay method's responsibility to take decisions based on the return from check_field .

You have a couple of options:

  • keep the earlier long function with a helper function that gets you you the JsonResponse for the passed field eg:

     def get_400_response(field_name): return JsonResponse({ 'status': 'failed', 'errors': { 'code': 400, 'message': f'{field_name} can not be empty' }, })

    and from the pay function eg:

     if not phone: return get_400_response('phone') # note the `return` here
  • if you want to use check_field , you can either keep it as is and then check from pay that if the return is a JsonReponse , if so return, otherwise save it as a valid value:

     phone = body.get('phone') if isinstance(phone, JsonResponse): return phone
  • Another option would be to raise an exception eg ValidationError from the check_field function, and rather than doing isinstance check, handle that in a try - except , so essentially duck-typing.


Better approach: it would be a better idea to accumulate all the errors from all fields and send them as a single response, it increases usability. So you can create a function that do the validations solely (leveraging other tiny functions as needed) and send either the validated data or a ValidationError for invalid data, and you can handle it from pay .

Note that your check_field does not work as is, because field refers to the value not the name of the variable you pass in.

Using exceptions avoids code duplication:

from django.core.exceptions import ValidationError

def pay(request):
    try
        body = request.body.decode('utf-8')
        if not body:
            raise ValidationError('empty query')
        body = json.loads(body)
        for field_name in ['phone', 'amount', 'merchant_name']:
            check_field(body, field_name)
    except ValidationError as error:
        return JsonResponse({
            'status': 'failed',
            'errors': {
                'code': 400,
                'message': error.message
            },
        })

 def check_field(data, field_name):
     if not data.get(field_name):
         raise ValidationError('{} field is empty'.format(field_name)

I second the suggestion of @youngminz to look into Django REST Framework serializers. Alternatively, you can also use a regular Django form.

If amount is a number and 0 is a valid value, you have to use data.get(field_name) is None for the check.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM