0

Edit: Mostly got this working with a clean update

I added docfile2 and docfile3 beyond the example I used to code this. I also changed the view to handle them:

 @login_required(login_url='/ngasite/login/')
def list(request):
    # Handle file upload
    if request.method == 'POST':
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            for fieldname in ('docfile', 'docfile2', 'docfile3'):
                file = form.cleaned_data[fieldname]
                if file:
                    Document(docfile=file).save()


            # Redirect to the document list after POST
            return HttpResponseRedirect(reverse('ngasite:list', args=""))
    else:
        form = DocumentForm() # A empty, unbound form

    # Load documents for the list page
    documents = Document.objects.all()
    paginator = Paginator(documents, 25)
    page = request.GET.get('page')
    try:
        docs = paginator.page(page)
    except PageNotAnInteger:
        docs = paginator.page(1)
    except EmptyPage:
        docs = paginator.page(paginator.num_pages)


    # Render list page with the documents and the form
    return render_to_response(
        'ngasite/list.html',
        {'documents': documents, 'form': form, 'docs':docs},
        context_instance=RequestContext(request)
    )
This worked pretty well, I can

handle three files on my form now instead of one. But relisting after a post gives me an attribute has no file associated with it error. I am guessing because my checks for saving in the view is not really working correctly and saving regardless if there was a file or not. Maybe my python syntax is bad? no errors int he console though.

This is what is failing in my list.html template:

  {% if docs %}
                                    <ul>
                                    {% for doc in docs %}
                                        <li><a href="{{ doc.docfile.url }}">{{ doc.docfile.name }} </a></li>
                                    {% endfor %}

                                    </ul>
                                {% else %}
                                    <p>No documents.</p>
                                {% endif %}

So my real forms.py with the overriding of the is_valid

    from django import forms

 class DocumentForm(forms.Form):
    docfile = forms.FileField(
        label='Select a file',
        help_text='max. 7 Gigabytes',
        required=False
    )
    docfile2 = forms.FileField(
        label='Select a file',
        help_text='max. 7 Gigabytes',
        required=False
    )
    docfile3 = forms.FileField(
        label='Select a file',
        help_text='max. 7 Gigabytes',
        required=False
    )
    def clean(self):
        data = super(DocumentForm, self).clean()

        if not (data['docfile'] or data['docfile2'] or data['docfile3']):
            return ValidationError('You must upload at least one file.')

        return data

What am i missing? my view fails on lines like

docfile=request.FILES['docfile'] cause it knows its missing, I thought my if newdoc2: etc would handle that but it doesnt work like I think it does/should

Ultimately as I learn more python and Django i want to turn this into a nice file uploader with responses to the browser to show the upload progress etc.

Codejoy
  • 3,722
  • 13
  • 59
  • 99
  • As I said in the [very similar question that was posted just before yours](http://stackoverflow.com/questions/37171746/django-file-field-update-causing-error-even-though-not-required/37172593#37172593) you should be using `form.cleaned_data['docfile2']` etc which avoids the problem completely. – Daniel Roseman May 11 '16 at 21:11
  • didn't see that question as I was typing this one. I see the cleaned data and even tried to get that to work. Keeps saying the attribute doesn't exist, but I did notice your other post where you said cleaned_data doesnt get created until is_valid is ran. So i do run that on the parent class(and then just return true anyway)...still says no attribute. I am not doing a double form instantiation either like the other person you helped. – Codejoy May 11 '16 at 21:30
  • the other one on the cleaned_data no attribute is here: http://stackoverflow.com/questions/4308527/django-model-form-object-has-no-attribute-cleaned-data – Codejoy May 11 '16 at 21:46

1 Answers1

1

You should set your fields to required=False, which will avoid a validation error if any field is empty:

docfile3 = forms.FileField(
    label='Select a file',
    help_text='max. 7 Gigabytes',
    required=False
)

Then in your form clean method check to see if at least one file is there:

from django.core.exceptions import ValidationError

def clean(self):
    data = super(DocumentForm, self).clean()

    # if there are already form errors, don't proceed with additional
    # validation because it may depend on fields which didn't validate.
    if self.errors:
        return data

    if not (data['docfile1'] or data['docfile2'] or data['docfile3']):
        return ValidationError('You must upload at least one file.')

    return data

I have not actually run this code so there may be some little issue but you get the idea...

In your view form_valid() method, there are problems with your saving code. Do something more like this:

for fieldname in ('docfile', 'docfile2', 'docfile3'):
    file = form.cleaned_data.get(fieldname, None)
    if file:
        Document(docfile = file).save()
little_birdie
  • 5,600
  • 3
  • 23
  • 28
  • that worked, or at least got me farther. It seems my code `docfile = form.cleaned_data['docfile'] newdoc = Document(docfile = docfile) if newdoc: newdoc.save()` fails as I get an error after the files are uploaded and when the view tries to list them that the docfile attribute has no file associated with it (in my template.html) < a href="{{ doc.docfile.url}}"> I am guessing because its saving what it shouldn't be. So close – Codejoy May 11 '16 at 21:55
  • The problem with your saving code is that you are constructing a new Document object without first checking to see if there is a value for the file. I updated my answer – little_birdie May 11 '16 at 22:01
  • updated, and it works! i did have to change my: file = form.cleaned_data[fieldname] from the request.FILES as it still needed the cleaned data. but now it works with one or more files. Though if i select no files, it does fail on the same line with a `sequence index must be integer, not 'str'` guessing I need a cast, or something with that for fieldname to know its blank. – Codejoy May 11 '16 at 22:18
  • Cool that's great I'll fix my answer for future searchers. As far as the other problem I don't have enough info to fix it... – little_birdie May 11 '16 at 23:40
  • yah its a tough one, i am not python-pro enough to figure it out yet. something about that not liking `file = form.cleaned_data[fieldname]` when nothing is uploaded. – Codejoy May 12 '16 at 02:55
  • Try `file = form.cleaned_data.get(fieldname, None)`.. the `None` is the default if `fieldname` is not found in cleaned_data, which it will return instead of throwing an exception. – little_birdie May 12 '16 at 03:15
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/111773/discussion-between-codejoy-and-little-birdie). – Codejoy May 12 '16 at 14:11