from deepmerge import Merger, STRATEGY_END from django.db.models.signals import class_prepared from django.utils.functional import curry from django_fsm import ( FSMFieldMixin, get_all_FIELD_transitions, get_available_FIELD_transitions, get_available_user_FIELD_transitions, ) from rest_framework.request import _hasattr, is_form_media_type, Request __all__ = ["patches"] def merge_dicts(config, path, base, nxt): if isinstance(base, list) and isinstance(nxt, list) and len(base) == len(nxt): data = [{**a, **b} for a, b in zip(base, nxt)] return data return STRATEGY_END merger = Merger( [(list, merge_dicts), (list, "append"), (dict, "merge")], ["override"], ["override"] ) def _patched_load_data_and_files(self): """ Parses the request content into `self.data`. """ if not _hasattr(self, "_data"): self._data, self._files = self._parse() if self._files: self._full_data = merger.merge(self._data, self._files) else: self._full_data = self._data # if a form media type, copy data & files refs to the underlying # http request so that closable objects are handled appropriately. if is_form_media_type(self.content_type): self._request._post = self.POST self._request._files = self.FILES def _modified_contribute_to_class(self, cls, name, **kwargs): self.base_cls = cls super(FSMFieldMixin, self).contribute_to_class(cls, name, **kwargs) setattr(cls, f"{self.name}_description", self.descriptor_class(self)) setattr( cls, "get_all_{0}_transitions".format(self.name), curry(get_all_FIELD_transitions, field=self), ) setattr( cls, "get_available_{0}_transitions".format(self.name), curry(get_available_FIELD_transitions, field=self), ) setattr( cls, "get_available_user_{0}_transitions".format(self.name), curry(get_available_user_FIELD_transitions, field=self), ) class_prepared.connect(self._collect_transitions) # Do not obscure related model in FSMKeyField # Pull request https://github.com/viewflow/django-fsm/pull/200/files#diff-7b4b9604f1842ff54833d14e644e9710 class Patches: def __init__(self): self._load_data_and_files = None self._contribute_to_class = None def on(self): self._load_data_and_files = Request._load_data_and_files self._contribute_to_class = FSMFieldMixin.contribute_to_class Request._load_data_and_files = _patched_load_data_and_files FSMFieldMixin.contribute_to_class = _modified_contribute_to_class def off(self): if self._load_data_and_files: Request._load_data_and_files = self._load_data_and_files if self._contribute_to_class: FSMFieldMixin.contribute_to_class = self._contribute_to_class patches = Patches()