흔들림 꼬리로 이미지 업로드
Heads up - This is an update of my earlier post Image Uploads in Wagtail Forms which was written for Wagtail v1.12, this new post is written for v2.10/v2.11.
문제는 귀하의 팀은 Wagtail CMS의 사용자 정의 폼 생성기를 좋아해서 사람들이 폼과 함께 그림을 업로드할 수 있도록 합니다.
솔루션 - CMS 관리자에서 필드를 편집할 때 선택할 수 있는 새 양식 필드 유형을 정의합니다. 이 필드 유형은 이미지 업로드라고 합니다.이 필드는 보기에 일반 업로드 필드로 표시되고 Wagtail 이미지 시스템처럼 파일 형식과 크기를 제한해야 합니다.
대상: 이미지 업로드 필드를 추가하면 폼 보기에 표시됩니다.
흔들림, 이미지 및 형식
만약 당신이 이곳의 기본 지식을 알고 있다면 건너뛰세요.
Wagtail는 Django Web Framework 위에 구축된 컨텐츠 관리 시스템(CMS)입니다.내가 Wagtail을 좋아하는 것은 Django 생태계와 일하는 방식을 포옹하는 데 있다.그것은 또한 매우 좋은 관리 인터페이스를 가지고 있어 사용자가 쉽게 내용과 상호작용할 수 있다.
Wagtail에는 이미지 업로드, 저장 및 서비스에 사용할 수 있는 내장된 인터페이스와 프레임워크가 있습니다.이것은 Wagtail Images로 적절하게 명명되었습니다. Using Images in Templates 또는 Advanced Image Usage에 대한 문서를 보고 더 많은 정보를 얻을 수 있습니다.
Wagtail은 사용자가 관리 인터페이스에서 자신의 폼을 구축할 수 있도록 하는 아주 좋은 Form Builder 모듈을 첨부했다.이 폼들은 일련의 필드가 있을 수 있다. 예를 들어 텍스트, 여러 줄 텍스트, 전자메일, URL, 복선상자 등이다. 이 필드는 웹 사이트 앞에서 볼 수 있는 폼 페이지를 구성한다.사용자는 기본값, 이 필드가 필요한지, 이 필드와 관련된 도움말 텍스트를 사용자 정의할 수 있습니다.
저희가 시작하기 전에.
우리가 사물을 바꾸기 시작하기 전에 중요한 것은 네가 아래의 항목을 완성하는 것이다.
Wagtailforms module가 설치되어 실행 중이며 폼을 사용할 수 있습니다.
'wagtail.contrib.forms'
을(를) INSTALLED_APPS
에 추가해야 합니다.Wagtail에서 양식에 이미지 업로드 필드 추가
우리의 변화를 기획하다
우리는 다음과 같은 사용자 상호작용을 실현하기를 희망한다.
1. AbstractFormField 클래스 확장
FormPage
류의 정의를 포함하는 모델 파일에는 FormField
류의 정의가 있어야 한다.원래 정의에서 AbstractFormField class 는 FORM_FIELD_CHOICES 의 고정 메타그룹을 사용합니다.필드 _ 형식을 덮어쓰는 추가 그룹을 선택해야 합니다.# models.py
from wagtail.contrib.forms.models import AbstractForm, AbstractFormField, FORM_FIELD_CHOICES
class FormField(AbstractFormField):
field_type = models.CharField(
verbose_name='field type',
max_length=16,
choices=list(FORM_FIELD_CHOICES) + [('image', 'Upload Image')]
)
page = ParentalKey('FormPage', related_name='form_fields', on_delete=models.CASCADE)
위의 코드에서 우리가wagtail에서 원시 코드FORM_FIELD_CHOICES
를 가져온 것을 볼 수 있습니다.contrib.형식모형.그리고 목록으로 변환해서 새로운 필드 형식을 추가한 다음 field_type
필드의choices 매개 변수에 사용합니다.네가 이렇게 할 때, 너는 make a migration, and run that migration 필요하다.테스트해 보세요. 관리자의 폼은 현재 이런 종류를 선택할 수 있지만, 다른 일을 많이 할 수 없습니다.
2, 확장 FormBuilder 클래스
모델 파일에서 확장된formbuilder 클래스를 만들어야 합니다.원본 정의에서 FormBuilder class 각 FormPage 실례에 저장된
field_type
목록을 바탕으로 폼을 구축합니다.우리는 문서의 Adding a custom field type에 관한 예시를 따를 수 있다.이 방법은 필드 이름 (이 예에서 "image") 을 기반으로 방법 이름
create_image_field
에 따라 이 방법을 호출하고 Django form widget 의 실례를 되돌려야 합니다.Wagtail을 사용하여 사용자 정의 이미지 필드를 만드는 것이 아니라 자신만의 WagtailImageField 를 사용할 수 있습니다.# models.py
from wagtail.contrib.forms.forms import FormBuilder
from wagtail.images.fields import WagtailImageField
class CustomFormBuilder(FormBuilder):
def create_image_field(self, field, options):
return WagtailImageField(**options)
위의 코드에서, 우리는 FormBuilder
과 wagtail.contrib.forms.forms
에서 WagtailImageField
을 가져온 다음, 새로운 클래스로 우리의 사용자 정의 wagtail.images.fields
를 만들었다.우리는createdFormBuilder
를 되돌려주고 제공하는 모든 옵션을 전달하는 방법create_date_field
을 추가했습니다.3. WagtailImageField를 사용하여 FormPage 클래스 설정
이 단계는 매우 간단합니다. 우리는 FormPage 모델의
CustomFormBuilder
정의를 덮어쓰기를 원합니다.이것은 매우 교묘한 방식입니다. Wagtail은 당신이 사용하는form_생성기를 덮어쓸 수 있습니다.# models.py
from wagtail.contrib.forms.models import AbstractForm
class FormPage(AbstractForm):
form_builder = CustomFormBuilder
#... rest of the FormPage definition
4. 양식 페이지 템플릿을 업데이트하여 파일 데이터를 적용합니다.
폼 페이지 보기에
form_builder
표시가 있어야 합니다. Wagtail이 제안한 구현은 폼에 파일 데이터를 제출하는 것을 허락하지 않습니다.<!-- templates/form_page.html -->
{% extends "base.html" %}
{% load wagtailcore_tags %}
{% block content %}
{{ self.intro }}
<form action="{% pageurl self %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" />
</form>
{% endblock %}
기본 폼과 유일한 차이점은 폼 속성에 <form />
을 추가했다는 것이다.만약 당신이 이렇게 하지 않는다면, 당신은 요청을 통해 어떤 파일도 보내지 않을 것이며, 이유를 제시하는 오류도 없을 것입니다.우리가 왜 이렇게 해야 하는지에 대한 더 많은 정보는 Django Docs File Uploads 페이지를 보고 깊이 있게 이해할 수 있습니다 enctype form attribute on MDN.
5. 이미지 업로드를 위한 컬렉션 선택 기능 추가
관리 인터페이스를 통해 이미지를 업로드할 때 각 이미지를 하나의 집합에 추가할 수 있는 옵션이 있습니다. 기본값은'루트'입니다. 이것은 이미지의 폴더와 같습니다.
우리는 양식 제출에 업로드된 모든 이미지를 "Root"에 저장하는 것이 아니라, 사용자가 각 양식의 이미지를 어느 페이지에 추가할지 선택할 수 있도록 하려고 합니다.
enctype="multipart/form-data"
# models.py
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.core.models import Collection
class FormPage(AbstractForm):
form_builder = CustomFormBuilder
# other fields...
uploaded_image_collection = models.ForeignKey(
'wagtailcore.Collection',
null=True,
blank=True,
on_delete=models.SET_NULL,
)
# content_panels...
settings_panels = AbstractForm.settings_panels + [
FieldPanel('uploaded_image_collection')
]
def get_uploaded_image_collection(self):
"""
Returns a Wagtail Collection, using this form's saved value if present,
otherwise returns the 'Root' Collection.
"""
collection = self.uploaded_image_collection
return collection or Collection.get_first_root_node()
위의 코드에서 우리는 Collection
모델을 가져왔고 Collections
모델에 FormPage
라는 새로운 필드를 추가했다. 이것은 uploaded_image_collection
모델의 ForeignKey relation이다.페이지에서 검색하고 get_first_root_node 방법을 통해 루트 집합으로 돌아가는 방법도 추가했습니다. (Wagtail 집합은 트리 구조를 사용하여 트리 구조를 정의하기 때문입니다.)
이 코드 단계를 완료하려면 make a migration, and run that migration 이 필요합니다.
6. 검증 후 이미지 (파일) 데이터 처리
이제 FormPage 클래스
'wagtailCore.Collection'
를 덮어씁니다.process_form_submission method의 원시적인 정의는 처리process_form_submission
데이터 이외의 어떠한 내용의 개념이 없다.그것은 폼 제출 실례에 저장하기 위해 정리된 데이터를 JSON으로 변환하기만 하면 된다.모든 필드를 훑어보고 Wagtail Image Field의 모든 실례를 찾은 다음 데이터를 가져와 이 파일 데이터로 새 Wagtail 이미지를 만들고 응답에 이미지에 저장된 링크를 만듭니다.# models.py
import json
from os.path import splitext
from django.core.serializers.json import DjangoJSONEncoder
from wagtail.images import get_image_model
class FormPage(AbstractForm):
form_builder = CustomFormBuilder
# fields & panels definitions...
@staticmethod
def get_image_title(filename):
"""
Generates a usable title from the filename of an image upload.
Note: The filename will be provided as a 'path/to/file.jpg'
"""
if filename:
result = splitext(filename)[0]
result = result.replace('-', ' ').replace('_', ' ')
return result.title()
return ''
def process_form_submission(self, form):
"""
Processes the form submission, if an Image upload is found, pull out the
files data, create an actual Wgtail Image and reference its ID only in the
stored form response.
"""
cleaned_data = form.cleaned_data
for name, field in form.fields.items():
if isinstance(field, WagtailImageField):
image_file_data = cleaned_data[name]
if image_file_data:
ImageModel = get_image_model()
kwargs = {
'file': cleaned_data[name],
'title': self.get_image_title(cleaned_data[name].name),
'collection': self.get_uploaded_image_collection(),
}
if form.user and not form.user.is_anonymous:
kwargs['uploaded_by_user'] = form.user
image = ImageModel(**kwargs)
image.save()
# saving the image id
# alternatively we can store a path to the image via image.get_rendition
cleaned_data.update({name: image.pk})
else:
# remove the value from the data
del cleaned_data[name]
submission = self.get_submission_class().objects.create(
form_data=json.dumps(form.cleaned_data, cls=DjangoJSONEncoder),
page=self,
)
# important: if extending AbstractEmailForm, email logic must be re-added here
# if self.to_address:
# self.send_mail(form)
return submission
이 옵션을 적용하면 업로드 이미지가 있는 폼 응답을 제출할 수 있습니다.여기에는 몇 가지 주의해야 할 점이 있다.
request.POST
은 Wagtail에서 사용하는 이미지 모델을 얻는 가장 좋은 실천 방법이다.get_image_model
파일 데이터(모든 파일에 대한)를 포함하고 있으며, Django 양식 모듈은 이를 실현합니다.JSON 해석기는 파일 데이터를 해석할 수 없으므로 이러한 경우 URL 또는 이미지 ID로 처리해야 합니다.cleaned_data
는 당신이 원하는 것처럼 보일 수 있습니다. 저는 대소문자를 빼고 파일 제목과 대소문자를 만들었습니다.이렇게 할 필요는 없지만, 삽입 get_image_title
시 제목이 있는지 확인해야 합니다.WagtailImage
, 양식을 제출하고 이메일을 보내는 경우send_mail code를 추가해야 합니다.AbstractEmailForm
를 사용해야 하므로 파일 데이터가 작동하지 않습니다.7. 양식 제출 목록을 통해 이미지 보기
마지막 단계는 제출 목록 보기에서 이 그림을 쉽게 볼 수 있는 방법을 제공하는 것입니다. customising how this list generates 를 통해 완성할 수 있습니다.
우리는 이미 이미지의 id를 저장했지만,
cleaned_data.update
를 사용하고 싶습니다. 이것은 Wagtail Documentation 에서 상세하게 소개한 매우 유용한 함수입니다.이 함수는 템플릿 보조 프로그램을 시뮬레이션하지만 Python에서 사용할 수 있습니다.기본적으로 URL은 상대적입니다. (http/https 또는 필드를 포함하지 않습니다.) 이것은 전자메일로 보내는 링크가 작용하지 않는다는 것을 의미합니다.만약 이것이 문제라면, 네가 그것을 어떻게 가장 잘 해결할 것인지를 결정해라.# models.py
from django.utils.html import format_html
from django.urls import reverse
from wagtail.contrib.forms.views import SubmissionsListView
class CustomSubmissionsListView(SubmissionsListView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if not self.is_export:
# generate a list of field types, the first being the injected 'submission date'
field_types = ['submission_date'] + [field.field_type for field in self.form_page.get_form_fields()]
data_rows = context['data_rows']
ImageModel = get_image_model()
for data_row in data_rows:
fields = data_row['fields']
for idx, (value, field_type) in enumerate(zip(fields, field_types)):
if field_type == 'image' and value:
image = ImageModel.objects.get(pk=value)
rendition = image.get_rendition('fill-100x75|jpegquality-40')
preview_url = rendition.url
url = reverse('wagtailimages:edit', args=(image.id,))
# build up a link to the image, using the image title & id
fields[idx] = format_html(
"<a href='{}'><img alt='Uploaded image - {}' src='{}' />{} ({})</a>",
url,
image.title,
preview_url,
image.title,
value
)
return context
class FormPage(AbstractForm):
form_builder = CustomFormBuilder
submissions_list_view_class = CustomSubmissionsListView # added
위의 코드에 새 image.get_rendition
를 추가했습니다. 이것은 Wagtail CustomSubmissionsListView
을 확장했습니다. 이 Wagtail은 사용자 정의 SubmissionsListView
방법을 가지고 있습니다.이 방법에서, 우리는 원시get_context_data를 호출하여 생성된 상하문 데이터를 얻는다.그런 다음 사용자에게 제출 (내보내기 대신) 을 표시하고 각 제출 행을 매핑하며 값이 이미지인지 확인하고 일부 HTML을 사용하여 표시된 값을 업데이트합니다.이 HTML에는 이미지 미리 보기 (변환 사용) 가 포함되며, 제목과 id를 기반으로 한 설명이 포함되며, 이 설명은 이 이미지의 관리 페이지를 가리키는 링크에 있습니다.
완료 중
양식 모델입니다.py 파일은 현재 아래와 같이 보입니다.
전체 코드 세그먼트
# models.py
import json
from os.path import splitext
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
from django.utils.html import format_html
from django.urls import reverse
from modelcluster.fields import ParentalKey
from wagtail.admin.edit_handlers import (
FieldPanel,
FieldRowPanel,
InlinePanel,
MultiFieldPanel,
PageChooserPanel,
StreamFieldPanel,
)
from wagtail.core.models import Collection
from wagtail.contrib.forms.forms import FormBuilder
from wagtail.contrib.forms.models import AbstractForm, AbstractFormField, FORM_FIELD_CHOICES
from wagtail.contrib.forms.views import SubmissionsListView
from wagtail.images import get_image_model
from wagtail.images.fields import WagtailImageField
class CustomFormBuilder(FormBuilder):
def create_image_field(self, field, options):
return WagtailImageField(**options)
class CustomSubmissionsListView(SubmissionsListView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if not self.is_export:
# generate a list of field types, the first being the injected 'submission date'
field_types = ['submission_date'] + [field.field_type for field in self.form_page.get_form_fields()]
data_rows = context['data_rows']
ImageModel = get_image_model()
for data_row in data_rows:
fields = data_row['fields']
for idx, (value, field_type) in enumerate(zip(fields, field_types)):
if field_type == 'image' and value:
image = ImageModel.objects.get(pk=value)
rendition = image.get_rendition('fill-100x75|jpegquality-40')
preview_url = rendition.url
url = reverse('wagtailimages:edit', args=(image.id,))
# build up a link to the image, using the image title & id
fields[idx] = format_html(
"<a href='{}'><img alt='Uploaded image - {}' src='{}' />{} ({})</a>",
url,
image.title,
preview_url,
image.title,
value
)
return context
class FormField(AbstractFormField):
field_type = models.CharField(
verbose_name='field type',
max_length=16,
choices=list(FORM_FIELD_CHOICES) + [('image', 'Upload Image')]
)
page = ParentalKey('FormPage', related_name='form_fields', on_delete=models.CASCADE)
class FormPage(AbstractForm):
form_builder = CustomFormBuilder
submissions_list_view_class = CustomSubmissionsListView
# ... fields
uploaded_image_collection = models.ForeignKey(
'wagtailcore.Collection',
null=True,
blank=True,
on_delete=models.SET_NULL,
)
content_panels = AbstractForm.content_panels + [
# ... panels
]
settings_panels = AbstractForm.settings_panels + [
FieldPanel('uploaded_image_collection')
]
def get_uploaded_image_collection(self):
"""
Returns a Wagtail Collection, using this form's saved value if present,
otherwise returns the 'Root' Collection.
"""
collection = self.uploaded_image_collection
return collection or Collection.get_first_root_node()
@staticmethod
def get_image_title(filename):
"""
Generates a usable title from the filename of an image upload.
Note: The filename will be provided as a 'path/to/file.jpg'
"""
if filename:
result = splitext(filename)[0]
result = result.replace('-', ' ').replace('_', ' ')
return result.title()
return ''
def process_form_submission(self, form):
"""
Processes the form submission, if an Image upload is found, pull out the
files data, create an actual Wgtail Image and reference its ID only in the
stored form response.
"""
cleaned_data = form.cleaned_data
for name, field in form.fields.items():
if isinstance(field, WagtailImageField):
image_file_data = cleaned_data[name]
if image_file_data:
ImageModel = get_image_model()
kwargs = {
'file': cleaned_data[name],
'title': self.get_image_title(cleaned_data[name].name),
'collection': self.get_uploaded_image_collection(),
}
if form.user and not form.user.is_anonymous:
kwargs['uploaded_by_user'] = form.user
image = ImageModel(**kwargs)
image.save()
# saving the image id
# alternatively we can store a path to the image via image.get_rendition
cleaned_data.update({name: image.pk})
else:
# remove the value from the data
del cleaned_data[name]
submission = self.get_submission_class().objects.create(
form_data=json.dumps(form.cleaned_data, cls=DjangoJSONEncoder),
page=self,
)
# important: if extending AbstractEmailForm, email logic must be re-added here
# if self.to_address:
# self.send_mail(form)
return submission
이제 양식에 CMS 편집기로 정의된 이미지 업로드 필드가 하나 이상 있을 수 있습니다.이 이미지는 관리 중인 이미지 섹션에서 제공되며 Wagtail의 나머지 섹션에서 사용할 수 있습니다.색인 검색, 템플릿에서의 사용, 압축된 크기의 다양한 이미지의 URL 등 Wagtail 이미지의 모든 장점을 얻을 수 있습니다.양식 응답의 관리 보기에
get_context_data
에서 저장한 내용이 표시됩니다.만약 본문에서 문제가 있거나 맞춤법 오류가 발견되면 저에게 알려주십시오.Torchbox의 우수한 팀과 Wagtail의 모든 개발자들에게 감사드리며, 이 놀라운 도구를 만들어 주셔서 감사합니다.주연Wagtail repo on Github을 통해 Wagtail에 대한 지지를 표시합니다.
Github의 모든 코드 변경 사항을 image-uploads branch 에서 확인할 수 있습니다.
내 친구 아담이 그걸 증명해줘서 고마워.
Reference
이 문제에 관하여(흔들림 꼬리로 이미지 업로드), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/lb/image-uploads-in-wagtail-forms-39pl텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)