3

I'm trying to upload some files through a form on my project and have the files save to disk. Currently, I am able to get the data to insert into the DB (postgreSQL), but the data is not being written to disk. I've done some looking around, and using a modelform and calling save should take care of everything (but apparently I missed something?). Any help is appreciated.

Here's my code: views.py (for uploading files related to a job)

# Upload files to a Job (Developer)
@login_required()
@user_passes_test(lambda u: u.groups.filter(name='Developer').exists(), login_url='/login/', redirect_field_name='not allowed')
@require_http_methods(['POST'])
def job_file_upload(request, jobid):

    # Get the Job
    job = Jobs.objects.get(id=jobid)
    fileform = JobFileSubmitForm(request.POST, request.FILES)
    if fileform.is_valid():
        jfs = fileform.save(commit=False)
        file = request.FILES['file']
        jfs.user_id = request.user.id
        jfs.job_id = jobid
        jfs.file = file.name
        jfs.uploadDate = datetime.now()
        # Save to DB
        jfs.save()

        return redirect('view_job', jobid=jobid, slug=job.slug)

models.py (DB for holding file data and holding saving to the correct path)

from django.core.files.storage import FileSystemStorage

fs = FileSystemStorage(location='/media/')

...


def upload_job_file_path(instance, filename):
    return os.path.join('/uploads/job_meta/files/', instance.id, filename)


# Submit Files for a Job (Developer Role)
class JobFileSubmit(models.Model):
    job = models.ForeignKey(Jobs)
    user = models.ForeignKey(User)
    file = models.FileField(storage=fs, upload_to=upload_job_file_path, blank=False, null=False)
    uploadDate = models.DateTimeField(auto_now=True)

forms.py

class JobFileSubmitForm(forms.ModelForm):

    class Meta:
        model = JobFileSubmit
        fields = 'file',

    def save(self, commit=True):
        jobfilesubmit = super(JobFileSubmitForm, self).save(commit=False)

        if commit:
            jobfilesubmit.save()
        return jobfilesubmit

view.html

                            <form method="post" action="/job/job_file_upload/j{{ job.id }}/" class="form-inline btn-group" enctype="multipart/form-data">
                                {% csrf_token %}
                                <div class="span6 inline">
                                    <label class="control-label">Attach Files: </label>{{ job_file_submit.file }}
                                    <p class="help-block">Attach files that go with this Job.</p>
                                </div>
                                <div class="modal-footer">
                                    <button type="submit" class="btn btn-success btn-med pull-left"><i class="fa fa-check-circle"></i> Attach Files</button>
                                    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                                </div>
                            </form>

TL;DR: Currently the form submits, inserts into the DB, but the data is not written to the disk and to the dynamic path it should. For instance, the data is not being written to:

/media/uploads/job_meta/files/{{job.id}}/{{file.name}}

Thanks for taking the time to read this! Any help is much appreciated!

jaredgilmore
  • 657
  • 8
  • 17
  • looks like your upload_job_file_path function returns an absolute filepath. – kanu Dec 09 '13 at 05:28
  • Ah. It needs to be relative? Such as: uploads/job_meta/files/. Assuming this takes into account MEDIA_ROOT as a prefix? – jaredgilmore Dec 09 '13 at 05:32
  • yes. the 2nd problem i see is that your path contains the job.id which is None when called with a new and unsaved job. – kanu Dec 09 '13 at 05:41
  • Kanu, can you post an example of what you mean? I'm still relatively new to Python and Django. :( Does this look correct? Still posts the information to the DB, but the file isn't uploaded: ` def upload_job_file_path(instance, filename): return os.path.join('uploads/job_meta/files/', instance, filename)` – jaredgilmore Dec 09 '13 at 05:47
  • I would expect this to work: `models.FileField(upload_to="uploads/job_meta/files/", blank=False, null=False)` – kanu Dec 09 '13 at 05:52
  • That would, however I want files uploaded to go to a dynamic path. For instance: /media/uploads/job_meta/files/{{job.id}}/test.txt – jaredgilmore Dec 09 '13 at 05:54

1 Answers1

2

I am not sure if this is the final answer but its easier to write codeblock here

The upload_job_file_path needs to return a relative path from the MEDIA_ROOT. If I understand it right, the id is the job_id of the instance not its id. This way the following function should work. (It might be that FileField has to be below the job field in the code)

models.py

def upload_job_file_path(instance, filename):
    return 'uploads/job_meta/files/%s/%s' % (instance.job_id, filename)


class JobFileSubmit(models.Model):
    job = models.ForeignKey(Jobs)
    user = models.ForeignKey(User)
    file = models.FileField(upload_to=upload_job_file_path, blank=False, null=False)
    uploadDate = models.DateTimeField(auto_now=True)

I am not too familiar with modeforms but i think the save method should handle the fileupload too. So this might be enough

views.py

if fileform.is_valid():
    jfs = fileform.save(commit=True)
kanu
  • 726
  • 5
  • 9