I am currently using Django's forms to create a FileField that allows the user to upload a file. However, after I select the file, it creates an object of type django.core.files.uploadedfile.InMemoryUploadedFile, which is a copy of the original file.
How would I go about to get the original path of this file so I can either modify or replace it?
# forms.py
class UploadFile(forms.Form):
file = forms.FileField(label="Select File: ", required=True)
# views.py
def get_path(response):
context = {}
if response.method == "POST":
form = UploadFile(response.FILES)
if form.is_valid():
file = form.cleaned_data["file"]
# Is there a way to get the original file path or should I just not use FileField?
print("The File Path Here")
temporary_file_path function
if form.is_valid():
file = form.cleaned_data["file"]
print(file.temporary_file_path())
Not sure why you'd want to try modifying an InMemoryUploadedFile file path. However, just keep in mind the term "InMemory".
The file has not been saved as yet, so there will be no path associated with the file before saving. To my knowledge (I stand corrected if I'm wrong here), InMemoryUploadedFile do not have a path attribute either, but content/file, a filename, etc.; in which the filename can be modified. You can see the doc here.
You could make modifications to the filename though if you wish by doing the following:
if form.is_valid():
form.cleaned_data["file"].name = "modify.txt" # or to whatever filename and extension you want.
form.save() # If you have a save method defined
However, you could get a little creative in saving the file to a specific path within the media folder though. For example:
# forms.py
import os
from django.conf import settings
class UploadFile(forms.Form):
file = forms.FileField(label="Select File: ", required=True)
# The save method that will save the file to a specific location in the media folder.
def save(self):
filename = file.name
folder = 'uploaded/'
# Create the folder if it doesn't exist.
try:
os.mkdir(os.path.join(settings.MEDIA_ROOT, folder))
except:
pass
# Save the uploaded file inside that folder.
file_path = os.path.join(settings.MEDIA_ROOT, folder, filename)
with open(file_path, 'wb+') as destination:
for chunk in file.chunks():
destination.write(chunk)
# view.py
def get_path(response):
if response.method == "POST":
form = UploadFile(response.FILES)
if form.is_valid():
file = form.cleaned_data["file"]
# file.name = "modify.txt" # Can modify here if necessary...
form.save()
Related
I am learning django. I am stuck with this problem.
I have created a form in which a user uploads a text file and selects a gender. The user is also supposed to write the name of the text file in the text box.
sample output
In the backend I want to save the name of the text file along with the gender in a model. The purpose of doing this is because when multiple users will use the application, I should know which user selected what gender so that I can produce the desired output.
As I have already mentioned the user needs to type the name of the file, I was thinking is it possible to get the name of the file from the uploaded file and then save it to the model so the user need not type the name of the file and hence no text box.
Here is the link to my git repository - https://github.com/AnshulGupta22/auto_generation.git
Can someone please tell me how to do it? As I already said I am new to django and some help will be appreciated.
You can get the name of file from request.POST and you can create an object for file_name so:
##in views.py
def get_gender(request):
if request.method == 'POST':
form = GenderForm(request.POST)
uploaded_file = request.FILES['document']
file_name = request.FILES["name"] #NEW
Gender.objects.create(file_name=file_name) #NEW
fs = FileSystemStorage()
name = fs.save(uploaded_file.name, uploaded_file)
fbh = name[:-4]
if form.is_valid():
form.save()
run(["gnome-terminal", "--", "sh", "-c", f"espeak -ven+m1 -f {uploaded_file.name} -w {fbh}.wav "])
return HttpResponse("Blog created")
##in models.py
class Gender(models.Model):
file_name = models.CharField(max_length=120) #NEW
in models.py,
class Files(models.Model):
File = models.FileField(upload_to="documents/")
in my views.py, all works perfectly when i remove the 3rd line
file = Files(File=f)
file.File.upload_to('Media/')
file.save()
Question is.
I want to create new upload_to Folder from views.py only. is it even POSSIBLE ?
I know the instance function method of creating Folder used in models.py, i dont want to use it
You can set the name of the file, and thus work with:
from django.conf import settings
from os.path import join as pathjoin
file = Files(File=f)
old_path = car.photo.path
file.File.name = f'Media/{file.File.name}'
new_path = pathjoin(settings.MEDIA_ROOT, file.File.name)
rename(path, new_path)
file.save()
We thus first construct a Files object with f as File, then we check the path, create a new name, and then move the file to the new directory and update the Files object in the database.
You can do this in bulk by enumerating over the Files items and set the file as described in the answer. Instead of saving the object directly, you can collect these in a list and perform a .bulk_update(…) [Django-doc]:
from django.conf import settings
from os.path import join as pathjoin
files = list(Files.objects.all())
for file in files:
old_path = car.photo.path
file.File.name = f'Media/{file.File.name}'
new_path = pathjoin(settings.MEDIA_ROOT, file.File.name)
rename(path, new_path)
Files.objects.bulk_update(files, ['File'])
In my Django "sources" project I have the "bulletins" app.
I have a view "model_form_upload" inside of which I get the uploaded file name ("csvFilename") when I upload it.
views.py
from bulletins.forms import ErnageForm
from bulletins.models import ErnageModel
from bulletins.pretreatment import pretreatment
def model_form_upload(request):
if request.method == 'POST':
form = ErnageForm(request.POST, request.FILES)
if form.is_valid():
form.save()
for filename, file in request.FILES.items():
csvFilename = file.name
resultPretreatment = pretreatment()
print(resultPretreatment)
return redirect('proceed_FT')
else:
return redirect('upload_fail')
else:
form = ErnageForm()
return render(request, 'bulletins/upload_csv.html', {'form': form})
Beside that, I have a python script "pretreatment.py" that effectuates a bunch of pandas-dataframe transformations on my csv file.
pretreatment.py
def pretreatment(csvFileToTreat="..."
#... all the transformations with pandas dataframes...
df.to_csv(txtExportFilePath, index=False, header=None, sep='\t')
models.py
class ErnageModel(models.Model):
csv = models.FileField(upload_to="METEO ERNAGE/{0}/{1}".format(yearToTreat,monthToTreat))
My csvFilename will always be a string of this type : "Ernageyyyymm.csv".
My question here is : How can I collect my csvFilename variable out of the views.model_form_upload function to use its date information it in the pretreatment python script variables "csvFileToTreat" and "txtExportFilePath".
Those two ones are of the type "path/Ernage{0}{1}.format(yearToTreat,monthToTreat).
As I have to use it each month I would like to have my files uploaded in media/METEO ERNAGE/{0}/{1}".format(yearNumber,monthNumber) as you can see in the FileField upload_to and then everything happening in this directory. So the pretreatment charges the uploaded file and then export in it also.
I hope all this is as clear as possible, thank you for your help.
store csvFilename name in a session like this:
request.session['file']=csvFilename
and you can use this session where ever you want like this.
csvFilename =request.session['file']
I generate a file in python, and want to "upload" that file to the django database. This way it is automatically put inside the media folder, and organized neatly with all other files of my application.
Now here is what I tried: (type hinting used, since it's python 3.6)
# forms.py
class UploadForm(forms.ModelForm):
class Meta:
model = UploadedFile
fields = ('document',)
# models.py
class UploadedFile(models.Model):
document = models.FileField(upload_to=get_upload_path)
# mimetype is generated by filename on save
mimetype = models.CharField(max_length=255)
# ... additional fields like temporary
def get_upload_path(instance: UploadedFile, filename):
if instance.temporary:
return "uploaded_files/temp/" + filename
return "uploaded_files/" + filename
# views.py, file_out has been generated
with open(file_out, 'rb') as local_file:
from django.core.files import File
form = UploadForm(dict(), {'document': File(local_file)})
print(form.errors)
if form.is_valid():
file = form.save(commit=False)
# ... set additional fields
file.save()
form.save_m2m()
return file
Now this is not the only thing I've tried. First I've gone with setting the FileField directly, but that resulted in the save() to fail, while the mimetype field is set. Because the original file sits outside the media folder, and thus a suspicious file action is triggered.
Also, the form gives some feedback about the "upload", through the form.errors.
Depending on my approach, either the save() fails as mentioned above -- meaning the "uploading" does not actually copy the file in the media folder -- or the form returns the error that no file was transmitted, and tells to check the form protocol.
Now my theory is, that I would have to go and initialize my own instance of InMemoryUploadedFile, but I could not figure out how to do that myself, and no documentation was available on the internet.
It feels like I'm taking the wrong approach from the get go. How would one do this properly?
Do you have get_upload_path defined? If not, that would explain the errors you're getting.
From what I can see you're on the right track. If you don't need a dynamic path for your uploads, if you just want them in media/uploads, you can pass in a string value for upload_to (from the Django docs):
# file will be uploaded to MEDIA_ROOT/uploads
document = models.FileField(upload_to='uploads/')
First of all, thanks to Franey for pointing me at storage documentation which lead me to contentfile documentation.
The ContentFile actually solves the problem, because it basically is the self-instantiated version of InMemoryUploadedFile that I was looking for. It's a django File that is not stored on disk.
Here's the full solution:
# views.py, file_out has been generated
with open(file_out, 'rb') as local_file:
from django.core.files.base import ContentFile
# we need to provide a name. Otherwise the Storage.save
# method reveives a None-parameter and breaks.
form = UploadForm(dict(), {'document': ContentFile(local_file.read(), name=name)})
if form.is_valid():
file = form.save(commit=False)
# ... set additional fields
file.save()
form.save_m2m()
return file
I am trying to work with a filestorage in Django. Everything is working fine but a thing in my save method I guess. I have a model with a FileField
download_url = models.FileField(verbose_name = 'Konfig', upload_to = file_path, storage = OverwriteStorage())
In this method in my model I create the file_path
def file_path(instance, filename):
path = os.getcwd() + '/files'
return os.path.join(path, str(instance.download_url), filename)
And the filestorage method I use is outsourced in my storage.py which I import in my models.py
from django.core.files.storage import FileSystemStorage
class OverwriteStorage(FileSystemStorage):
def _save(self, name, content):
if self.exists(name):
self.delete(name)
return super(OverwriteStorage, self)._save(name, content)
def get_available_name(self, name):
return name
Now when I create a new file in the admin interface from django, it successfully uploads the file, makes a database entry with the correct filepath, but it fails to create the right path. When my filename is foo the path would look like following:
cwd/files/foo/foo
and if its name would be bar.txt it would look like following:
cwd/files/bar.txt/bar.txt
I don't want django to create a subdirectory based on the filename. Can you guys help me out ?
Im pretty sure you have to rename the save function from "save" to "_save".
On the Super Call, you used ._save, which isnt the same function as the save function above.
You can read alot about Super here