首页 > 解决方案 > 如何使用 CreateView 在 Django 中上传多个文件?

问题描述

请帮我。我是 Django 新手,无法理解以下内容 - 我有 CreateView 的子类来创建评论。我想创建一个项目,人们可以在其中留下评论并将文件(图像)附加到此评论。一个人应该可以将任意数量的图像附加到一个带有文本注释的表单中。我在 Internet 上发现我需要使用 2 个模型 - 1 个用于文本评论的模型 + 1 个用于图像的单独模型。是这样吗?

评论(文本)表单是由 CreateView 的子类在我的 views.py 中创建和处理的。如何将新的独立图像模型与我的 CreateView 连接起来?

models.py

class Descriptions(models.Model):
…
city = models.ForeignKey(Cities, on_delete=models.CASCADE)
description = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.DO_NOTHING)
…
class Description_Photos(models.Model):
    image = models.ImageField(upload_to='images/', blank=True)
    description = models.ForeignKey(Descriptions, on_delete=models.CASCADE, related_name='photos')

forms.py

class DescriptionsForm(forms.ModelForm):
    class Meta:
        model = Descriptions
        exclude = []
        widgets = {'description': forms.Textarea(attrs={'cols':90})}

class Photos_form(forms.Form):
    photos = forms.FileField(widget=forms.FileInput(attrs={'multiple': True}))

views.py

class DescriptionCreate(CreateView):
    model = Descriptions
    form_class = DescriptionsForm
    template_name = 'countries/new_description.html'

    def get_success_url(self):
        return reverse('countries:descr', args=[self.kwargs['country_id'], self.kwargs['city_id']])

    def get_context_data(self, **kwargs):
        self.city = get_object_or_404(Cities, id=self.kwargs['city_id'])
        kwargs['city'] = self.city
        return super().get_context_data(**kwargs)

    def form_valid(self, form):
        form.instance.city = get_object_or_404(Cities, id=self.kwargs['city_id'])
        form.instance.owner = self.request.user
        messages.success(self.request, 'Your post has been added, thank you')
        return super().form_valid(form)

所以我的问题是我应该在 views.py 中写什么class Photos_form(forms.Form):?如何连接这个类和我的class DescriptionCreate(CreateView)

标签: pythondjango

解决方案


我在相同的情况下使用 FormSets https://docs.djangoproject.com/en/2.0/topics/forms/formsets/

为图像模型声明 FormSet

…

# forms.py

class DescriptionsForm(forms.ModelForm):
    class Meta:
        model = Descriptions
        exclude = []
        widgets = {'description': forms.Textarea(attrs={'cols':90})}

class Photos_form(forms.Form):
    photos = forms.FileField(widget=forms.FileInput(attrs={'multiple': True}))


##### Declare FORMSET !!! ###
class BasePhotosFormSet(BaseModelFormSet):

    """By default, when you create a formset from a model, the formset
    will use a queryset that includes all objects in the model"""

    def __init__(self, *args, **kwargs):
        if 'city' in kwargs.keys():
            city = kwargs.pop('city')
        else:
            city = None
        super().__init__(*args, **kwargs)
        if city and isinstance(instance, Cities):
            self.queryset = Description_Photos.objects.filter(city=city)
        else:
            self.queryset = Description_Photos.objects.none()

# I usually declare formset for create operations and formset for update operations separately          
PhotosCreateFormSet = forms.modelformset_factory(Description_Photos, Photos_form,
                                                    fields=Photos_form.Meta.fields, extra=0,
                                                    formset=BasePhotosFormSet)


PhotosUpdateFormSet = forms.modelformset_factory(Description_Photos, Photos_form, can_delete=True,
                                              fields=PropertyImageForm.Meta.fields, extra=0,
                                              formset=BasePhotosFormSet)


#############

# views.py

class DescriptionCreate(CreateView):

    def __init__(self, **kwargs):
        self.object = None
        super().__init__(**kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        if self.request.POST:
            images_formset = PhotosCreateFormSet(self.request.POST, self.request.FILES, city=self.object)
        else:
            images_formset = PhotosCreateFormSet(instance=self.object)
        context['formset'] = images_formset
        context['city'] = self.object
        return context

模板

    <div id="img-form-template" style="display: none">
       <!- Declare EMPTY FORM for dynamically rebuild user interface by jQuery, for example->
      {{ formset.empty_form }}
    </div>
    ...
    <div id="my-images">
        ...
        {{ formset.management_form }}
        {% for image_form in formset %}
            {{ image_form }}
        {% endfor %}
    </div>
    ...
    <script>
      ...
      <!- Any javascript code to dynamically create empty form based on template "#img-form-template" ->
      ...
    </script>

我试图将我的自定义代码重写为您的变体。我想self.city在您的示例中声明为创建模型实例是一个坏主意:Django 自动创建self.object


推荐阅读