Пример #1
0
        def _reconstruct_request_files():
            """
            Reconstruct the file(s) from the file cache (if any).
            Returns a dictionary of field name to cached file
            """
            reconstructed_files = {}

            cached_object = cache.get(CACHE_KEYS["object"])
            # Reconstruct the files from cached object
            if not cached_object:
                log("Warning: no cached_object")
                return

            if type(cached_object) != self.model:
                # Do not use cache if the model doesn't match this model
                log(f"Warning: cached_object is not of type {self.model}")
                return

            query_dict = request.POST

            for field in self.model._meta.get_fields():
                if not (isinstance(field, FileField)
                        or isinstance(field, ImageField)):
                    continue

                cached_file = self._file_cache.get(
                    format_cache_key(model=self.model.__name__,
                                     field=field.name))

                # If a file was uploaded, the field is omitted from the POST since it's in request.FILES
                if not query_dict.get(field.name):
                    if not cached_file:
                        log(f"Warning: Could not find file cached for field {field.name}"
                            )
                    else:
                        reconstructed_files[field.name] = cached_file

            return reconstructed_files
Пример #2
0
    def test_save_as_continue_true_should_not_redirect_to_changelist(self):
        item = self.item
        # Load the Change Item Page
        ItemAdmin.save_as_continue = True

        # Upload new image and remove file
        i2 = SimpleUploadedFile(
            name="test_image2.jpg",
            content=open(self.image_path, "rb").read(),
            content_type="image/jpeg",
        )
        # Request.POST
        data = {
            "id": item.id,
            "name": "name",
            "price": 2.0,
            "file": "",
            "file-clear": "on",
            "currency": Item.VALID_CURRENCIES[0][0],
            "_confirm_change": True,
            "_saveasnew": True,
        }

        # Set cache
        cache_item = Item(
            name=data["name"],
            price=data["price"],
            currency=data["currency"],
            image=i2,
        )
        file_cache = FileCache()
        file_cache.set(format_cache_key(model="Item", field="image"), i2)

        cache.set(CACHE_KEYS["object"], cache_item)
        cache.set(CACHE_KEYS["post"], data)

        # Click "Yes, I'm Sure"
        del data["_confirm_change"]
        data[CONFIRMATION_RECEIVED] = True

        with mock.patch.object(ItemAdmin, "message_user") as message_user:
            response = self.client.post(
                f"/admin/market/item/{self.item.id}/change/", data=data
            )
            # Should show message to user with correct obj and path
            message_user.assert_called_once()
            message = message_user.call_args[0][1]
            self.assertIn("/admin/market/item/2/change/", message)
            self.assertIn(data["name"], message)
            self.assertIn("You may edit it again below.", message)

        # Should not have redirected to changelist
        self.assertEqual(response.url, f"/admin/market/item/{self.item.id + 1}/change/")

        # Should not have changed existing item
        item.refresh_from_db()
        self.assertEqual(item.name, "Not name")
        self.assertEqual(item.file.name.count("test_file"), 1)
        self.assertEqual(item.image.name.count("test_image2"), 0)
        self.assertEqual(item.image.name.count("test_image"), 1)

        # Should have saved new item
        self.assertEqual(Item.objects.count(), 2)
        new_item = Item.objects.filter(id=item.id + 1).first()
        self.assertIsNotNone(new_item)
        self.assertEqual(new_item.name, data["name"])
        self.assertEqual(new_item.price, data["price"])
        self.assertEqual(new_item.currency, data["currency"])
        self.assertFalse(new_item.file)
        self.assertEqual(new_item.image.name.count("test_image2"), 1)

        # Should have cleared cache
        for key in CACHE_KEYS.values():
            self.assertIsNone(cache.get(key))
Пример #3
0
    def _change_confirmation_view(self, request, object_id, form_url,
                                  extra_context):
        # This code is taken from super()._changeform_view
        # https://github.com/django/django/blob/master/django/contrib/admin/options.py#L1575-L1592
        to_field = request.POST.get(TO_FIELD_VAR,
                                    request.GET.get(TO_FIELD_VAR))
        if to_field and not self.to_field_allowed(request, to_field):
            raise DisallowedModelAdminToField(
                "The field %s cannot be referenced." % to_field)

        model = self.model
        opts = model._meta

        if SAVE_AS_NEW in request.POST:
            object_id = None

        add = object_id is None
        if add:
            if not self.has_add_permission(request):
                raise PermissionDenied

            obj = None
        else:
            obj = self.get_object(request, unquote(object_id), to_field)
            if obj is None:
                return self._get_obj_does_not_exist_redirect(
                    request, opts, object_id)

            if not self.has_view_or_change_permission(request, obj):
                raise PermissionDenied

        fieldsets = self.get_fieldsets(request, obj)
        ModelForm = self.get_form(request,
                                  obj,
                                  change=not add,
                                  fields=flatten_fieldsets(fieldsets))

        form = ModelForm(request.POST, request.FILES, instance=obj)
        form_validated = form.is_valid()
        if form_validated:
            new_object = self.save_form(request, form, change=not add)
        else:
            new_object = form.instance
        formsets, inline_instances = self._create_formsets(request,
                                                           new_object,
                                                           change=not add)
        # End code from super()._changeform_view
        # form.is_valid() checks both errors and "is_bound"
        # If form has errors, show the errors on the form instead of showing confirmation page
        if not form_validated:
            log("Invalid Form: return early")
            log(form.errors)
            # We must ensure that we ask for confirmation when showing errors
            extra_context = self._add_confirmation_options_to_extra_context(
                extra_context)
            return super()._changeform_view(request, object_id, form_url,
                                            extra_context)

        add_or_new = add or SAVE_AS_NEW in request.POST
        # Get changed data to show on confirmation
        changed_data = self._get_changed_data(form, model, obj, add_or_new)

        changed_confirmation_fields = set(
            self.get_confirmation_fields(request, obj)) & set(
                changed_data.keys())
        if not bool(changed_confirmation_fields):
            log("No change detected")
            # No confirmation required for changed fields, continue to save
            return super()._changeform_view(request, object_id, form_url,
                                            extra_context)

        # Parse the original save action from request
        save_action = None
        # No cover: There would not be a case of not request.POST.keys() and form is valid
        for key in request.POST.keys():  # pragma: no cover
            if key in SAVE_ACTIONS:
                save_action = key
                break

        cleared_fields = []
        if form.is_multipart():
            log("Caching files")
            cache.set(CACHE_KEYS["object"], new_object, CACHE_TIMEOUT)

            # Save files as tempfiles
            for field_name in request.FILES:
                file = request.FILES[field_name]
                self._file_cache.set(
                    format_cache_key(model=model.__name__, field=field_name),
                    file)

            # Handle when files are cleared - since the `form` object would not hold that info
            cleared_fields = self._get_cleared_fields(request)

        log("Render Change Confirmation")
        title_action = _("adding") if add_or_new else _("changing")
        context = {
            **self.admin_site.each_context(request),
            "preserved_filters":
            self.get_preserved_filters(request),
            "title":
            f"{_('Confirm')} {title_action} {opts.verbose_name}",
            "subtitle":
            str(obj),
            "object_name":
            str(obj),
            "object_id":
            object_id,
            "app_label":
            opts.app_label,
            "model_name":
            opts.model_name,
            "opts":
            opts,
            "changed_data":
            changed_data,
            "add":
            add,
            "save_as_new":
            SAVE_AS_NEW in request.POST,
            "submit_name":
            save_action,
            "form":
            form,
            "cleared_fields":
            cleared_fields,
            "formsets":
            formsets,
            **(extra_context or {}),
        }
        return self.render_change_confirmation(request, context)