Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.5k views
in Technique[技术] by (71.8m points)

django - How can I add an Upload File field to a Wagtail Form?

I want to make File Upload a possible Wagtail Form field type on form pages. How do I configure the models to make this possible? Please note I am more interested in allowing users to upload document files like PDFs rather than images.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Here is a great article from LB Johnson on that matter. The code below is directly taken from the article (but copying here in case the article goes away), however you might want to follow the article as it explains everything step by step.

It's quite involved at the moment, you'll need to:

  1. extend AbstractFormField to define a new field type.
  2. extend FormBuilder to deal with the new field type.
  3. set form_builder to your custom FormBuilder class on your FormPage .
  4. override the serve method on the FormPage to pass the file data to the form (only if you use Wagtail 1.12 and below as it does it automatically from Wagtail 1.13)
  5. override the process_form_submission to process the file

Here is the full code:

from wagtail.wagtailforms.models import AbstractFormField, FORM_FIELD_CHOICES
from wagtail.wagtailforms.forms import FormBuilder
from wagtail.wagtailimages.fields import WagtailImageField


def filename_to_title(filename):
    from os.path import splitext
    if filename:
        result = splitext(filename)[0]
        result = result.replace('-', ' ').replace('_', ' ')
        return result.title()


class FormField(AbstractFormField):
    FORM_FIELD_CHOICES = list(FORM_FIELD_CHOICES) + [('image', 'Upload Image')]
    field_type = models.CharField(
        verbose_name=_('field type'),
        max_length=16,
        choices=FORM_FIELD_CHOICES)
    page = ParentalKey('FormPage', related_name='form_fields')


class ExtendedFormBuilder(FormBuilder):
    def create_image_upload_field(self, field, options):
        return WagtailImageField(**options)
    FIELD_TYPES = FormBuilder.FIELD_TYPES
    FIELD_TYPES.update({
        'image': create_image_upload_field,
    })


class FormPage(AbstractEmailForm):
    form_builder = ExtendedFormBuilder

    def serve(self, request, *args, **kwargs):
        if request.method == 'POST':
            # form = self.get_form(request.POST, page=self, user=request.user)  # Original line
            form = self.get_form(request.POST, request.FILES, page=self, user=request.user)

            if form.is_valid():
                self.process_form_submission(form)
                return render(
                    request,
                    self.get_landing_page_template(request),
                    self.get_context(request)
                )
        else:
            form = self.get_form(page=self, user=request.user)

        context = self.get_context(request)
        context['form'] = form
        return render(
            request,
            self.get_template(request),
            context
        )

    def process_form_submission(self, form):
        cleaned_data = form.cleaned_data

        for name, field in form.fields.iteritems():
            if isinstance(field, WagtailImageField):
                image_file_data = cleaned_data[name]
                if image_file_data:
                    ImageModel = get_image_model()
                    image = ImageModel(
                        file=cleaned_data[name],
                        title=filename_to_title(cleaned_data[name].name),
                        collection=self.upload_image_to_collection,
                        # assumes there is always a user - will fail otherwise
                        uploaded_by_user=form.user,
                        )
                    image.save()
                    cleaned_data.update({name: image.id})
                else:
                    # remove the value from the data
                    del cleaned_data[name]

        form_data = json.dumps(cleaned_data, cls=DjangoJSONEncoder)
        submission_object = dict(
            page=self,
            form_data=form_data,
            user=form.user,
        )

    intro = RichTextField(blank=True)
    thank_you_text = RichTextField(blank=True)

    FormPage.content_panels = [
        FieldPanel('title', classname="full title"),
        FieldPanel('intro', classname="full"),
        InlinePanel('form_fields', label="Form fields"),
        FieldPanel('thank_you_text', classname="full"),
        MultiFieldPanel([
            FieldRowPanel([
                FieldPanel('from_address', classname="col6"),
                FieldPanel('to_address', classname="col6"),
            ]),
            FieldPanel('subject'),
        ], "Email"),
]

Here is the (untested) code to handle a document instead of an image:

from django.forms import FileField
from wagtail.wagtaildocs.models import get_document_model
# Other imports

class FormField(AbstractFormField):
    FORM_FIELD_CHOICES = list(FORM_FIELD_CHOICES) + [('document', 'Upload Document')]
    # `field_type` and `page` remain unchanged


class ExtendedFormBuilder(FormBuilder):
    def create_document_upload_field(self, field, options):
        return FileField(**options)
    FIELD_TYPES = FormBuilder.FIELD_TYPES
    FIELD_TYPES.update({
        'document': create_document_upload_field,
    })

class FormPage(AbstractEmailForm):
    # `form_builder` attribute and `serve` remain unchanged.

    def process_form_submission(self, form):
        cleaned_data = form.cleaned_data

        for name, field in form.fields.iteritems():
            if isinstance(field, FileField):
                document_file_data = cleaned_data[name]
                if document_file_data:
                    DocumentModel = get_document_model()
                    document = DocumentModel(
                        file=cleaned_data[name],
                        title=filename_to_title(cleaned_data[name].name),
                        # assumes there is always a user - will fail otherwise
                        uploaded_by_user=form.user,
                    )
                    document.save()
                    cleaned_data.update({name: document.id})
                else:
                    # remove the value from the data
                    del cleaned_data[name]

        # The rest of the function is unchanged

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...