kopia lustrzana https://github.com/wagtail/wagtail
Implemented the UploadedFile mode to replace UploadedDocument and UploadedImage
- Rebase of #8085pull/11577/head
rodzic
58ebfe1fb1
commit
e6aebc65a8
|
@ -23,6 +23,7 @@ Changelog
|
|||
* Maintenance: Rename the React `Button` that only renders links (a element) to `Link` and remove unused prop & behavior that was non-compliant for aria role usage (Advik Kabra)
|
||||
* Maintenance: Set up an `wagtail.models.AbstractWorkflow` model to support future customisations around workflows (Hossein)
|
||||
* Maintenance: Improve `classnames` template tag to handle nested lists of strings, use template tag for admin `body` element (LB (Ben) Johnston)
|
||||
* Maintenance: Merge `UploadedDocument` and `UploadedImage` into new `UploadedFile` model for easier shared code usage (Advik Kabra, Karl Hobley)
|
||||
|
||||
|
||||
6.0 (07.02.2024)
|
||||
|
|
|
@ -44,6 +44,7 @@ depth: 1
|
|||
* Rename the React `Button` that only renders links (a element) to `Link` and remove unused prop & behavior that was non-compliant for aria role usage (Advik Kabra)
|
||||
* Set up an `wagtail.models.AbstractWorkflow` model to support future customisations around workflows (Hossein)
|
||||
* Improve `classnames` template tag to handle nested lists of strings, use template tag for admin `body` element (LB (Ben) Johnston)
|
||||
* Merge `UploadedDocument` and `UploadedImage` into new `UploadedFile` model for easier shared code usage (Advik Kabra, Karl Hobley)
|
||||
|
||||
|
||||
## Upgrade considerations
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import os.path
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import HttpResponseBadRequest, JsonResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
@ -10,13 +11,13 @@ from django.views.decorators.vary import vary_on_headers
|
|||
from django.views.generic.base import TemplateView, View
|
||||
|
||||
from wagtail.admin.views.generic import PermissionCheckedMixin
|
||||
from wagtail.models import UploadedFile
|
||||
|
||||
|
||||
class AddView(PermissionCheckedMixin, TemplateView):
|
||||
# subclasses need to provide:
|
||||
# - permission_policy
|
||||
# - template_name
|
||||
# - upload_model
|
||||
|
||||
# - edit_object_url_name
|
||||
# - delete_object_url_name
|
||||
|
@ -152,10 +153,12 @@ class AddView(PermissionCheckedMixin, TemplateView):
|
|||
return JsonResponse(self.get_invalid_response_data(form))
|
||||
else:
|
||||
# Some other field of the form has failed validation, e.g. a required metadata field
|
||||
# on a custom image model. Store the object as an upload_model instance instead and
|
||||
# on a custom image model. Store the object as an UploadedFile instance instead and
|
||||
# present the edit form so that it will become a proper object when successfully filled in
|
||||
self.upload_object = self.upload_model.objects.create(
|
||||
file=self.request.FILES["files[]"], uploaded_by_user=self.request.user
|
||||
self.upload_object = UploadedFile.objects.create(
|
||||
for_content_type=ContentType.objects.get_for_model(self.get_model()),
|
||||
file=self.request.FILES["files[]"],
|
||||
uploaded_by_user=self.request.user,
|
||||
)
|
||||
self.object = self.model(
|
||||
title=self.request.FILES["files[]"].name,
|
||||
|
@ -297,7 +300,6 @@ class CreateFromUploadView(View):
|
|||
# subclasses need to provide:
|
||||
# - edit_upload_url_name
|
||||
# - delete_upload_url_name
|
||||
# - upload_model
|
||||
# - upload_pk_url_kwarg
|
||||
# - edit_upload_form_prefix
|
||||
# - context_object_id_name
|
||||
|
@ -320,7 +322,11 @@ class CreateFromUploadView(View):
|
|||
self.model = self.get_model()
|
||||
self.form_class = self.get_edit_form_class()
|
||||
|
||||
self.upload = get_object_or_404(self.upload_model, id=upload_id)
|
||||
self.upload = get_object_or_404(
|
||||
UploadedFile,
|
||||
id=upload_id,
|
||||
for_content_type=ContentType.objects.get_for_model(self.model),
|
||||
)
|
||||
|
||||
if self.upload.uploaded_by_user != request.user:
|
||||
raise PermissionDenied
|
||||
|
@ -369,14 +375,17 @@ class CreateFromUploadView(View):
|
|||
|
||||
class DeleteUploadView(View):
|
||||
# subclasses need to provide:
|
||||
# - upload_model
|
||||
# - upload_pk_url_kwarg
|
||||
|
||||
http_method_names = ["post"]
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
upload_id = kwargs[self.upload_pk_url_kwarg]
|
||||
upload = get_object_or_404(self.upload_model, id=upload_id)
|
||||
upload = get_object_or_404(
|
||||
UploadedFile,
|
||||
id=upload_id,
|
||||
for_content_type=ContentType.objects.get_for_model(self.get_model()),
|
||||
)
|
||||
|
||||
if upload.uploaded_by_user != request.user:
|
||||
raise PermissionDenied
|
||||
|
|
|
@ -16,7 +16,7 @@ urlpatterns = [
|
|||
path("multiple/add/", multiple.AddView.as_view(), name="add_multiple"),
|
||||
path("multiple/<int:doc_id>/", multiple.EditView.as_view(), name="edit_multiple"),
|
||||
path(
|
||||
"multiple/create_from_uploaded_document/<int:uploaded_document_id>/",
|
||||
"multiple/create_from_uploaded_document/<int:uploaded_file_id>/",
|
||||
multiple.CreateFromUploadedDocumentView.as_view(),
|
||||
name="create_multiple_from_uploaded_document",
|
||||
),
|
||||
|
@ -26,7 +26,7 @@ urlpatterns = [
|
|||
name="delete_multiple",
|
||||
),
|
||||
path(
|
||||
"multiple/delete_upload/<int:uploaded_document_id>/",
|
||||
"multiple/delete_upload/<int:uploaded_file_id>/",
|
||||
multiple.DeleteUploadView.as_view(),
|
||||
name="delete_upload_multiple",
|
||||
),
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# Generated by Django 5.0.1 on 2024-01-30 18:19
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wagtaildocs', '0012_uploadeddocument'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='UploadedDocument',
|
||||
),
|
||||
]
|
|
@ -215,23 +215,3 @@ class Document(AbstractDocument):
|
|||
|
||||
# provides args: request
|
||||
document_served = Signal()
|
||||
|
||||
|
||||
class UploadedDocument(models.Model):
|
||||
"""
|
||||
Temporary storage for documents uploaded through the multiple doc uploader, when validation
|
||||
rules (e.g. required metadata fields) prevent creating a Document object from the document file
|
||||
alone. In this case, the document file is stored against this model, to be turned into a
|
||||
Document object once the full form has been filled in.
|
||||
"""
|
||||
|
||||
file = models.FileField(upload_to="uploaded_documents", max_length=200)
|
||||
uploaded_by_user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name=_("uploaded by user"),
|
||||
null=True,
|
||||
blank=True,
|
||||
editable=False,
|
||||
on_delete=models.SET_NULL,
|
||||
)
|
||||
uploaded_by_user.wagtail_reference_index_ignore = True
|
||||
|
|
|
@ -2,6 +2,7 @@ import json
|
|||
from unittest import mock
|
||||
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.test import TestCase, TransactionTestCase
|
||||
from django.test.utils import override_settings
|
||||
|
@ -12,7 +13,13 @@ from django.utils.http import urlencode
|
|||
from wagtail.admin.admin_url_finder import AdminURLFinder
|
||||
from wagtail.documents import get_document_model, models
|
||||
from wagtail.documents.tests.utils import get_test_document_file
|
||||
from wagtail.models import Collection, GroupCollectionPermission, Page, ReferenceIndex
|
||||
from wagtail.models import (
|
||||
Collection,
|
||||
GroupCollectionPermission,
|
||||
Page,
|
||||
ReferenceIndex,
|
||||
UploadedFile,
|
||||
)
|
||||
from wagtail.test.testapp.models import (
|
||||
CustomDocument,
|
||||
CustomDocumentWithAuthor,
|
||||
|
@ -1277,15 +1284,16 @@ class TestMultipleCustomDocumentUploaderWithRequiredField(TestMultipleDocumentUp
|
|||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
# Create an UploadedDocument for running tests on
|
||||
self.uploaded_document = models.UploadedDocument.objects.create(
|
||||
# Create an UploadedFile for running tests on
|
||||
self.uploaded_document = UploadedFile.objects.create(
|
||||
for_content_type=ContentType.objects.get_for_model(get_document_model()),
|
||||
file=get_test_document_file(),
|
||||
uploaded_by_user=self.user,
|
||||
)
|
||||
|
||||
def test_add_post(self):
|
||||
"""
|
||||
This tests that a POST request to the add view saves the document as an UploadedDocument
|
||||
This tests that a POST request to the add view saves the document as an UploadedFile
|
||||
and returns an edit form
|
||||
"""
|
||||
response = self.client.post(
|
||||
|
@ -1326,11 +1334,11 @@ class TestMultipleCustomDocumentUploaderWithRequiredField(TestMultipleDocumentUp
|
|||
|
||||
# Check JSON
|
||||
response_json = json.loads(response.content.decode())
|
||||
self.assertIn("uploaded_document_id", response_json)
|
||||
self.assertIn("uploaded_file_id", response_json)
|
||||
self.assertIn("form", response_json)
|
||||
self.assertIn("success", response_json)
|
||||
self.assertEqual(
|
||||
response_json["uploaded_document_id"],
|
||||
response_json["uploaded_file_id"],
|
||||
response.context["uploaded_document"].id,
|
||||
)
|
||||
self.assertTrue(response_json["success"])
|
||||
|
@ -1363,10 +1371,10 @@ class TestMultipleCustomDocumentUploaderWithRequiredField(TestMultipleDocumentUp
|
|||
|
||||
# Check JSON
|
||||
response_json = json.loads(response.content.decode())
|
||||
self.assertIn("uploaded_document_id", response_json)
|
||||
self.assertIn("uploaded_file_id", response_json)
|
||||
self.assertIn("form", response_json)
|
||||
self.assertEqual(
|
||||
response_json["uploaded_document_id"],
|
||||
response_json["uploaded_file_id"],
|
||||
response.context["uploaded_document"].id,
|
||||
)
|
||||
self.assertTrue(response_json["success"])
|
||||
|
@ -1419,11 +1427,11 @@ class TestMultipleCustomDocumentUploaderWithRequiredField(TestMultipleDocumentUp
|
|||
|
||||
# Check JSON
|
||||
response_json = json.loads(response.content.decode())
|
||||
self.assertIn("uploaded_document_id", response_json)
|
||||
self.assertIn("uploaded_file_id", response_json)
|
||||
self.assertIn("form", response_json)
|
||||
self.assertIn("success", response_json)
|
||||
self.assertEqual(
|
||||
response_json["uploaded_document_id"],
|
||||
response_json["uploaded_file_id"],
|
||||
response.context["uploaded_document"].id,
|
||||
)
|
||||
self.assertTrue(response_json["success"])
|
||||
|
@ -1438,10 +1446,10 @@ class TestMultipleCustomDocumentUploaderWithRequiredField(TestMultipleDocumentUp
|
|||
def test_create_from_upload_invalid_post(self):
|
||||
"""
|
||||
Posting an invalid form to the create_from_uploaded_document view throws a validation error
|
||||
and leaves the UploadedDocument intact
|
||||
and leaves the UploadedFile intact
|
||||
"""
|
||||
doc_count_before = CustomDocumentWithAuthor.objects.count()
|
||||
uploaded_doc_count_before = models.UploadedDocument.objects.count()
|
||||
uploaded_doc_count_before = UploadedFile.objects.count()
|
||||
|
||||
# Send request
|
||||
response = self.client.post(
|
||||
|
@ -1459,9 +1467,9 @@ class TestMultipleCustomDocumentUploaderWithRequiredField(TestMultipleDocumentUp
|
|||
)
|
||||
|
||||
doc_count_after = CustomDocumentWithAuthor.objects.count()
|
||||
uploaded_doc_count_after = models.UploadedDocument.objects.count()
|
||||
uploaded_doc_count_after = UploadedFile.objects.count()
|
||||
|
||||
# no changes to document / UploadedDocument count
|
||||
# no changes to document / UploadedFile count
|
||||
self.assertEqual(doc_count_after, doc_count_before)
|
||||
self.assertEqual(uploaded_doc_count_after, uploaded_doc_count_before)
|
||||
|
||||
|
@ -1497,7 +1505,7 @@ class TestMultipleCustomDocumentUploaderWithRequiredField(TestMultipleDocumentUp
|
|||
Posting a valid form to the create_from_uploaded_document view will create the document
|
||||
"""
|
||||
doc_count_before = CustomDocumentWithAuthor.objects.count()
|
||||
uploaded_doc_count_before = models.UploadedDocument.objects.count()
|
||||
uploaded_doc_count_before = UploadedFile.objects.count()
|
||||
|
||||
# Send request
|
||||
response = self.client.post(
|
||||
|
@ -1519,7 +1527,7 @@ class TestMultipleCustomDocumentUploaderWithRequiredField(TestMultipleDocumentUp
|
|||
)
|
||||
|
||||
doc_count_after = CustomDocumentWithAuthor.objects.count()
|
||||
uploaded_doc_count_after = models.UploadedDocument.objects.count()
|
||||
uploaded_doc_count_after = UploadedFile.objects.count()
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
@ -1530,7 +1538,7 @@ class TestMultipleCustomDocumentUploaderWithRequiredField(TestMultipleDocumentUp
|
|||
self.assertIn("doc_id", response_json)
|
||||
self.assertTrue(response_json["success"])
|
||||
|
||||
# Document should have been created, UploadedDocument deleted
|
||||
# Document should have been created, UploadedFile deleted
|
||||
self.assertEqual(doc_count_after, doc_count_before + 1)
|
||||
self.assertEqual(uploaded_doc_count_after, uploaded_doc_count_before - 1)
|
||||
|
||||
|
@ -1544,7 +1552,7 @@ class TestMultipleCustomDocumentUploaderWithRequiredField(TestMultipleDocumentUp
|
|||
|
||||
def test_delete_uploaded_document(self):
|
||||
"""
|
||||
This tests that a POST request to the delete view deletes the UploadedDocument
|
||||
This tests that a POST request to the delete view deletes the UploadedFile
|
||||
"""
|
||||
# Send request
|
||||
response = self.client.post(
|
||||
|
@ -1559,9 +1567,7 @@ class TestMultipleCustomDocumentUploaderWithRequiredField(TestMultipleDocumentUp
|
|||
|
||||
# Make sure the document is deleted
|
||||
self.assertFalse(
|
||||
models.UploadedDocument.objects.filter(
|
||||
id=self.uploaded_document.id
|
||||
).exists()
|
||||
UploadedFile.objects.filter(id=self.uploaded_document.id).exists()
|
||||
)
|
||||
|
||||
# Check JSON
|
||||
|
|
|
@ -12,14 +12,12 @@ from wagtail.admin.views.generic.multiple_upload import EditView as BaseEditView
|
|||
|
||||
from .. import get_document_model
|
||||
from ..forms import get_document_form, get_document_multi_form
|
||||
from ..models import UploadedDocument
|
||||
from ..permissions import permission_policy
|
||||
|
||||
|
||||
class AddView(BaseAddView):
|
||||
permission_policy = permission_policy
|
||||
template_name = "wagtaildocs/multiple/add.html"
|
||||
upload_model = UploadedDocument
|
||||
|
||||
edit_object_url_name = "wagtaildocs:edit_multiple"
|
||||
delete_object_url_name = "wagtaildocs:delete_multiple"
|
||||
|
@ -31,7 +29,7 @@ class AddView(BaseAddView):
|
|||
delete_upload_url_name = "wagtaildocs:delete_upload_multiple"
|
||||
edit_upload_form_prefix = "uploaded-document"
|
||||
context_upload_name = "uploaded_document"
|
||||
context_upload_id_name = "uploaded_document_id"
|
||||
context_upload_id_name = "uploaded_file_id"
|
||||
|
||||
def get_model(self):
|
||||
return get_document_model()
|
||||
|
@ -90,8 +88,7 @@ class DeleteView(BaseDeleteView):
|
|||
class CreateFromUploadedDocumentView(BaseCreateFromUploadView):
|
||||
edit_upload_url_name = "wagtaildocs:create_multiple_from_uploaded_document"
|
||||
delete_upload_url_name = "wagtaildocs:delete_upload_multiple"
|
||||
upload_model = UploadedDocument
|
||||
upload_pk_url_kwarg = "uploaded_document_id"
|
||||
upload_pk_url_kwarg = "uploaded_file_id"
|
||||
edit_upload_form_prefix = "uploaded-document"
|
||||
context_object_id_name = "doc_id"
|
||||
context_upload_name = "uploaded_document"
|
||||
|
@ -118,5 +115,7 @@ class CreateFromUploadedDocumentView(BaseCreateFromUploadView):
|
|||
|
||||
|
||||
class DeleteUploadView(BaseDeleteUploadView):
|
||||
upload_model = UploadedDocument
|
||||
upload_pk_url_kwarg = "uploaded_document_id"
|
||||
upload_pk_url_kwarg = "uploaded_file_id"
|
||||
|
||||
def get_model(self):
|
||||
return get_document_model()
|
||||
|
|
|
@ -24,7 +24,7 @@ urlpatterns = [
|
|||
path("multiple/add/", multiple.AddView.as_view(), name="add_multiple"),
|
||||
path("multiple/<int:image_id>/", multiple.EditView.as_view(), name="edit_multiple"),
|
||||
path(
|
||||
"multiple/create_from_uploaded_image/<int:uploaded_image_id>/",
|
||||
"multiple/create_from_uploaded_image/<int:uploaded_file_id>/",
|
||||
multiple.CreateFromUploadedImageView.as_view(),
|
||||
name="create_multiple_from_uploaded_image",
|
||||
),
|
||||
|
@ -34,7 +34,7 @@ urlpatterns = [
|
|||
name="delete_multiple",
|
||||
),
|
||||
path(
|
||||
"multiple/delete_upload/<int:uploaded_image_id>/",
|
||||
"multiple/delete_upload/<int:uploaded_file_id>/",
|
||||
multiple.DeleteUploadView.as_view(),
|
||||
name="delete_upload_multiple",
|
||||
),
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# Generated by Django 5.0.1 on 2024-01-30 18:19
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wagtailimages', '0025_alter_image_file_alter_rendition_file'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='UploadedImage',
|
||||
),
|
||||
]
|
|
@ -1331,23 +1331,3 @@ class Rendition(AbstractRendition):
|
|||
|
||||
class Meta:
|
||||
unique_together = (("image", "filter_spec", "focal_point_key"),)
|
||||
|
||||
|
||||
class UploadedImage(models.Model):
|
||||
"""
|
||||
Temporary storage for images uploaded through the multiple image uploader, when validation rules (e.g.
|
||||
required metadata fields) prevent creating an Image object from the image file alone. In this case,
|
||||
the image file is stored against this model, to be turned into an Image object once the full form
|
||||
has been filled in.
|
||||
"""
|
||||
|
||||
file = models.ImageField(upload_to="uploaded_images", max_length=200)
|
||||
uploaded_by_user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name=_("uploaded by user"),
|
||||
null=True,
|
||||
blank=True,
|
||||
editable=False,
|
||||
on_delete=models.SET_NULL,
|
||||
)
|
||||
uploaded_by_user.wagtail_reference_index_ignore = True
|
||||
|
|
|
@ -4,6 +4,7 @@ import urllib
|
|||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile, TemporaryUploadedFile
|
||||
from django.template.defaultfilters import filesizeformat
|
||||
from django.template.loader import render_to_string
|
||||
|
@ -16,12 +17,12 @@ from django.utils.safestring import mark_safe
|
|||
|
||||
from wagtail.admin.admin_url_finder import AdminURLFinder
|
||||
from wagtail.images import get_image_model
|
||||
from wagtail.images.models import UploadedImage
|
||||
from wagtail.images.utils import generate_signature
|
||||
from wagtail.models import (
|
||||
Collection,
|
||||
GroupCollectionPermission,
|
||||
Page,
|
||||
UploadedFile,
|
||||
get_root_collection_id,
|
||||
)
|
||||
from wagtail.test.testapp.models import (
|
||||
|
@ -2886,7 +2887,7 @@ class TestMultipleImageUploaderWithCustomImageModel(WagtailTestUtils, TestCase):
|
|||
|
||||
def test_unique_together_validation_error(self):
|
||||
"""
|
||||
If unique_together validation fails, create an UploadedImage and return a form so the
|
||||
If unique_together validation fails, create an UploadedFile and return a form so the
|
||||
user can fix it
|
||||
"""
|
||||
root_collection = Collection.get_first_root_node()
|
||||
|
@ -2895,7 +2896,7 @@ class TestMultipleImageUploaderWithCustomImageModel(WagtailTestUtils, TestCase):
|
|||
self.image.save()
|
||||
|
||||
image_count_before = CustomImage.objects.count()
|
||||
uploaded_image_count_before = UploadedImage.objects.count()
|
||||
uploaded_image_count_before = UploadedFile.objects.count()
|
||||
|
||||
response = self.client.post(
|
||||
reverse("wagtailimages:add_multiple"),
|
||||
|
@ -2908,9 +2909,9 @@ class TestMultipleImageUploaderWithCustomImageModel(WagtailTestUtils, TestCase):
|
|||
)
|
||||
|
||||
image_count_after = CustomImage.objects.count()
|
||||
uploaded_image_count_after = UploadedImage.objects.count()
|
||||
uploaded_image_count_after = UploadedFile.objects.count()
|
||||
|
||||
# an UploadedImage should have been created now, but not a CustomImage
|
||||
# an UploadedFile should have been created now, but not a CustomImage
|
||||
self.assertEqual(image_count_after, image_count_before)
|
||||
self.assertEqual(uploaded_image_count_after, uploaded_image_count_before + 1)
|
||||
|
||||
|
@ -3034,8 +3035,9 @@ class TestMultipleImageUploaderWithCustomRequiredFields(WagtailTestUtils, TestCa
|
|||
def setUp(self):
|
||||
self.user = self.login()
|
||||
|
||||
# Create an UploadedImage for running tests on
|
||||
self.uploaded_image = UploadedImage.objects.create(
|
||||
# Create an UploadedFile for running tests on
|
||||
self.uploaded_image = UploadedFile.objects.create(
|
||||
for_content_type=ContentType.objects.get_for_model(get_image_model()),
|
||||
file=get_test_image_file(),
|
||||
uploaded_by_user=self.user,
|
||||
)
|
||||
|
@ -3053,11 +3055,11 @@ class TestMultipleImageUploaderWithCustomRequiredFields(WagtailTestUtils, TestCa
|
|||
|
||||
def test_add_post(self):
|
||||
"""
|
||||
A POST request to the add view should create an UploadedImage rather than an image,
|
||||
A POST request to the add view should create an UploadedFile rather than an image,
|
||||
as we do not have enough data to pass CustomImageWithAuthor's validation yet
|
||||
"""
|
||||
image_count_before = CustomImageWithAuthor.objects.count()
|
||||
uploaded_image_count_before = UploadedImage.objects.count()
|
||||
uploaded_image_count_before = UploadedFile.objects.count()
|
||||
|
||||
response = self.client.post(
|
||||
reverse("wagtailimages:add_multiple"),
|
||||
|
@ -3069,9 +3071,9 @@ class TestMultipleImageUploaderWithCustomRequiredFields(WagtailTestUtils, TestCa
|
|||
)
|
||||
|
||||
image_count_after = CustomImageWithAuthor.objects.count()
|
||||
uploaded_image_count_after = UploadedImage.objects.count()
|
||||
uploaded_image_count_after = UploadedFile.objects.count()
|
||||
|
||||
# an UploadedImage should have been created now, but not a CustomImageWithAuthor
|
||||
# an UploadedFile should have been created now, but not a CustomImageWithAuthor
|
||||
self.assertEqual(image_count_after, image_count_before)
|
||||
self.assertEqual(uploaded_image_count_after, uploaded_image_count_before + 1)
|
||||
|
||||
|
@ -3137,10 +3139,10 @@ class TestMultipleImageUploaderWithCustomRequiredFields(WagtailTestUtils, TestCa
|
|||
def test_create_from_upload_invalid_post(self):
|
||||
"""
|
||||
Posting an invalid form to the create_from_uploaded_image view throws a validation error and leaves the
|
||||
UploadedImage intact
|
||||
UploadedFile intact
|
||||
"""
|
||||
image_count_before = CustomImageWithAuthor.objects.count()
|
||||
uploaded_image_count_before = UploadedImage.objects.count()
|
||||
uploaded_image_count_before = UploadedFile.objects.count()
|
||||
|
||||
# Send request
|
||||
response = self.client.post(
|
||||
|
@ -3156,9 +3158,9 @@ class TestMultipleImageUploaderWithCustomRequiredFields(WagtailTestUtils, TestCa
|
|||
)
|
||||
|
||||
image_count_after = CustomImageWithAuthor.objects.count()
|
||||
uploaded_image_count_after = UploadedImage.objects.count()
|
||||
uploaded_image_count_after = UploadedFile.objects.count()
|
||||
|
||||
# no changes to image / UploadedImage count
|
||||
# no changes to image / UploadedFile count
|
||||
self.assertEqual(image_count_after, image_count_before)
|
||||
self.assertEqual(uploaded_image_count_after, uploaded_image_count_before)
|
||||
|
||||
|
@ -3194,7 +3196,7 @@ class TestMultipleImageUploaderWithCustomRequiredFields(WagtailTestUtils, TestCa
|
|||
Posting a valid form to the create_from_uploaded_image view will create the image
|
||||
"""
|
||||
image_count_before = CustomImageWithAuthor.objects.count()
|
||||
uploaded_image_count_before = UploadedImage.objects.count()
|
||||
uploaded_image_count_before = UploadedFile.objects.count()
|
||||
|
||||
# Send request
|
||||
response = self.client.post(
|
||||
|
@ -3212,7 +3214,7 @@ class TestMultipleImageUploaderWithCustomRequiredFields(WagtailTestUtils, TestCa
|
|||
)
|
||||
|
||||
image_count_after = CustomImageWithAuthor.objects.count()
|
||||
uploaded_image_count_after = UploadedImage.objects.count()
|
||||
uploaded_image_count_after = UploadedFile.objects.count()
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
@ -3223,7 +3225,7 @@ class TestMultipleImageUploaderWithCustomRequiredFields(WagtailTestUtils, TestCa
|
|||
self.assertIn("image_id", response_json)
|
||||
self.assertTrue(response_json["success"])
|
||||
|
||||
# Image should have been created, UploadedImage deleted
|
||||
# Image should have been created, UploadedFile deleted
|
||||
self.assertEqual(image_count_after, image_count_before + 1)
|
||||
self.assertEqual(uploaded_image_count_after, uploaded_image_count_before - 1)
|
||||
|
||||
|
@ -3239,7 +3241,7 @@ class TestMultipleImageUploaderWithCustomRequiredFields(WagtailTestUtils, TestCa
|
|||
|
||||
def test_delete_uploaded_image(self):
|
||||
"""
|
||||
This tests that a POST request to the delete view deletes the UploadedImage
|
||||
This tests that a POST request to the delete view deletes the UploadedFile
|
||||
"""
|
||||
# Send request
|
||||
response = self.client.post(
|
||||
|
@ -3254,7 +3256,7 @@ class TestMultipleImageUploaderWithCustomRequiredFields(WagtailTestUtils, TestCa
|
|||
|
||||
# Make sure the image is deleted
|
||||
self.assertFalse(
|
||||
UploadedImage.objects.filter(id=self.uploaded_image.id).exists()
|
||||
UploadedFile.objects.filter(id=self.uploaded_image.id).exists()
|
||||
)
|
||||
|
||||
# Check JSON
|
||||
|
|
|
@ -15,7 +15,6 @@ from wagtail.admin.views.generic.multiple_upload import EditView as BaseEditView
|
|||
from wagtail.images import get_image_model
|
||||
from wagtail.images.fields import get_allowed_image_extensions
|
||||
from wagtail.images.forms import get_image_form, get_image_multi_form
|
||||
from wagtail.images.models import UploadedImage
|
||||
from wagtail.images.permissions import ImagesPermissionPolicyGetter, permission_policy
|
||||
from wagtail.images.utils import find_image_duplicates
|
||||
|
||||
|
@ -23,7 +22,6 @@ from wagtail.images.utils import find_image_duplicates
|
|||
class AddView(BaseAddView):
|
||||
permission_policy = ImagesPermissionPolicyGetter()
|
||||
template_name = "wagtailimages/multiple/add.html"
|
||||
upload_model = UploadedImage
|
||||
|
||||
edit_object_url_name = "wagtailimages:edit_multiple"
|
||||
delete_object_url_name = "wagtailimages:delete_multiple"
|
||||
|
@ -35,7 +33,7 @@ class AddView(BaseAddView):
|
|||
delete_upload_url_name = "wagtailimages:delete_upload_multiple"
|
||||
edit_upload_form_prefix = "uploaded-image"
|
||||
context_upload_name = "uploaded_image"
|
||||
context_upload_id_name = "uploaded_image_id"
|
||||
context_upload_id_name = "uploaded_file_id"
|
||||
|
||||
def get_model(self):
|
||||
return get_image_model()
|
||||
|
@ -131,8 +129,7 @@ class DeleteView(BaseDeleteView):
|
|||
class CreateFromUploadedImageView(BaseCreateFromUploadView):
|
||||
edit_upload_url_name = "wagtailimages:create_multiple_from_uploaded_image"
|
||||
delete_upload_url_name = "wagtailimages:delete_upload_multiple"
|
||||
upload_model = UploadedImage
|
||||
upload_pk_url_kwarg = "uploaded_image_id"
|
||||
upload_pk_url_kwarg = "uploaded_file_id"
|
||||
edit_upload_form_prefix = "uploaded-image"
|
||||
context_object_id_name = "image_id"
|
||||
context_upload_name = "uploaded_image"
|
||||
|
@ -160,5 +157,7 @@ class CreateFromUploadedImageView(BaseCreateFromUploadView):
|
|||
|
||||
|
||||
class DeleteUploadView(BaseDeleteUploadView):
|
||||
upload_model = UploadedImage
|
||||
upload_pk_url_kwarg = "uploaded_image_id"
|
||||
upload_pk_url_kwarg = "uploaded_file_id"
|
||||
|
||||
def get_model(self):
|
||||
return get_image_model()
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
# Generated by Django 4.2.7 on 2024-02-12 21:04
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
("contenttypes", "0002_remove_content_type_name"),
|
||||
("wagtailcore", "0092_alter_collectionviewrestriction_password_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="UploadedFile",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("file", models.FileField(max_length=200, upload_to="wagtail_uploads")),
|
||||
(
|
||||
"for_content_type",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="uploads",
|
||||
to="contenttypes.contenttype",
|
||||
verbose_name="for content type",
|
||||
),
|
||||
),
|
||||
(
|
||||
"uploaded_by_user",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
editable=False,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="uploaded by user",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -100,16 +100,6 @@ from .audit_log import ( # noqa: F401
|
|||
LogEntryQuerySet,
|
||||
ModelLogEntry,
|
||||
)
|
||||
from .collections import ( # noqa: F401
|
||||
BaseCollectionManager,
|
||||
Collection,
|
||||
CollectionManager,
|
||||
CollectionMember,
|
||||
CollectionViewRestriction,
|
||||
GroupCollectionPermission,
|
||||
GroupCollectionPermissionManager,
|
||||
get_root_collection_id,
|
||||
)
|
||||
from .copying import _copy, _copy_m2m_relations, _extract_field_data # noqa: F401
|
||||
from .i18n import ( # noqa: F401
|
||||
BootstrapTranslatableMixin,
|
||||
|
@ -120,6 +110,17 @@ from .i18n import ( # noqa: F401
|
|||
bootstrap_translatable_model,
|
||||
get_translatable_models,
|
||||
)
|
||||
from .media import ( # noqa: F401
|
||||
BaseCollectionManager,
|
||||
Collection,
|
||||
CollectionManager,
|
||||
CollectionMember,
|
||||
CollectionViewRestriction,
|
||||
GroupCollectionPermission,
|
||||
GroupCollectionPermissionManager,
|
||||
UploadedFile,
|
||||
get_root_collection_id,
|
||||
)
|
||||
from .reference_index import ReferenceIndex # noqa: F401
|
||||
from .sites import Site, SiteManager, SiteRootPath # noqa: F401
|
||||
from .specific import SpecificMixin
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
from django.conf import settings
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models
|
||||
from django.utils.html import format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
|
@ -188,3 +190,29 @@ class GroupCollectionPermission(models.Model):
|
|||
unique_together = ("group", "collection", "permission")
|
||||
verbose_name = _("group collection permission")
|
||||
verbose_name_plural = _("group collection permissions")
|
||||
|
||||
|
||||
class UploadedFile(models.Model):
|
||||
"""
|
||||
Temporary storage for media fields uploaded through the multiple image/document uploader.
|
||||
When validation rules (e.g. required metadata fields) prevent creating an Image/Document object from the file alone.
|
||||
In this case, the file is stored against this model, to be turned into an Image/Document object once the full form
|
||||
has been filled in.
|
||||
"""
|
||||
|
||||
for_content_type = models.ForeignKey(
|
||||
ContentType,
|
||||
verbose_name=_("for content type"),
|
||||
related_name="uploads",
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
)
|
||||
file = models.FileField(upload_to="wagtail_uploads", max_length=200)
|
||||
uploaded_by_user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name=_("uploaded by user"),
|
||||
null=True,
|
||||
blank=True,
|
||||
editable=False,
|
||||
on_delete=models.SET_NULL,
|
||||
)
|
|
@ -15,7 +15,7 @@ import wagtail.contrib.table_block.blocks
|
|||
import wagtail.fields
|
||||
import wagtail.images.blocks
|
||||
import wagtail.images.models
|
||||
import wagtail.models.collections
|
||||
import wagtail.models.media
|
||||
import wagtail.search.index
|
||||
import wagtail.test.testapp.models
|
||||
|
||||
|
@ -1043,7 +1043,7 @@ class Migration(migrations.Migration):
|
|||
(
|
||||
"collection",
|
||||
models.ForeignKey(
|
||||
default=wagtail.models.collections.get_root_collection_id,
|
||||
default=wagtail.models.media.get_root_collection_id,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="+",
|
||||
to="wagtailcore.collection",
|
||||
|
@ -2244,7 +2244,7 @@ class Migration(migrations.Migration):
|
|||
(
|
||||
"collection",
|
||||
models.ForeignKey(
|
||||
default=wagtail.models.collections.get_root_collection_id,
|
||||
default=wagtail.models.media.get_root_collection_id,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="+",
|
||||
to="wagtailcore.collection",
|
||||
|
@ -2341,7 +2341,7 @@ class Migration(migrations.Migration):
|
|||
(
|
||||
"collection",
|
||||
models.ForeignKey(
|
||||
default=wagtail.models.collections.get_root_collection_id,
|
||||
default=wagtail.models.media.get_root_collection_id,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="+",
|
||||
to="wagtailcore.collection",
|
||||
|
@ -2622,7 +2622,7 @@ class Migration(migrations.Migration):
|
|||
(
|
||||
"collection",
|
||||
models.ForeignKey(
|
||||
default=wagtail.models.collections.get_root_collection_id,
|
||||
default=wagtail.models.media.get_root_collection_id,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="+",
|
||||
to="wagtailcore.collection",
|
||||
|
@ -3891,7 +3891,7 @@ class Migration(migrations.Migration):
|
|||
(
|
||||
"collection",
|
||||
models.ForeignKey(
|
||||
default=wagtail.models.collections.get_root_collection_id,
|
||||
default=wagtail.models.media.get_root_collection_id,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="+",
|
||||
to="wagtailcore.collection",
|
||||
|
@ -3936,7 +3936,7 @@ class Migration(migrations.Migration):
|
|||
(
|
||||
"collection",
|
||||
models.ForeignKey(
|
||||
default=wagtail.models.collections.get_root_collection_id,
|
||||
default=wagtail.models.media.get_root_collection_id,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="+",
|
||||
to="wagtailcore.collection",
|
||||
|
@ -4055,7 +4055,7 @@ class Migration(migrations.Migration):
|
|||
(
|
||||
"collection",
|
||||
models.ForeignKey(
|
||||
default=wagtail.models.collections.get_root_collection_id,
|
||||
default=wagtail.models.media.get_root_collection_id,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="+",
|
||||
to="wagtailcore.collection",
|
||||
|
|
Ładowanie…
Reference in New Issue