2

I'm trying to restrict file type, size and extension that can be uploaded in a form. The functionality seems to work, but the validation error messages are not showing. I realize that if file._size > 4*1024*1024 is probably not the best way - but I'll deal with that later.

Here's the forms.py:

class ProductForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = ['name', 'description', 'url', 'product_type', 'price', 'image', 'image_url', 'product_file']
        labels = {
            'name': 'Product Name',
            'url': 'Product URL',
            'product_type': 'Product Type',
            'description': 'Product Description',
            'image': 'Product Image',
            'image_url': 'Product Image URL',
            'price': 'Product Price',
            'product_file': 'Product Zip File',
        }
        widgets = {
            'description': Textarea(attrs={'rows': 5}),
        }

    def clean(self):
        file = self.cleaned_data.get('product_file')

        if file:
            if file._size > 4*1024*1024:
                raise ValidationError("Zip file is too large ( > 4mb )")
            if not file.content-type in ["zip"]:
                raise ValidationError("Content-Type is not Zip")
            if not os.path.splitext(file.name)[1] in [".zip"]:
                raise ValidationError("Doesn't have proper extension")

                return file
            else:
                raise ValidationError("Couldn't read uploaded file")

...and here's the view I'm using for that form:

def post_product(request):
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = ProductForm(data = request.POST, files = request.FILES)
        # check whether it's valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required
            product = form.save(commit = False)
            product.user = request.user
            product.likes = 0
            product.save()
        # redirect to a new URL:
        return HttpResponseRedirect('/products')

What am I missing?

Steve
  • 1,182
  • 1
  • 11
  • 25

1 Answers1

2

In your view, you are doing a redirect regardless of whether or not the form is valid - so there is nowhere for Django to show form errors.

The normal way to do this would be to re-render the form when is_valid() is False:

if form.is_valid():
    # process the data in form.cleaned_data as required
    product.save()
    # redirect to a new URL - only if form is valid!
    return HttpResponseRedirect('/products')
else:
    ctx = {"form": form} 
    # You may need other context here - use your get view as a template
    # The template should be the same one that you use to render the form
    # in the first place.
    return render(request, "form_template.html", ctx}

You may want to consider using a class-based FormView for this, as it handles the logic of re-rendering forms with errors. This is simpler and easier than writing two separate get and post views to manage your form. Even if you don't do that, it will be easier to have a single view that handles both GET and POST for the form.

solarissmoke
  • 30,039
  • 14
  • 71
  • 73
  • Thanks - so are you saying instead of used `def clean` on the form, use context on the view instead? – Steve Dec 16 '17 at 03:28
  • No. Your form `clean()` is fine, but your view isn't. You need to handle the case where `form.is_valid()` returns false differently - i.e., you need to re-render the form when that happens, so that you can display the validation errors. – solarissmoke Dec 16 '17 at 03:31
  • Got it - thanks for the clues - I'll go figure this out! :) – Steve Dec 16 '17 at 03:55
  • Ok - I have the error part working now - thanks for that. I'm getting errors on `'InMemoryUploadedFile' object has no attribute 'content'` for `file.content-type'. The other two work just fine. – Steve Dec 16 '17 at 23:08
  • It should be `content_type`, not `content-type` as you currently have. You can't have dashes in python variables. – solarissmoke Dec 17 '17 at 01:59
  • Yeah - I thought so - although when I upload the correct file (a zip file less than 4mb's) it defaults to the else statement `else: raise ValidationError("Couldn't read uploaded file")` - complicated! – Steve Dec 17 '17 at 04:54
  • Oh boy - just noticed that the else statement prints an error even if all the if statements pass - d'oh! – Steve Dec 17 '17 at 15:34