Hey,
So I have some shared extra processing that needs to happen on a bunch of CBV methods (such as saving related objects upon form_valid()).
So I have used the @method_decorator(decorator_name, name="target_method) to add that processing upon form_valid().
That works fine - however I did have to modify the target_method to accommodate this, as the decorator needs extra arguments.
For instance:
@method_decorator(save_related, name="form_valid")
class SomeCreateView(...):
...
def form_valid(self, form, formset):
self.object = form.save()
formset.instance = self.object
formset.save()
# return HttpResponseRedirect(self.get_success_url())
return {"httpresponse":HttpResponseRedirect(self.get_success_url()), "saved_instance": self.object, "mapping": self.related_field_map}
The decorator:
def save_related(func):
@functools.wraps(func)
def wrapper_test_decor(*args, **kwargs):
""" input: args[0]: Form obj (form)
args[1]: Formset obj (Form as well - but of the class of the inlines)
"""
value = func(*args, **kwargs)
obj = value["saved_instance"]
mapping = value["mapping"]
for src, dest in mapping:
attr_chain = src.split(sep=".") # since the value to fetch may span multiple foreign keys
attr = attr_chain.pop(0)
tmp_obj = getattr(obj, attr)
while attr and tmp_obj: # attr. will be a foreign Key model, until the last one where it will be the value to set to dest
attr = attr_chain.pop(0) if len(attr_chain) > 0 else None
tmp_obj = getattr(tmp_obj, attr) if attr else tmp_obj
setattr(obj, dest, tmp_obj)
# save the resulting object, after all related fields have been set
obj.save()
return value["httpresponse"]
return wrapper_test_decor
So basically, the form_valid() code had to be modified so that it returns the extra arguments the decorator needs to do its job. Among those arguments is the HttpResponse() the form_valid() would normally returns. Then the decorators returns that after doing its extra processing.
With a regular decorator, I could simply do:
@mydec(extra_argument=foo)
def decorated_method(...)
...
def mydec(extra_argument):
# extra stuff available here
However, with @method_decorator, I can’t seem to be able to find an equivalent way to pass extra arguments to the decorator. Of course my current setup works fine - I just don’t like the idea of having to modify the decorated method in order to accomodate the needs of the decorators, especially since those modifications ONLY have to do with passing arguments…
Any way to accomplish this better? I’m guessing there may be a better way to save related fields based on submitted form data. However more generally I’m still interested in passing extra arguments to decorators as I’ll be using them for other stuff (such as outputting pdfs).