kopia lustrzana https://github.com/wagtail/wagtail
Support custom document models with required fields
Same approach as 4023a90d6e
for images - define an UploadedDocument model to hold the uploaded file up to the point where the required metadata has been supplied.
pull/6701/head
rodzic
c29a92f801
commit
b13641e936
|
@ -25,8 +25,6 @@ Here's an example:
|
|||
# Custom field example:
|
||||
source = models.CharField(
|
||||
max_length=255,
|
||||
# This must be set to allow Wagtail to create a document instance
|
||||
# on upload.
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
|
@ -36,12 +34,9 @@ Here's an example:
|
|||
'source',
|
||||
)
|
||||
|
||||
.. note::
|
||||
.. versionchanged:: 2.12
|
||||
|
||||
Fields defined on a custom document model must either be set as non-required
|
||||
(``blank=True``), or specify a default value. This is because uploading the
|
||||
document and entering custom data happens as two separate actions. Wagtail
|
||||
needs to be able to create a document record immediately on upload.
|
||||
Fields on a custom document model can now be defined as required (``blank=False``).
|
||||
|
||||
Then in your settings module:
|
||||
|
||||
|
|
|
@ -12,7 +12,9 @@ urlpatterns = [
|
|||
|
||||
path('multiple/add/', multiple.add, name='add_multiple'),
|
||||
path('multiple/<int:doc_id>/', multiple.edit, name='edit_multiple'),
|
||||
path('multiple/create_from_uploaded_document/<int:uploaded_document_id>/', multiple.create_from_uploaded_document, name='create_multiple_from_uploaded_document'),
|
||||
path('multiple/<int:doc_id>/delete/', multiple.delete, name='delete_multiple'),
|
||||
path('multiple/delete_upload/<int:uploaded_document_id>/', multiple.delete_upload, name='delete_upload_multiple'),
|
||||
|
||||
path('chooser/', chooser.chooser, name='chooser'),
|
||||
path('chooser/<int:document_id>/', chooser.document_chosen, name='document_chosen'),
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 3.0.10 on 2020-12-20 01:55
|
||||
|
||||
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),
|
||||
('wagtaildocs', '0011_add_choose_permissions'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='UploadedDocument',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('file', models.FileField(max_length=200, upload_to='uploaded_documents')),
|
||||
('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')),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -208,3 +208,17 @@ class Document(AbstractDocument):
|
|||
|
||||
|
||||
document_served = Signal(providing_args=['request'])
|
||||
|
||||
|
||||
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
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
{% include "wagtailadmin/shared/non_field_errors.html" %}
|
||||
|
||||
<form action="{% url 'wagtaildocs:edit_multiple' doc.id %}" method="POST" enctype="multipart/form-data" novalidate>
|
||||
<form action="{{ edit_action }}" method="POST" enctype="multipart/form-data" novalidate>
|
||||
<ul class="fields">
|
||||
{% csrf_token %}
|
||||
{% for field in form %}
|
||||
|
@ -14,7 +14,7 @@
|
|||
{% endfor %}
|
||||
<li>
|
||||
<input type="submit" value="{% trans 'Update' %}" class="button" />
|
||||
<a href="{% url 'wagtaildocs:delete_multiple' doc.id %}" class="delete button button-secondary no">{% trans "Delete" %}</a>
|
||||
<a href="{{ delete_action }}" class="delete button button-secondary no">{% trans "Delete" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
|
|
|
@ -11,7 +11,8 @@ from django.urls import reverse
|
|||
from wagtail.core.models import Collection, GroupCollectionPermission, Page
|
||||
from wagtail.documents import get_document_model, models
|
||||
from wagtail.documents.tests.utils import get_test_document_file
|
||||
from wagtail.tests.testapp.models import CustomDocument, EventPage, EventPageRelatedLink
|
||||
from wagtail.tests.testapp.models import (
|
||||
CustomDocument, CustomDocumentWithAuthor, EventPage, EventPageRelatedLink)
|
||||
from wagtail.tests.utils import WagtailTestUtils
|
||||
|
||||
|
||||
|
@ -581,11 +582,11 @@ class TestMultipleDocumentUploader(TestCase, WagtailTestUtils):
|
|||
"""
|
||||
edit_post_data = {
|
||||
'title': "New title!",
|
||||
'tags': "",
|
||||
'tags': "cromarty, finisterre",
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
self.login()
|
||||
self.user = self.login()
|
||||
|
||||
# Create a document for running tests on
|
||||
self.doc = get_document_model().objects.create(
|
||||
|
@ -596,7 +597,7 @@ class TestMultipleDocumentUploader(TestCase, WagtailTestUtils):
|
|||
def check_doc_after_edit(self):
|
||||
self.doc.refresh_from_db()
|
||||
self.assertEqual(self.doc.title, "New title!")
|
||||
self.assertFalse(self.doc.tags.all())
|
||||
self.assertIn('cromarty', self.doc.tags.names())
|
||||
|
||||
def check_form_media_in_response(self, response):
|
||||
# draftail should NOT be a standard JS include on this page
|
||||
|
@ -651,6 +652,8 @@ class TestMultipleDocumentUploader(TestCase, WagtailTestUtils):
|
|||
self.assertEqual(response.context['doc'].title, 'test.png')
|
||||
self.assertTrue(response.context['doc'].file_size)
|
||||
self.assertTrue(response.context['doc'].file_hash)
|
||||
self.assertEqual(response.context['edit_action'], '/admin/documents/multiple/%d/' % response.context['doc'].id)
|
||||
self.assertEqual(response.context['delete_action'], '/admin/documents/multiple/%d/delete/' % response.context['doc'].id)
|
||||
|
||||
# check that it is in the root collection
|
||||
doc = get_document_model().objects.get(title='test.png')
|
||||
|
@ -888,6 +891,216 @@ class TestMultipleCustomDocumentUploaderNoCollection(TestMultipleCustomDocumentU
|
|||
super().tearDownClass()
|
||||
|
||||
|
||||
@override_settings(WAGTAILDOCS_DOCUMENT_MODEL='tests.CustomDocumentWithAuthor')
|
||||
class TestMultipleCustomDocumentUploaderWithRequiredField(TestMultipleDocumentUploader):
|
||||
edit_post_data = dict(TestMultipleDocumentUploader.edit_post_data, author="William Shakespeare")
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
# Create an UploadedDocument for running tests on
|
||||
self.uploaded_document = models.UploadedDocument.objects.create(
|
||||
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
|
||||
and returns an edit form
|
||||
"""
|
||||
response = self.client.post(reverse('wagtaildocs:add_multiple'), {
|
||||
'files[]': SimpleUploadedFile('test.png', b"Simple text document"),
|
||||
}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response['Content-Type'], 'application/json')
|
||||
self.assertTemplateUsed(response, 'wagtaildocs/multiple/edit_form.html')
|
||||
|
||||
# Check document
|
||||
self.assertIn('uploaded_document', response.context)
|
||||
self.assertTrue(response.context['uploaded_document'].file.size)
|
||||
self.assertEqual(
|
||||
response.context['edit_action'],
|
||||
'/admin/documents/multiple/create_from_uploaded_document/%d/' % response.context['uploaded_document'].id
|
||||
)
|
||||
self.assertEqual(
|
||||
response.context['delete_action'],
|
||||
'/admin/documents/multiple/delete_upload/%d/' % response.context['uploaded_document'].id
|
||||
)
|
||||
|
||||
# Check form
|
||||
self.assertIn('form', response.context)
|
||||
self.assertEqual(
|
||||
set(response.context['form'].fields),
|
||||
set(get_document_model().admin_form_fields) - {'file', 'collection'},
|
||||
)
|
||||
self.assertEqual(response.context['form'].initial['title'], 'test.png')
|
||||
|
||||
# Check JSON
|
||||
response_json = json.loads(response.content.decode())
|
||||
self.assertIn('uploaded_document_id', response_json)
|
||||
self.assertIn('form', response_json)
|
||||
self.assertIn('success', response_json)
|
||||
self.assertEqual(response_json['uploaded_document_id'], response.context['uploaded_document'].id)
|
||||
self.assertTrue(response_json['success'])
|
||||
|
||||
# form should not contain a collection chooser
|
||||
self.assertNotIn('Collection', response_json['form'])
|
||||
|
||||
def test_add_post_with_collections(self):
|
||||
"""
|
||||
This tests that a POST request to the add view saves the document
|
||||
and returns an edit form, when collections are active
|
||||
"""
|
||||
|
||||
root_collection = Collection.get_first_root_node()
|
||||
evil_plans_collection = root_collection.add_child(name="Evil plans")
|
||||
|
||||
response = self.client.post(reverse('wagtaildocs:add_multiple'), {
|
||||
'files[]': SimpleUploadedFile('test.png', b"Simple text document"),
|
||||
'collection': evil_plans_collection.id
|
||||
}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response['Content-Type'], 'application/json')
|
||||
self.assertTemplateUsed(response, 'wagtaildocs/multiple/edit_form.html')
|
||||
|
||||
# Check document
|
||||
self.assertIn('uploaded_document', response.context)
|
||||
self.assertTrue(response.context['uploaded_document'].file.size)
|
||||
self.assertEqual(
|
||||
response.context['edit_action'],
|
||||
'/admin/documents/multiple/create_from_uploaded_document/%d/' % response.context['uploaded_document'].id
|
||||
)
|
||||
self.assertEqual(
|
||||
response.context['delete_action'],
|
||||
'/admin/documents/multiple/delete_upload/%d/' % response.context['uploaded_document'].id
|
||||
)
|
||||
|
||||
# Check form
|
||||
self.assertIn('form', response.context)
|
||||
self.assertEqual(
|
||||
set(response.context['form'].fields),
|
||||
set(get_document_model().admin_form_fields) - {'file'} | {'collection'},
|
||||
)
|
||||
self.assertEqual(response.context['form'].initial['title'], 'test.png')
|
||||
|
||||
# Check JSON
|
||||
response_json = json.loads(response.content.decode())
|
||||
self.assertIn('uploaded_document_id', response_json)
|
||||
self.assertIn('form', response_json)
|
||||
self.assertIn('success', response_json)
|
||||
self.assertEqual(response_json['uploaded_document_id'], response.context['uploaded_document'].id)
|
||||
self.assertTrue(response_json['success'])
|
||||
|
||||
# form should contain a collection chooser
|
||||
self.assertIn('Collection', response_json['form'])
|
||||
|
||||
def check_doc_after_edit(self):
|
||||
super().check_doc_after_edit()
|
||||
self.assertEqual(self.doc.author, "William Shakespeare")
|
||||
|
||||
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
|
||||
"""
|
||||
doc_count_before = CustomDocumentWithAuthor.objects.count()
|
||||
uploaded_doc_count_before = models.UploadedDocument.objects.count()
|
||||
|
||||
# Send request
|
||||
response = self.client.post(reverse('wagtaildocs:create_multiple_from_uploaded_document', args=(self.uploaded_document.id, )), {
|
||||
('uploaded-document-%d-title' % self.uploaded_document.id): "New title!",
|
||||
('uploaded-document-%d-tags' % self.uploaded_document.id): "",
|
||||
('uploaded-document-%d-author' % self.uploaded_document.id): "",
|
||||
}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
|
||||
doc_count_after = CustomDocumentWithAuthor.objects.count()
|
||||
uploaded_doc_count_after = models.UploadedDocument.objects.count()
|
||||
|
||||
# no changes to document / UploadedDocument count
|
||||
self.assertEqual(doc_count_after, doc_count_before)
|
||||
self.assertEqual(uploaded_doc_count_after, uploaded_doc_count_before)
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response['Content-Type'], 'application/json')
|
||||
|
||||
# Check form
|
||||
self.assertIn('form', response.context)
|
||||
self.assertIn('author', response.context['form'].fields)
|
||||
self.assertEqual(response.context['edit_action'], '/admin/documents/multiple/create_from_uploaded_document/%d/' % response.context['uploaded_document'].id)
|
||||
self.assertEqual(response.context['delete_action'], '/admin/documents/multiple/delete_upload/%d/' % response.context['uploaded_document'].id)
|
||||
self.assertFormError(response, 'form', 'author', "This field is required.")
|
||||
|
||||
# Check JSON
|
||||
response_json = json.loads(response.content.decode())
|
||||
self.assertIn('form', response_json)
|
||||
self.assertIn('New title!', response_json['form'])
|
||||
self.assertFalse(response_json['success'])
|
||||
|
||||
def test_create_from_upload(self):
|
||||
"""
|
||||
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()
|
||||
|
||||
# Send request
|
||||
response = self.client.post(reverse('wagtaildocs:create_multiple_from_uploaded_document', args=(self.uploaded_document.id, )), {
|
||||
('uploaded-document-%d-title' % self.uploaded_document.id): "New title!",
|
||||
('uploaded-document-%d-tags' % self.uploaded_document.id): "fairies, donkey",
|
||||
('uploaded-document-%d-author' % self.uploaded_document.id): "William Shakespeare",
|
||||
}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
|
||||
doc_count_after = CustomDocumentWithAuthor.objects.count()
|
||||
uploaded_doc_count_after = models.UploadedDocument.objects.count()
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response['Content-Type'], 'application/json')
|
||||
|
||||
# Check JSON
|
||||
response_json = json.loads(response.content.decode())
|
||||
self.assertIn('doc_id', response_json)
|
||||
self.assertTrue(response_json['success'])
|
||||
|
||||
# Document should have been created, UploadedDocument deleted
|
||||
self.assertEqual(doc_count_after, doc_count_before + 1)
|
||||
self.assertEqual(uploaded_doc_count_after, uploaded_doc_count_before - 1)
|
||||
|
||||
doc = CustomDocumentWithAuthor.objects.get(id=response_json['doc_id'])
|
||||
self.assertEqual(doc.title, 'New title!')
|
||||
self.assertEqual(doc.author, 'William Shakespeare')
|
||||
self.assertTrue(doc.file.name)
|
||||
self.assertTrue(doc.file_hash)
|
||||
self.assertTrue(doc.file_size)
|
||||
self.assertIn('donkey', doc.tags.names())
|
||||
|
||||
def test_delete_uploaded_document(self):
|
||||
"""
|
||||
This tests that a POST request to the delete view deletes the UploadedDocument
|
||||
"""
|
||||
# Send request
|
||||
response = self.client.post(reverse(
|
||||
'wagtaildocs:delete_upload_multiple', args=(self.uploaded_document.id, )
|
||||
), HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response['Content-Type'], 'application/json')
|
||||
|
||||
# Make sure the document is deleted
|
||||
self.assertFalse(models.UploadedDocument.objects.filter(id=self.uploaded_document.id).exists())
|
||||
|
||||
# Check JSON
|
||||
response_json = json.loads(response.content.decode())
|
||||
self.assertTrue(response_json['success'])
|
||||
|
||||
|
||||
class TestDocumentChooserView(TestCase, WagtailTestUtils):
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import os.path
|
||||
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import HttpResponseBadRequest, JsonResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.template.loader import render_to_string
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.encoding import force_str
|
||||
from django.urls import reverse
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.views.decorators.vary import vary_on_headers
|
||||
|
||||
|
@ -12,6 +14,7 @@ from wagtail.search.backends import get_search_backends
|
|||
|
||||
from .. import get_document_model
|
||||
from ..forms import get_document_form, get_document_multi_form
|
||||
from ..models import UploadedDocument
|
||||
from ..permissions import permission_policy
|
||||
|
||||
|
||||
|
@ -63,19 +66,42 @@ def add(request):
|
|||
'success': True,
|
||||
'doc_id': int(doc.id),
|
||||
'form': render_to_string('wagtaildocs/multiple/edit_form.html', {
|
||||
'doc': doc,
|
||||
'doc': doc, # only used for tests
|
||||
'edit_action': reverse('wagtaildocs:edit_multiple', args=(doc.id,)),
|
||||
'delete_action': reverse('wagtaildocs:delete_multiple', args=(doc.id,)),
|
||||
'form': DocumentMultiForm(
|
||||
instance=doc, prefix='doc-%d' % doc.id, user=request.user
|
||||
),
|
||||
}, request=request),
|
||||
})
|
||||
else:
|
||||
# Validation error
|
||||
elif 'file' in form.errors:
|
||||
# The uploaded file is invalid; reject it now
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error_message': '\n'.join(form.errors['file']),
|
||||
})
|
||||
else:
|
||||
# Some other field of the document form has failed validation, e.g. a required metadata
|
||||
# field on a custom document model. Store the document as an UploadedDocument instead
|
||||
# and present the edit form so that it will become a proper Document when successfully
|
||||
# filled in
|
||||
uploaded_doc = UploadedDocument.objects.create(
|
||||
file=request.FILES['files[]'], uploaded_by_user=request.user
|
||||
)
|
||||
doc = Document(title=request.FILES['files[]'].name, collection_id=request.POST.get('collection'))
|
||||
|
||||
# https://github.com/django/django/blob/stable/1.6.x/django/forms/util.py#L45
|
||||
'error_message': '\n'.join(['\n'.join([force_str(i) for i in v]) for k, v in form.errors.items()]),
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
|
||||
'uploaded_document_id': uploaded_doc.id,
|
||||
'form': render_to_string('wagtaildocs/multiple/edit_form.html', {
|
||||
'uploaded_document': uploaded_doc, # only used for tests
|
||||
'edit_action': reverse('wagtaildocs:create_multiple_from_uploaded_document', args=(uploaded_doc.id,)),
|
||||
'delete_action': reverse('wagtaildocs:delete_upload_multiple', args=(uploaded_doc.id,)),
|
||||
'form': DocumentMultiForm(
|
||||
instance=doc, prefix='uploaded-document-%d' % uploaded_doc.id, user=request.user
|
||||
),
|
||||
}, request=request),
|
||||
})
|
||||
else:
|
||||
# Instantiate a dummy copy of the form that we can retrieve validation messages and media from;
|
||||
|
@ -122,7 +148,9 @@ def edit(request, doc_id, callback=None):
|
|||
'success': False,
|
||||
'doc_id': int(doc_id),
|
||||
'form': render_to_string('wagtaildocs/multiple/edit_form.html', {
|
||||
'doc': doc,
|
||||
'doc': doc, # only used for tests
|
||||
'edit_action': reverse('wagtaildocs:edit_multiple', args=(doc_id,)),
|
||||
'delete_action': reverse('wagtaildocs:delete_multiple', args=(doc_id,)),
|
||||
'form': form,
|
||||
}, request=request),
|
||||
})
|
||||
|
@ -146,3 +174,75 @@ def delete(request, doc_id):
|
|||
'success': True,
|
||||
'doc_id': int(doc_id),
|
||||
})
|
||||
|
||||
|
||||
@require_POST
|
||||
def create_from_uploaded_document(request, uploaded_document_id):
|
||||
Document = get_document_model()
|
||||
DocumentMultiForm = get_document_multi_form(Document)
|
||||
|
||||
uploaded_doc = get_object_or_404(UploadedDocument, id=uploaded_document_id)
|
||||
|
||||
if not request.is_ajax():
|
||||
return HttpResponseBadRequest("Cannot POST to this view without AJAX")
|
||||
|
||||
if uploaded_doc.uploaded_by_user != request.user:
|
||||
raise PermissionDenied
|
||||
|
||||
doc = Document()
|
||||
form = DocumentMultiForm(
|
||||
request.POST, request.FILES, instance=doc, prefix='uploaded-document-%d' % uploaded_document_id, user=request.user
|
||||
)
|
||||
|
||||
if form.is_valid():
|
||||
# assign the file content from uploaded_doc to the image object, to ensure it gets saved to
|
||||
# Document's storage
|
||||
|
||||
doc.file.save(os.path.basename(uploaded_doc.file.name), uploaded_doc.file.file, save=False)
|
||||
doc.uploaded_by_user = request.user
|
||||
doc.file_size = doc.file.size
|
||||
doc.file.open()
|
||||
doc.file.seek(0)
|
||||
doc._set_file_hash(doc.file.read())
|
||||
doc.file.seek(0)
|
||||
form.save()
|
||||
|
||||
uploaded_doc.file.delete()
|
||||
uploaded_doc.delete()
|
||||
|
||||
# Reindex the document to make sure all tags are indexed
|
||||
for backend in get_search_backends():
|
||||
backend.add(doc)
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'doc_id': doc.id,
|
||||
})
|
||||
else:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'form': render_to_string('wagtaildocs/multiple/edit_form.html', {
|
||||
'uploaded_document': uploaded_doc, # only used for tests
|
||||
'edit_action': reverse('wagtaildocs:create_multiple_from_uploaded_document', args=(uploaded_doc.id,)),
|
||||
'delete_action': reverse('wagtaildocs:delete_upload_multiple', args=(uploaded_doc.id,)),
|
||||
'form': form,
|
||||
}, request=request),
|
||||
})
|
||||
|
||||
|
||||
@require_POST
|
||||
def delete_upload(request, uploaded_document_id):
|
||||
uploaded_doc = get_object_or_404(UploadedDocument, id=uploaded_document_id)
|
||||
|
||||
if not request.is_ajax():
|
||||
return HttpResponseBadRequest("Cannot POST to this view without AJAX")
|
||||
|
||||
if uploaded_doc.uploaded_by_user != request.user:
|
||||
raise PermissionDenied
|
||||
|
||||
uploaded_doc.file.delete()
|
||||
uploaded_doc.delete()
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
})
|
||||
|
|
|
@ -1902,7 +1902,7 @@ class TestMultipleImageUploaderWithCustomRequiredFields(TestCase, WagtailTestUti
|
|||
self.assertEqual(response['Content-Type'], 'application/json')
|
||||
|
||||
# Make sure the image is deleted
|
||||
self.assertFalse(CustomImageWithAuthor.objects.filter(id=self.uploaded_image.id).exists())
|
||||
self.assertFalse(UploadedImage.objects.filter(id=self.uploaded_image.id).exists())
|
||||
|
||||
# Check JSON
|
||||
response_json = json.loads(response.content.decode())
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
# Generated by Django 3.0.10 on 2020-12-20 14:52
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import taggit.managers
|
||||
import wagtail.core.models
|
||||
import wagtail.search.index
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wagtailcore', '0059_apply_collection_ordering'),
|
||||
('taggit', '0003_taggeditem_add_unique_index'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('tests', '0056_streampage_nested_streamblock'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CustomDocumentWithAuthor',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=255, verbose_name='title')),
|
||||
('file', models.FileField(upload_to='documents', verbose_name='file')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created at')),
|
||||
('file_size', models.PositiveIntegerField(editable=False, null=True)),
|
||||
('file_hash', models.CharField(blank=True, editable=False, max_length=40)),
|
||||
('author', models.CharField(max_length=255)),
|
||||
('collection', models.ForeignKey(default=wagtail.core.models.get_root_collection_id, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='wagtailcore.Collection', verbose_name='collection')),
|
||||
('tags', taggit.managers.TaggableManager(blank=True, help_text=None, through='taggit.TaggedItem', to='taggit.Tag', verbose_name='tags')),
|
||||
('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')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'document',
|
||||
'verbose_name_plural': 'documents',
|
||||
'abstract': False,
|
||||
},
|
||||
bases=(wagtail.search.index.Indexed, models.Model),
|
||||
),
|
||||
]
|
|
@ -990,6 +990,15 @@ class CustomDocument(AbstractDocument):
|
|||
]
|
||||
|
||||
|
||||
# Custom document model with a required field
|
||||
class CustomDocumentWithAuthor(AbstractDocument):
|
||||
author = models.CharField(max_length=255)
|
||||
|
||||
admin_form_fields = Document.admin_form_fields + (
|
||||
'author',
|
||||
)
|
||||
|
||||
|
||||
class StreamModel(models.Model):
|
||||
body = StreamField([
|
||||
('text', CharBlock()),
|
||||
|
|
Ładowanie…
Reference in New Issue