FIle upload with chunks: avoid saving it twice - django

I use a custom function to upload a file splitting it in chunks, as documented here.
My problem is that calling save() after handle_uploaded_file() uploads my file twice, one into "MEDIA_URL/my_path" dir and one into "MEDIA_URL".
But I would like to have only one upload, the one with chunks.
It is possible to force save() to make 'chunked' upload?
Or should I use different approaches?
Thank you.
models.py
class ShapeFile(models.Model):
name = models.CharField(max_length=100)
srid = models.ForeignKey(SpatialRefSys)
user = models.ForeignKey(User)
color_table = models.ForeignKey(ColorTable)
file = models.FileField(upload_to="my_path")
class Meta:
unique_together = ('name', 'user')
forms.py
class UploadForm(ModelForm):
class Meta:
model = ShapeFile
fields = ('name','user','srid','file','color_table')
widgets = {'srid': TextInput(),
'user': HiddenInput()
views.py
def handle_uploaded_file(fileName, filePath):
with open(filePath, 'wb+') as destination:
for chunk in fileName.chunks():
destination.write(chunk)
#login_required
def shapeIng(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
req = request.POST
# Split uploaded file into chunks
fileName = request.FILES['file']
filePath = ShapeFile(file=fileName).file.path
handle_uploaded_file(fileName, filePath)
form.save()
messages.success(request, 'Shapefile upload succesful!')
return redirect('shapeCreated')
else:
messages.error(request, 'Something went wrong uploading Shapefile.')
else: # request.method == 'GET'
form = UploadForm(initial={'user': request.user})
return render_to_response('my_app/base_shapeIngestion.html',
{'form': form},
context_instance=RequestContext(request))

change your view function to this:
def testupload2(request):
if request.method == 'POST':
file_name=request.FILES['file']
form = SomeForm(request.POST, request.FILES)
if form.is_valid():
dest_file = open('C:/prototype/upload/'+ str(file_name), 'wb+')
path = 'C:/prototype/upload/'+ str(file_name)
for chunk in request.FILES['file'].chunks():
dest_file.write(chunk)
dest_file.close()
t = get_template("testupload2.html")
lst = os.listdir('C:/downloads/prototype/prototype/upload/')
html = t.render(Context({'MEDIA_URL':'http://127.0.0.1:8000/site_media/'}))
return HttpResponse(html)

Related

Upload in Django

I was trying to understand how to upload files using Django, so I used the example (latest version, upload is working):
https://github.com/axelpale/minimal-django-file-upload-example
I have a few Questions now:
How do I set a max file size? (I have checked the documentation of Django, but I don't get it)
Is there a way for me to read the file before the user uploads it?
(i.e a program that checks certain things and if they are ok, it can be uploaded)
Here is some code...
model.py:
class Document(models.Model):
name= models.CharField(max_length=500)
docfile= models.FileField(upload_to='documents/%Y/%m/%d', verbose_name="", validators=[validate_file_size])
def __str__(self):
return self.name + ": " + str(self.docfile)
validators.py:
def validate_file_size(value):
filesize= value.size
if filesize > 20971520:
raise ValidationError("File too big")
else:
return value
views.py:
def uploadView(request):
message = 'Upload your .csv-File'
if request.method == 'POST':
form = documentForm(request.POST, request.FILES)
if form.is_valid():
newDoc = Document(docfile=request.FILES['docfile'])
newDoc.save()
return redirect(uploadView)
else:
message = 'The form is not valid.'
else:
form = documentForm()
documents = Document.objects.all()
context = {'documents': documents, 'form': form, 'message': message}
return render(request, 'upload.html', context)

Get the real name of saved file (Model) after upload - Django

I'm trying to get the real name of the uploaded file in Django. It turns out that if a file name already exists inside the Model, the Django will create another name aleatory.
For example, if 'abc.xls' is inside the Model database and I try to upload 'abc.xls' again, Django will create a file called 'abc_123456.xls'. That's not the problem!
My question is: how can I get this name ('abc_123456.xls') inside my view.py?
def index(request):
if 'GET' == request.method:
form = DocumentForm()
return render(request, 'auditoria_app/index.html', {'form': form})
else:
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
form.save()
# I'd like to get the correct file name here!
mediaFolder = settings.MEDIA_ROOT
fileName = f"{mediaFolder}/SAE/{form.cleaned_data['file'].get_alternative_name}" # .xlsm (Excel file)
splitFileName = fileName.split('.')
zipFileName = f"{splitFileName[0]}.zip" # .zip
My model:
from django.db import models
from django.core.validators import FileExtensionValidator
class Document(models.Model):
file = models.FileField(upload_to='SAE/')
uploaded_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.file)
You can retrieve it from file with name attribute. Bu it gives you file name with joined MEDIA_ROOT. You can extract MEDIA_ROOT from that attribute. Also you can retrieve object when form.save(commit=False) Try this:
def index(request):
if 'GET' == request.method:
form = DocumentForm()
return render(request, 'auditoria_app/index.html', {'form': form})
else:
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
document = form.save(commit=False)
document.save()
# you can get file_name like this
file_name = document.file.name.split('/')[-1]

How to upload an image and store its link in database

I read some documents in Django site such as: Basic file uploads and FileField.storage. However, I still don't understand how to upload a file (or an image) to server and store its link in database. I would like to write the files to the following directory such as: 'image/%Y/%m/%d'
Please give me a sample code. Thank you so much.
My code follows as:
#models.py
class Image(models.Model):
imageid = models.AutoField()
title = models.CharField(max_length=100)
imagepath = models.ImageField(upload_to='images/%Y/%m/%d/')
#forms.py
class UploadFileForm(forms.Form):
title = forms.CharField(max_length=100)
image = forms.FileField()
#views.py
def upload_file(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
# How to upload file to folder named 'images/%Y/%m/%d/'
# How to save the link above to database
return HttpResponseRedirect('/success/url/')
else:
form = UploadFileForm()
return render_to_response('upload.html', {'form': form})
I believe if you create the upload form as a model form and then just save it in the view, it will have the effect of saving the file to the filesystem and the path to the model. This is a basic example, but I think it should work.
# forms.py
class UploadFileForm(forms.ModelForm):
class Meta:
model = models.Image
# views.py
...
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect('/success/url/')

Uploading Profile Image using Django ModelForm

I've looked around at related questions, but none of the answers seem to work. I'm trying to upload a profile image for a user and have it replace (overwrite) the current image. Upon saving the image I want to change the filename to the user id. In it's current form the image will upload, but it won't replace the existing image (e.g. it'll be saved as 2_1.png).
class PhotoForm(forms.ModelForm):
def save(self):
content_type = self.cleaned_data['photo'].content_type.split('/')[-1]
filename = '%d.%s' % (self.instance.user.id, content_type)
instance = super(PhotoForm, self).save(commit=False)
instance.photo = SimpleUploadedFile(filename, self.cleaned_data['photo'].read(), content_type)
instance.save()
return instance
class Meta:
model = UserProfile
fields = ('photo',)
def photo_form(request):
if request.method == 'POST':
form = PhotoForm(data=request.POST, file=request.FILES, instance=request.user.get_profile())
if form.is_valid():
form.save()
else:
form = PhotoForm()
return render(request, 'photo_form.html', {'form': form})
def photo_form(request):
if request.method == 'POST':
form = PhotoForm(data=request.POST, file=request.FILES, instance=request.user.get_profile())
if form.is_valid():
handle_uploaded_file(request.FILES['<name of the FileField in models.py>'])
def handle_uploaded_file(f):
dest = open('/path/to/file', 'wb') # write should overwrite the file
for chunk in f.chunks():
dest.write(chunk)
dest.close()
check here: https://docs.djangoproject.com/en/dev/topics/http/file-uploads/
If that doesn't work, I suppose you could just use os.system to delete the file if the form is accepted. That probably wouldn't be that great of a solution, but it should work.

How to override FileField and automatically delete previous file in Django?

im django beginner (django 1.2.5)
I have that model:
class Document(models.Model):
file = models.FileField(upload_to='documents/%Y/%m/%d', null=True, blank=True)
title = models.CharField(max_length=30)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
author = models.ForeignKey(User)
#other fields
#other fields
and model form to this:
class DocumentForm(ModelForm):
file = forms.FileField(required=True, error_messages={'required' : 'required!','empty': "empty!"})
title = forms.CharField(widget = forms.TextInput(attrs={'size': 93,}), error_messages={'required': 'required!'})
#other fields
#other fields
class Meta:
model = Document
exclude = ('author',)
def save(self, author, commit=True):
document=ModelForm.save(self,commit=False)
document.author = author
if commit:
document.save()
return document
I uploading new documents in using DocumentForm above and it works pretty but when i trying edit some document i cannot put new file in place previous. I may change every field except FileField.
def document_edit(request, document_id):
doc = get_object_or_404(Document, id=document_id)
form = DocumentForm(instance=doc)
if doc.author == request.user:
if request.method == "POST":
form = DocumentForm(request.POST, request.FILES, instance=doc)
if form.is_valid():
if request.POST.get('cancel'):
return HttpResponseRedirect('/')
elif request.POST.get('delete'):
document = Document.objects.get(id=document_id)
document.file.delete()
document.delete()
return HttpResponseRedirect('/')
else:
form.save(author=request.user)
return HttpResponseRedirect('/')
else:
# return again form with errors
else:
# return form with doc instance
else:
# return "you can't edit this doc!"
I research django documentation and i only know i should write some custom save method in some class but i completely have no idea how can i do this. It should be save() method in Document() or in DocumentForm()?
Generally i want this: When i put path to new file in form i want override this new file in his place and automatically delete previous file.
Can you help me? Thanks in advance!
You are on the right track, you just want to use the instance keyword, so your form reflects the object being edited. Simplified version:
def edit_upload(request, document_id):
doc = get_object_or_404(Document, id=document_id)
if request.method == 'POST': # If the form has been submitted...
form = DocumentForm(request.POST, request.FILES, instance=doc)
if form.is_valid(): # All validation rules pass
if doc.file: # If document has file already...
doc.file.delete() # delete it
form.save() # Saves object, uses new uploaded file
return redirect('/thanks/') # Redirect after success
else:
form = DocumentForm(instance=doc) # Show form to edit
return render(request, 'edit.html', {
'form': form,
})