如何在Django的博客文章中上传多张图片

48

我想让每个用户上传多张图片到同一篇博客文章中。我已经试图找出最好的方法来做这件事情几天了。什么是最佳实践呢?

从我所了解的,我应该从博客文章模型中创建一个单独的图像模型并使用外键。对吗? 然后有一个问题就是如何让他们同时上传多张照片。我应该使用像drop zone这样的东西吗?

任何关于存储照片的最佳实践建议也欢迎提供。我已经看过Amazon s3和cloudinary。我希望创建一个可扩展的东西。

非常感谢您的任何帮助!


1
一种添加多个图像上传的方法是使用http://koensblog.eu/blog/7/multiple-file-upload-django/,另一种方法是使用django-ckeditor并使用其中一个图像上传插件,其中图像上传到您的Amazon S3存储,并且只有img标记插入到您的内容中,并带有指向您的S3图像位置的正确地址。 - Allen Fernandes
也许这个答案可以帮助。 - Muhammmed Nihad
5个回答

88

你只需要两个模型。一个是关于帖子的,另一个是关于图片的。你的图片模型将有一个外键指向帖子模型:

from django.db import models
from django.contrib.auth.models import User
from django.template.defaultfilters import slugify

class Post(models.Model):
    user = models.ForeignKey(User)
    title = models.CharField(max_length=128)
    body = models.CharField(max_length=400)
  
def get_image_filename(instance, filename):
    title = instance.post.title
    slug = slugify(title)
    return "post_images/%s-%s" % (slug, filename)  


class Images(models.Model):
    post = models.ForeignKey(Post, default=None)
    image = models.ImageField(upload_to=get_image_filename,
                              verbose_name='Image')

你需要为每个模型创建一个表格,但它们将相互关联,例如当用户填写“帖子”表格时,他必须完成与该帖子相关的“图片”表格,以使帖子成功发布。我们将在视图中处理这些关联,但现在你的表格可以像这样:

from django import forms
from .models import Post, Images

class PostForm(forms.ModelForm):
    title = forms.CharField(max_length=128)
    body = forms.CharField(max_length=245, label="Item Description.")
 
    class Meta:
        model = Post
        fields = ('title', 'body', )
 
 
class ImageForm(forms.ModelForm):
    image = forms.ImageField(label='Image')    
    class Meta:
        model = Images
        fields = ('image', )

现在这是所有内容中最重要的部分,视图(views),因为这是上传多个图像到单个魔术位置发生的地方。为了能够一次性上传多个图像,我们需要多个图像字段,对吧?这就是你会爱上Django formsets的地方。我们需要Django表单集来实现这一点,您可以在Django文档中阅读有关表单集的内容,我已经提供了链接 :) 但这是您的视图应该看起来的样子:

*非常重要的导入

from django.shortcuts import render
from django.forms import modelformset_factory
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.http import HttpResponseRedirect
from .forms import ImageForm, PostForm
from .models import Images

@login_required
def post(request):
 
    ImageFormSet = modelformset_factory(Images,
                                        form=ImageForm, extra=3)
    #'extra' means the number of photos that you can upload   ^
    if request.method == 'POST':
    
        postForm = PostForm(request.POST)
        formset = ImageFormSet(request.POST, request.FILES,
                               queryset=Images.objects.none())
    
    
        if postForm.is_valid() and formset.is_valid():
            post_form = postForm.save(commit=False)
            post_form.user = request.user
            post_form.save()
    
            for form in formset.cleaned_data:
                #this helps to not crash if the user   
                #do not upload all the photos
                if form:
                    image = form['image']
                    photo = Images(post=post_form, image=image)
                    photo.save()
            # use django messages framework
            messages.success(request,
                             "Yeeew, check it out on the home page!")
            return HttpResponseRedirect("/")
        else:
            print(postForm.errors, formset.errors)
    else:
        postForm = PostForm()
        formset = ImageFormSet(queryset=Images.objects.none())
    return render(request, 'index.html',
                  {'postForm': postForm, 'formset': formset})

视图中,我们将获取两个表单,并检查这两个表单是否有效。这样一来,用户必须同时填写表单和上传所有图片,而在本例中是3张附加的图片extra=3。只有这样,帖子才能成功创建。

那么你的模板应该看起来像这样:

<form id="post_form" method="post" action="" enctype="multipart/form-data">
 
    {% csrf_token %}
    {% for hidden in postForm.hidden_fields %}
        {{ hidden }}
    {% endfor %}
 
    {% for field in postForm %}
        {{ field }} <br />
    {% endfor %}
 
    {{ formset.management_form }}
    {% for form in formset %}
        {{ form }}
    {% endfor %}
 
 
    <input type="submit" name="submit" value="Submit" />
</form>

4
非常感谢您的评论!您想知道如何将拖放系统(如dropzone js)集成到其中?如果要使用拖放方法,是否需要表单集(formsets)呢? - ollysmall
1
@ollysmall 抱歉,我无法帮助你解决这个问题,因为我对Javascript一窍不通,所以无法告诉你该如何做。但是请看这个链接 https://github.com/sigurdga/django-jquery-file-upload 我曾尝试过做与你要求相同的事情,并找到了django jquery文件上传,但我不知道如何使用它并使其与表单集配合工作。也许你可以试试? - qasimalbaqali
1
管理员应该看起来像什么? - Chris
1
@bgarcial 抱歉,我已经有一段时间没有接触 Django 了,无法帮助你解决问题。但是也许这个链接可以帮到你:https://dev59.com/DFkT5IYBdhLWcg3wV-KZ#38739423 - qasimalbaqali
我能把它做成一个单独的模型吗? - Lars
显示剩余4条评论

8

逐步解决方案 => 即使我也遇到了难题,这是我成功的方法。

最后实现以下代码,我达到了这个目标:

  • 1个具有多个图像的Note模型
  • 多重上传(同时使用相同的选择文件按钮,并且全部一起保存,就像在Gmail文件上传中一样)

这是我的笔记和图像模型-(或查看完整代码

class Note(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
    title = models.CharField(max_length=30)
    text = models.TextField(null=True,blank=True)
    created_date = models.DateTimeField(auto_now_add=True)
    last_modified = models.DateTimeField(auto_now=True)

class Images(models.Model):
    note = models.ForeignKey(Note,on_delete=models.CASCADE)
    image = models.ImageField(upload_to=user_directory_path,null=True,blank=True)

这是我的表单(有关多文件上传的文档链接) - (或者查看完整代码)

class NoteForm(forms.ModelForm):
    class Meta:
        model = Note
        fields = ['title','text'] #make sure to mention field here, if nothing is mentioned then all will be required.

class NoteFullForm(NoteForm): #extending form
    images = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))

    class Meta(NoteForm.Meta):
        fields = NoteForm.Meta.fields + ['images',]

这是我的View文件-(或查看完整代码

def addNoteView(request):
if request.method == "POST":
    #images will be in request.FILES
    form = NoteFullForm(request.POST or None, request.FILES or None)
    files = request.FILES.getlist('images')
    if form.is_valid():
        user = request.user
        title = form.cleaned_data['title']
        text = form.cleaned_data['text']
        note_obj = Note.objects.create(user=user,title=title,text=text) #create will create as well as save too in db.
        for f in files:
            Images.objects.create(note=note_obj,image=f)
    else:
        print("Form invalid")

最后,我的Html文件(请确保按照文档中所述绑定文件)-(或查看完整代码

<form action="{% url 'note:add_note' %}" method="post" class="note-form" enctype="multipart/form-data">{% csrf_token %}
  <div class="form-group">
    <label for="note-title">Title</label>
    <input type="text" name="title" class="form-control" id="note-title" placeholder="Enter Title" required>
  </div>
  <div class="form-group">
    <label for="note-description">Description</label>
    <textarea type="text" name="text" class="form-control" id="note-description" placeholder="Description here"></textarea>
  </div>
  <div class="form-group">
    <label for="note-image">Images</label>
    <input type="file" name="images" class="form-control-file" id="note-image" multiple>
  </div>
  <button type="submit" class="btn btn-primary">Submit</button>
</form>

5
答案非常简单。不需要太多的代码。
HTML文件。
<input type="file" name = "files" multiple />

VIEWS.PY

files = request.FILES.getlist('files')

for f in files:
    a = Image(image=f)
    a.save()

这个方法适用于HTML文件上传,但OP想要将多张图片添加到一个对象中,即“在单个博客文章中添加多张图片”。 - Elcast
也许这个答案可以帮助。 - Muhammmed Nihad

1
我有一个类似的问题。在我的模型中,一篇文章必须有一个缩略图(图片),然后我设置了另外五个可选图片字段。问题出现在应用安全过滤器后,图像源无法呈现,因为它不再是HTML。

<img src="{{article.image1.url}}" class="some class">

无法正常工作。我使用的临时解决方案是根据文章标题命名图像。如果我正在写有关“django过滤器”的文章,则会将我的其他图像命名为djangofiltersimage1.png或djangofiltersimage2.png,这很有帮助,因为在我的模型中每篇文章都有唯一的标题。然后我更改了图像源:

<img src="/media/djangofiltersimage1.png" class="some class">

唯一的问题是图片的命名严格。您可以通过为博客图片创建模型来上传更多图片。同时,我仍在寻找另一个解决方案。 您可以在这里查看我的博客。

1

getlist名称和HTML输入名称字段应该相同。

对于models.py文件

class studentImage(models.Model):
        image = models.ImageField(upload_to='media/')
    
        def __str__(self):
            return self.image

针对 views.py

def studentImageView(request):
    if request.method == "POST":
        images = request.FILES.getlist('images')
        for image in images:
            photo = studentImage.objects.create(image=image,)
            photo.save()

    return render(request, 'image.html')

用于模板

<form method="post" enctype="multipart/form-data" >
  {% csrf_token %}
   <input required name="images" type="file" multiple >
  <button class="btn btn-primary" type="submit">Upload</button>
</form>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接