Kiran
Kiran

Reputation: 55

How do I store the uploaded file inside a folder having the name from the model field in django?

Using the upload_to argument of FileField I want to store the file uploaded by the user in a folder named TestNameie the test name entered by the user. I've written the following code, but it creates a folder named "models.CharField(max_length=255)" instead. How can I fix this?

from django.core.validators import FileExtensionValidator
from django.db import models
from django.contrib.auth.models import User


class TestInfo(models.Model):
    TestName = models.CharField(max_length=255)
    MaxMarks = models.IntegerField()
    TimeDuration = models.IntegerField()
    PosMarks = models.IntegerField()
    NegMarks = models.IntegerField()
    InputTextFile = models.FileField(upload_to='Tests/{}/'.format(TestName),\
    validators[FileExtensionValidator(allowed_extensions['txt'])],blank=False)

    def __str__(self):
        return self.TestName

Upvotes: 1

Views: 167

Answers (1)

Zollie
Zollie

Reputation: 1211

I think you just missed an additional function which could help you in this. I have not had time to test this particularly now but you are looking for something like this:

from django.core.validators import FileExtensionValidator
from django.db import models
from django.contrib.auth.models import User

def content_file_name(instance, filename):
    return "Tests/{folder}/{file}".format(folder=instance.TestName, file=filename)

class TestInfo(models.Model):
    TestName = models.CharField(max_length=255)
    MaxMarks = models.IntegerField()
    TimeDuration = models.IntegerField()
    PosMarks = models.IntegerField()
    NegMarks = models.IntegerField()
    InputTextFile = models.FileField(upload_to=content_file_name)
    validators[FileExtensionValidator(allowed_extensions['txt'])],blank=False)

    def __str__(self):
        return self.TestName

UPDATE - ON USING THE USERNAME AS FOLDER NAME TO SAVE UPLOADED FILE

If you want to add the username as the subfolder name where the user's uploads will be saved, into different subfolders by usernames. And at the same time you will keep the uploader's user name also in your model, then you should formulate your model a bit more like below. I added an extra field to your model as Uploader_info. It just gets the username at uploads, so it cannot be edited by the uploader user, it is just given always at uploads from your user table via request data.

in models.py:

from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError

def content_file_name(instance, filename):
    return "Tests/{subfolder}/{folder}/{file}".format(subfolder=instance.Uploader_info, folder=instance.TestName, file=filename)

def validate_file_extension(value):
    if value.file.content_type != 'text/plain':
        raise ValidationError('The uploaded file must be a text file')

class TestInfo(models.Model):
    Uploader_info = models.CharField(max_length=100, editable=False, null = True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='testinfos', on_delete=models.CASCADE, null=True)
    TestName = models.CharField(max_length=255)
    MaxMarks = models.IntegerField()
    TimeDuration = models.IntegerField()
    PosMarks = models.IntegerField()
    NegMarks = models.IntegerField()
    InputTextFile = models.FileField(upload_to=content_file_name, blank=False, validators=[validate_file_extension])        

    def __str__(self):
        template = '{0.TestName} {0.Uploader_info}'
        return template.format(self)

Do not forget to migrate.

Important! Register the model in the admin.py too with a short added function which will give the uploader's username to the records:

in admin.py:

from .models import TestInfo

class TestInfoAdmin(admin.ModelAdmin):

    def save_model(self, request, obj, form, change):
        if not change:
            obj.Uploader_info = request.user.username
        obj.save()

admin.site.register(TestInfo, TestInfoAdmin)

Your current view name is post for the Form submission, which has to be reformulated a bit like this:

in views,py

@login_required
def post(request):
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = TestForm(request.POST, request.FILES)
        # check whether it's valid:
        if form.is_valid():
            fulltest = form.save(commit=False)
            fulltest.user = request.user
            fulltest.Uploader_info = request.user
            TestName = form.cleaned_data.get('TestName')
            File = form.cleaned_data.get('InputTextFile')
            fulltest.save()
            messages.success(request, 'Test {} Posted successfully'.format(TestName))
            return redirect('Test-Making')
        else:
            parameters = {
              'form': form,                   
              'error': form.errors.as_ul()
            }
            return render(request, 'Test/post.html', parameters)

    # if a GET (or any other method) we'll create a blank form
    else:
        form = TestForm()
        return render(request, 'Test/post.html', {'form': form})

and your Form in forms.py is simply:

class TestForm(forms.ModelForm):
    class Meta:
        model = TestInfo
        fields = ('TestName', 'MaxMarks', 'TimeDuration', 'PosMarks', 'NegMarks', 'InputTextFile')

And that's about it. If your Form is OK then the above will work as I tested that successfully.

Upvotes: 1

Related Questions