0

I have a function that defines a variable inside of it called animal. I also have a decorator for that function, but it needs the variable that's defined in the original function.

Either I need the animal variable from the original function, or I need to somehow pass the model_data variable from the decorator into the original function. Any input?

Here is the code:

@csrf_exempt
@require_http_methods(["GET", "PUT", "DELETE"])
@parse_model_data
def base_animal(request, model_id):

    try:
        animal = Animal.objects.get(pk=model_id)
    except ObjectDoesNotExist:
        return json_error(error="No animal found for id: %s" % model_id)

    if request.method == "GET":
        return json_success(model=animal)

    if request.method == "DELETE":
        animal.delete()
        return json_success("Animal deleted.")

    if request.method == "PUT":
       animal.save()
       return json_success()

Here is the decorator function:

def parse_model_data(originalFunction):
  def parse(*args, **kwargs):
        request = args[0]
        model_data = request.get_json_body()
        if not model_data:
          return json_error("no json body detected")
        model_data = Animal.parse_model_data(model_data)
        for attr, value in model_data.items():
            setattr(animal, attr, value)
        return originalFunction(*args, **kwargs)

return parse
user1250991
  • 97
  • 2
  • 12
  • Make it part of the method signature so it's present on *args. – LMC Apr 02 '18 at 01:43
  • Can you provide an example? Make it a part of the method signature for the original function? And by it, do you mean model_data, or animal? – user1250991 Apr 02 '18 at 02:17
  • CHeck [this](https://stackoverflow.com/questions/1965607/how-can-i-pass-a-variable-in-a-decorator-to-functions-argument-in-a-decorated-f), it might help – LMC Apr 02 '18 at 02:33
  • Yes, I did see that, I want to do the opposite. I want to pass an argument TO the decorator from the original function. – user1250991 Apr 02 '18 at 02:39
  • The decorator magic works based on the same arguments of the original function so as I said add an arg to the signature or... try to add the value to your request object ;-). – LMC Apr 02 '18 at 02:47

1 Answers1

3

A decorator is not meant for this. It cannot access an object created inside the function because that object does not exist yet when the decorated function, parse, is called.

It seems what you need here is a plain simple function.

def parse(animal, request):
    model_data = request.get_json_body()
    if not model_data:
      return json_error("no json body detected")
    model_data = Animal.parse_model_data(model_data)
    for attr, value in model_data.items():
        setattr(animal, attr, value)

@csrf_exempt
@require_http_methods(["GET", "PUT", "DELETE"])
def base_animal(request, model_id):
    try:
        animal = Animal.objects.get(pk=model_id)
    except ObjectDoesNotExist:
        return json_error(error="No animal found for id: %s" % model_id)

    # Here we use our parse function
    # It cannot be called before that point since animal did not exist
    parse(animal, request)

    ...
Olivier Melançon
  • 21,584
  • 4
  • 41
  • 73
  • Ok thanks. I thought that a normal function was the way to go, but the task i was assigned was to "implement a decorator function" for this purpose. – user1250991 Apr 02 '18 at 15:47
  • Maybe what was meant is to have a decorator use the model_id to fetch the animal and inject it as argument. Would that make sense? – Olivier Melançon Apr 02 '18 at 16:00