File upload type validation in Django - python

I am trying to validate upload file type functionality in Django. The allowed extension would be xml only. The admin will upload a xml file and then the table would be populated with the data from xml file. The model has no filefield but the form has.
accounts/models.py --
class Coupling(models.Model):
coupling_name = models.CharField(max_length=150, blank=False, null=True, default="")
module_name = models.TextField(blank=False, null=True)
def __str__(self):
return self.coupling_name
class Meta:
verbose_name_plural = "Couplings"
accounts/forms.py --
class CouplingUploadForm(forms.ModelForm):
coupling_file = forms.FileField(label='XML File Upload:', required=True)
class Meta:
model = models.Coupling
exclude = ['coupling_name', 'module_name']
settings.py
UPLOAD_PATH = os.path.join(BASE_DIR, "static", "uploads")
CONTENT_TYPES = ['xml']
MAX_UPLOAD_SIZE = "2621440"
accounts/admin.py
class couplingAdmin(admin.ModelAdmin):
list_display = ('coupling_name','module_name')
form = CouplingUploadForm
admin.site.register(Coupling, couplingAdmin)
I have gone through some SOF references and most of them have model.FileField but in my case I do not want to save the file in model.
I tried with using magic -- https://djangosnippets.org/snippets/3039/ but I got an python-magic installation error -- Unable to find libmagic. So I would like to do it without magic.
Any help/suggestion/link is highly appreciated. Thanks in advance.

You can create a custom validator
def validate_file_extension(value):
import os
from django.core.exceptions import ValidationError
ext = os.path.splitext(value.name)[1]
valid_extensions = ['.xml']
if not ext.lower() in valid_extensions:
raise ValidationError(u'Unsupported file extension.')
Then in your form field
coupling_file = forms.FileField(label='XML File Upload:',
required=True, validators=[validate_file_extension])

Simply write an clean method to your forms.py
import os
def clean_coupling_file(self):
file = self.cleaned_data['coupling_file']
extension = os.path.splitext(file.name)[1] # [0] returns path+filename
VALID_EXTENSION = '.xml'
if extension != VALID_EXTENSION:
self.add_error(
'coupling_file',
_('Only files with ".xml" extension are supported, '
'received: "%s" file.' % extension)
)
return file

Related

Unique filename of uploaded file using the django FORM

I'm trying to generate a unique filename for the uploaded file using the Django forms. I've tried uuid_upload_path app but that app doesn't work with the form. Below is my code
Forms.py
class HelpGuideForm(forms.ModelForm):
title = forms.CharField(max_length = 50)
image = forms.ImageField(required = False)
class Meta:
model = Helpguide
fields = ['title', 'image']
Models.py
from uuid_upload_path import upload_to
class HelpguideImage(models.Model):
image = models.ImageField(upload_to = upload_to, blank=True, null=True)
I want a unique name for all uploaded files. something like sd564sadasd61.jpg. I'm using Django 2.2
In your Model you can set the upload_to of the imagefield to a function and then generate the uuid.
A very simple (untested) example:
import uuid
Class MyModel(models.Model):
def get_path(instance, filename):
extension = filename.split('.')[-1]
uuid = uuid.uuid1().hex
return f'path/to/file/{uuid}.{extension}'
image = ImageField(upload_to=get_path)
What I understand of your problem, you can set initial for FORM class when initialising it. like:
help_guide_form = HelpGuideForm(initial={'headline': uuid.uuid4().hex}, instance= Helpguide)
from django docs. Also see the initial reference.

Django image upload test error: "The attribute has no file associated with it"

I have a user profile app that allows a user to upload an avatar. I'm trying to test the image upload but am getting an error. Here are my relevant files:
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=100, blank=True)
last_name = models.CharField(max_length=100, blank=True)
avatar = models.ImageField(upload_to="media/", blank=True, null=True)
views.py
def userprofile(request):
# some code omitted for brevity
if form.is_valid():
form.save()
return HttpResponseRedirect(userprofile.get_absolute_url())
tests.py
class UserProfileTest(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(
username='testuser',
email='test#test.test',
password='testpassword',
)
self.user.userprofile.first_name = 'John'
self.user.userprofile.last_name = 'Doe'
def test_image_upload(self):
self.client.login(username='testuser', password='testpassword')
with open('media/tests/up.png', 'rb') as image:
self.client.post('/profile/', {'avatar': image})
print(self.user.userprofile.avatar.url)
Error:
File "userprofile/tests.py", line 110, in test_image_upload
print(self.user.userprofile.avatar.url)
ValueError: The 'avatar' attribute has no file associated with it.
In the tests, I've printed the response.content and can see the avatar's URL in the template. The 'media/tests/up.png' file is on the server and is being uploaded successfully. My goal here is to delete the file at the end of the test because it uploads every time I run the tests. I was going to delete the file by getting its file path (Django appends random alpha-numeric characters to the end of the file name when there are duplicates), but I can't get its file path right now.
You need a django file object for the test. You can make a python file and then have a django ImageFile :
from django.core.files.images import ImageFile
with open('media/tests/up.png', 'rb') as image:
test_file = ImageFile(image)
And then use test_file for the test.

Validating upload file type in Django

I have a Post model with a filefield which is used to upload files. How can I validate the file type (pdf for now, or any other types if I change to later). Preferably i'd like to validate the content, but if not I guess suffix would do too. I tried to look up online but most of the solutions I found are from way back and as the Django document get updated they don't work any more. Please if anyone can help. Thanks.
class Post(models.Model):
author = models.ForeignKey('auth.User',default='')
title = models.CharField(max_length=200)
text = models.TextField()
PDF = models.FileField(null=True, blank=True)
created_date = models.DateTimeField(
default=timezone.now)
published_date = models.DateTimeField(
blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
With Django 1.11 you can use FileExtensionValidator. With earlier versions, or for extra validation, you can build your own validator based on it. And you should probably create a validator either way because of this warning:
Don’t rely on validation of the file extension to determine a file’s type. Files can be renamed to have any extension no matter what data they contain.
Here's a sample code with the existing validator:
from django.core.validators import FileExtensionValidator
class Post(models.Model):
PDF = models.FileField(null=True, blank=True, validators=[FileExtensionValidator(['pdf'])])
Source code is also available so you can easily create your own:
https://docs.djangoproject.com/en/1.11/_modules/django/core/validators/#FileExtensionValidator
Think of validation in terms of:
Name/extension
Metadata (content type, size)
Actual content (is it really a PNG as the content-type says, or is it a malicious PDF?)
The first two are mostly cosmetic - pretty easy to spoof/fake that information. By adding content validation (via file magic - https://pypi.python.org/pypi/filemagic) you add a little bit of additional protection
Here is a good, related answer: Django: Validate file type of uploaded file It may be old, but the core idea should be easily adapted.
Firstly, I'd advise you change 'PDF' to 'pdf', then
to validate in older versions of Django, you could do this
forms.py
class PostForm(forms.ModelForm):
# fields here
class Meta:
model = Post
fields = ["title", "text", "pdf"]
def clean(self):
cd = self.cleaned_data
pdf = cd.get('pdf', None)
if pdf is not None:
main, sub = pdf.content_type.split('/')
# main here would most likely be application, as pdf mime type is application/pdf,
# but I'd like to be on a safer side should in case it returns octet-stream/pdf
if not (main in ["application", "octet-stream"] and sub == "pdf"):
raise forms.ValidationError(u'Please use a PDF file')
return cd
Here is a simple example for a form with file type validation based on Django 1.11 FileExtensionValidator
class ImageForm(ModelForm):
ALLOWED_TYPES = ['jpg', 'jpeg', 'png', 'gif']
class Meta:
model = Profile
fields = ['image', ]
def clean_avatar(self):
image = self.cleaned_data.get('image', None)
if not avatar:
raise forms.ValidationError('Missing image file')
try:
extension = os.path.splitext(image.name)[1][1:].lower()
if extension in self.ALLOWED_TYPES:
return avatar
else:
raise forms.ValidationError('File types is not allowed')
except Exception as e:
raise forms.ValidationError('Can not identify file type')

Django File-Transfers Save to New File for Each User

I'm trying to upload files using Django-filetransfer and I was wondering how to import the username to use as the name of the file the uploads are saved in. Right now I'm using User.username, but that is invalid. I guess the big question is how to access a user's attributes from models.py. Below is my models.py:
from django.db import models
from django.contrib.auth.models import User, UserManager
class UploadModel(User,models.Model):
title = models.CharField(max_length=64, blank=True)
user = models.ForeignKey(User)
file = models.FileField(upload_to='./uploads/'+str(User.username))
#property
def filename(self):
return self.file.name.rsplit('/', 1)[-1]
UPDATED:
app/upload/models.py
from django.db import models
from django.contrib.auth.models import User, UserManager
def uploadmodel_file_upload_to(instance, filename):
return 'uploads/%s/%s' % (instance.user.username, filename)
class UploadModel(models.Model):
user = models.ForeignKey('auth.user')
file = models.FileField(upload_to=uploadmodel_file_upload_to)
TemplateSyntaxError at /upload
Caught DatabaseError while rendering: column upload_uploadmodel.user_id does not exist
LINE 1: SELECT "upload_uploadmodel"."id", "upload_uploadmodel"."user...
Model attributes are officially defined when Python parses models.py code. You should use a callback for upload_to, to be generate the upload file path using the user related instance, for example:
def uploadmodel_file_upload_to(instance, filename):
return 'uploads/%s/%s' % (instance.user.username, filename)
class UploadModel(models.Model):
user = models.ForeignKey('auth.user')
file = models.FileField(upload_to=uploadmodel_file_upload_to)

validate file before upload

I have different categories like English,French and I've applied key language = models.ForeignKey(Category) I want to validate the file before it upload to disk.
I want if category is english then file upload to english/album_name if category is french then file upload to french/album_name. I've written forms.py file. But no idea. Thanks in advance
Models.py
class Artist(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(unique = True,max_length=100,help_text="Suggested value automatically generated from name. Must be unique.")
class Album(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(unique = True,max_length=100,help_text="Suggested value automatically generated from name. Must be unique.")
path = models.CharField(max_length=100,null=True, blank=True)
language = models.ForeignKey(Category)
albumid = models.CharField(max_length=100)
class Song(models.Model):
title = models.CharField(max_length=100)
artist = models.ManyToManyField(Artist)
music = models.ForeignKey(Music)
album = models.ForeignKey(Album)
file = models.FileField(upload_to='media/mp3_files')
forms.py
from django import forms
from db.song.models import Song
class SongAdminForm(forms.ModelForm):
class Meta:
model = Song
# No idea what to do next :-?
def clean_file(self):
file = self.cleaned_data["file"]
if file:
if file._size > 10*1024*1024:
raise ValidationError("Audio file too large ( > 10mb )")
if not file.content-type in ["audio/mpeg","audio/..."]:
raise ValidationError("Content-Type is not mpeg")
I think what you really want is to validate it after upload, but before saving it really.
When files are uploaded, they are in a temporary folder, so you can do all checks you want before saving it to the appropriate folder.
However, if you really mean pre-upload checks, you have to use Javascript (because it is client-side). But, don't forget that you should "never trust user input", and that includes what Javascript does, because user can change the Javascript code.

Categories

Resources