Search code examples
pythonpython-3.xdjangodjango-formsmodelform

Replicating Django forms request.FILES to upload multiple image files using smartfields


I'm trying to take a single image using Django forms and upload it with resized version under 3 headers. I'm even able to do so with request.POST QueryDict but not with request.FILES MultiValueDict even after it shows filled data for respective field names.

My Views.py

def image_add(request,article_id):
    template_name = 'blogs/image_add.html'
    articles = Article.objects.get(article_id=article_id)
    form = ImageAddForm
    if request.method == 'POST':
        image = request.FILES["image_1080"]
        request.FILES['image_800'] = image
        request.FILES['image_350'] = image
        print(request.POST)
        print(request.FILES)
        form = ImageAddForm(request.POST, request.FILES)
        if form.is_valid():
            new_form = form.save(commit=False)
            new_form.dir_id = article_id
            new_form.save()
            return redirect('/')
    context = {'form':form,'articles':articles}
    return render(request, template_name,context)

My Models.py

from smartfields import fields
from smartfields.dependencies import FileDependency
from smartfields.processors import ImageProcessor

    class Images(models.Model):
        dir_id = models.CharField(max_length=10,null=True)
        image_1080 = fields.ImageField(upload_to=img_1080_dir_path, name="image_1080", dependencies=[
            FileDependency(processor=ImageProcessor(
                format='PNG', scale={'max_width': 1080, 'max_height': 1080}))
        ])
        image_800 = fields.ImageField(upload_to=img_800_dir_path, blank=True, name="image_800", dependencies=[
            FileDependency(processor=ImageProcessor(
                format='PNG', scale={'max_width': 800, 'max_height': 800}))
        ])
        image_350 = fields.ImageField(upload_to=img_350_dir_path, blank=True, name="image_350", dependencies=[
            FileDependency(processor=ImageProcessor(
                format='PNG', scale={'max_width': 350, 'max_height': 350}))
        ])

My Forms.py

class ImageAddForm(forms.ModelForm):
    class Meta:
        model = Images
        fields = ('name','alt_text','image_1080')
        widgets = {
            'name': forms.TextInput(attrs={'class': 'form-control'}),
            'alt_text': forms.TextInput(attrs={'class': 'form-control'}),
            'image_1080': forms.ClearableFileInput(attrs={'class': 'form-file-input'})
            }

This saves only one image - 'image_1080' but not the other two.


Solution

  • Your approach of overwriting POST parameters is more of a hack, because conceptually it means you are ignoring the data that suppose to come in from the user. So this is something I would recommend against in general:

        if request.method == 'POST':
            image = request.FILES["image_1080"]
            request.FILES['image_800'] = image
            request.FILES['image_350'] = image
    

    I suspect this approach doesn't work because the same instance of the file is shared among many fields. Correct approach instead would be to simply use one form field as input and let smartfields attach other fields to the model for you. Also, I recommend keeping the original file uploaded, just in case if you ever need to resize and change the format again in the future:

    from smartfields import utils
    
    class Images(models.Model):
        image = fields.ImageField(
            upload_to=utils.UploadTo(generator=True, field_name='image'),
            dependencies=[
                FileDependency(suffix='1080', processor=ImageProcessor(
                    format='PNG', scale={'max_width': 1080, 'max_height': 1080}),
                FileDependency(suffix='800', processor=ImageProcessor(
                    format='PNG', scale={'max_width': 800, 'max_height': 800}),
                FileDependency(suffix='350', processor=ImageProcessor(
                    format='PNG', scale={'max_width': 350, 'max_height': 350}))
            ])
    
    

    When uploading an image to image field, smartfields will automatically add extra fields to you Images model, eg:

    > i = Images(my_image)
    > i.save()
    > print(i.image)      # original
    > print(i.image_1080) # resized to 1080
    > print(i.image_800)