Django how to change where uploaded file is saved - python

After uploading a CSV file into a Django form and submitting, Django creates a copy of this in the project app folder. I want to be able to tell Django to store this copy in another directory. How do I do that? Thanks.

You can do something like that :
models.FileField( upload_to="YourFolder")
In your setting your can specify the folder media :
Media URL
MEDIA_URL = "/media/"
Documentation for the FileField
If you want to specify the folder at save time :
from django.shortcuts import render
from django.conf import settings
from django.core.files.storage import FileSystemStorage
def upload(request):
folder='my_folder/'
if request.method == 'POST' and request.FILES['myfile']:
myfile = request.FILES['myfile']
fs = FileSystemStorage(location=folder) #defaults to MEDIA_ROOT
filename = fs.save(myfile.name, myfile)
file_url = fs.url(filename)
return render(request, 'upload.html', {
'file_url': file_url
})
else:
return render(request, 'upload.html')

Related

How can I add an Image to a Django Custom User Model (AbstractUser)?

I am trying to add 'image' to a class that extends AbstractUser. I would like to know how I could use fetch api to make a post request (vuejs) so that it calls on an views.py api and specifically uploads an image for a user.
I understand how this would work when it comes to frontend but I do not know what my django views.py api would do assuming I only want it to take a file object and just add to the appropriate user.
I have followed the below tutorial, however, they assume DRF is being used as opposed to an api simply made in views.py. This is why I am unsure about what my api will need to do with the image object I pass over using formData https://www.youtube.com/watch?v=4tpMG6btI1Q
I have seen the below SO post but it does not directly address what my views.py api would do. That is, will it just store an image in a certain format? A URL?
Add Profile picture to Django Custom User model(AbstractUser)
class CUser(AbstractUser):
image = models.ImageField(upload_to='uploads/', blank=True, null=True)
def __str__(self):
return f"{self.first_name}"
If my understanding is correct, you want to upload an image(in the process of creating a user) in an async manner. The followings are general settings and codes to upload an image and save an model instance.
First, You need to install pillow to use ImageField
pip install pillow
And create 'media' folder to save uploaded images(same level in manage.py)
settings.py
In Djnago, you need to specify where to save an image.
I have seen the below SO post but it does not directly address what my
views.py api would do. That is, will it just store an image in a
certain format? A URL?
MEDIA_ROOT and MEDIA_URL both are needed, one is for internal usage(storing images in server) and the other is for user
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_URL = '/media/' # trailing slash is needed.
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
models.py
ImageTest is a model to test uploading an image. You can use this in your CUser with a little adjustment.
from django.db import models
# Create your models here.
class ImageTest(models.Model):
image = models.ImageField(upload_to='avatar') # uploaded photo saved in MEDIA_ROOT/avatar/
# ex) media/avatar/pic.jpg
admin.py
from django.contrib import admin
from .models import ImageTest
class ImageTestAdmin(admin.ModelAdmin):
fields = ('image',)
list_display = ['id', 'image']
admin.site.register(ImageTest, ImageTestAdmin)
views.py
create-user is a view for serving upload form to user, and ajax-create-user is a handler for saving an image and an ImageTest instance.
from django.shortcuts import render
from django.http import HttpResponse
from .models import ImageTest
# Create your views here.
def create_user(request):
context = {'msg':'upload image'}
return render(request, 'templates/upload.html', context)
def ajax_create_user(request):
if request.method == 'POST':
user = ImageTest(image=request.FILES['avatar'])
user.save()
return HttpResponse('uploading image success')
upload.html
A template for rendering upload form. Write your vuejs codes here.
{{msg}}
<form action="{% url 'ajax-create-user' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" id="avatar" name="avatar" accept="image/*">
<button type="submit">Submit</button>
</form>
urls.py
Let's connect views and urls.
from django.contrib import admin
from django.urls import path
from anthony import views
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('anthony/', views.create_user, name='create-user'),
path('anthony/ajax-create-user', views.ajax_create_user, name='ajax-create-user'),
]
# for development environment(no needed in production)
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Summary
An image is passed to views via html form.
Views can access that image in request.FILES(or request.POST)
Assign an image to Model field, and save the instance. Both are saved automatically.

Django MEDIA not accessible

My Django app is unable to see MEDIA directory. When I run one of my views I need to open a file. The scenario is to:
take a record with requested id from data base
get the file path to a file
send the file to external server for OCR purposes
urls.py
from django.conf.urls.static import static
urlpatterns = [
#my urls
] + static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)
models.py
from django.db import models
from django.contrib.auth.models import User
class Homework( models.Model):
title = models.CharField(max_length = 200, default = None)
image = models.FileField(upload_to='user_scans/')
author = models.ForeignKey(User, default=None, on_delete = models.CASCADE)
latex = models.TextField(default = "No LaTeX Here")
settings.py:
from pathlib import Path
from django.conf.urls.static import static
BASE_DIR = Path(__file__).resolve().parent.parent
...
DEBUG = True
...
STATIC_ROOT = BASE_DIR / 'static'
STATIC_URL = '/static/'
MEDIA_ROOT = BASE_DIR / 'media'
MEDIA_URL = "/media/"
views.py
import requests
import json
from django.shortcuts import render, get_object_or_404
from .models import Homework
def create_homework(request):
if request.method == 'GET':
#some GET stuff
if request.method == 'POST':
homework = Homework()
homework.title = title
homework.image = image
homework.author = author
homework.save()
id = homework.id
json_to_mathsnip(id)
....
def json_to_mathsnip(id):
homework = get_object_or_404(Homework, pk=id)
f = open(homework.image.url, "rb")
...
some later stuff
...
Unfortunately I'm constantly running into an error:
FileNotFoundError at /homework/new
[Errno 2] No such file or directory: '/media/user_scans/kwa.png'
My main concern is I can access file from localhost:8000/media/user_scans/kwa.png
and from admin panel. Requested file is saved properly:
Also settings.py configuration seems to be in tact. What might be the issue?
(env) $ pip freeze
asgiref==3.5.0
backports.zoneinfo==0.2.1
certifi==2021.10.8
charset-normalizer==2.0.12
Django==4.0.1
idna==3.3
Pillow==9.0.0
psycopg2-binary==2.9.3
requests==2.27.1
sqlparse==0.4.2
urllib3==1.26.9
This may not be the best way, but I tried it and it works. The bottom line is that when using the open() function with a relative path, such as /media/user_scans/kwa.png, then that file must be in the same directory as where the function is called. You could just find the absolute path of your image files in your computer, then append the filename to that and it will work, or you can do what I have below.
import requests
import json
from django.shortcuts import render, get_object_or_404
from .models import Homework
# ADD THESE
import os
from pathlib import Path
def create_homework(request):
if request.method == 'GET':
#some GET stuff
if request.method == 'POST':
homework = Homework()
homework.title = title
homework.image = image
homework.author = author
homework.save()
id = homework.id
json_to_mathsnip(id)
....
def json_to_mathsnip(id):
homework = get_object_or_404(Homework, pk=id)
# ADD THESE
BASE_DIR = Path(__file__).resolve().parent.parent
path_to_image_file = str(BASE_DIR) + homework.image.url
f = open(path_to_image_file, "rb")
...
some later stuff
...
When you show an image in Django, or you access its url, it is a relative path, but the root is the url root, localhost:8000/, and the image url is appended to that, and Django manages to find the file (how exactly, I don't know). But the image file is in your computer, and that is what you want to open.

How to reload configurations in Django (Python)

I'm new to Django.
I made a URL shorter website like bitly for a test, and when users upload new URL, my program upload data to DB.
The problem is that Django doesn't reload urls.py. So if users upload their URL, data is saved in DB, but urls.py doesn't reload, so 404 shows because urls.py is not reloaded, because there are no data about new URL in urls.py.
I've searched in Google, I almost visited every website searched, but I couldn't find the answer.
How do I reload Django configurations without turning off and on the django server?
#root/url/urls.py
from django.urls import path
from django.http import HttpResponse #HttpResponse
from . import views
from . import models
urlpatterns = [
path("", views.Index),
path("urlprocess/", views.Process)
]
for i in models.Urllist.objects.all():
data = path(i.custom, lambda request: HttpResponse("<script>location.href = '{}'</script>".format(i.url)))
urlpatterns.append(data)
#views.py (Uploading Code)
def Process(request):
if request.method == "POST":
data = json.loads(request.body)
custom = data["custom"]
urllist = models.Urllist.objects.all()
for i in urllist:
if i.custom == custom:
return HttpResponse("customerror")
#upload
new = models.Urllist(url=data["url"], custom=custom)
new.save()
#reload(sys.modules[settings.ROOT_URLCONF])
return HttpResponse(custom)
else:
return HttpResponse(status="500")

How can I convert a dataframe to csv and save it to return it?

I'm reading a .xlsx file and converting it to .csv but I need to return it as a .csv because I don't have access to the path where it should be stored. I've only been able to return it as a dataframe. Any help will be really appreciated. Thanks.
import pandas as pd
def excel_to_csv(myfile):
print(myfile)
data_xls = pd.read_excel(myfile, index_col=None)
data_xls.to_csv(encoding='utf-8', index=False)
return(data_xls)
myfile = request.FILES['file']
if myfile.name.endswith('.xlsx'):
myfile=excel_to_csv(myfile)
If you're using Django, just save it into /media/ root, then you can access it via url
First you need to know the config of MEDIA_ROOT & MEDIA_URL inside your settings.py.
If it's not exist, then you can create one
[ settings.py ]
MEDIA_ROOT = os.path.join(BASE_DIR, 'myproject/static')
MEDIA_URL = '/media/'
Then, make sure you're already add the media config into your urls.py
from django.conf.urls.static import static
urlpatterns = [
# Project url patterns...
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Since you're mentioned this code myfile = request.FILES['file'], then I assume you're in views.py. Last but not least we need FileSystemStorage API.
There you go
[ views.py ]
from django.core.files.storage import FileSystemStorage
def upload(request):
if request.method == "POST":
csv_file = request.FILE['file']
fs = FileSystemStorage()
name = fs.save(csv_file.name, csv_file)
print(f"file_name = {fs.url(name)}"
return render(request, 'somepage.html')
Access the file via url,
www.yourhost.com/media/file_name
if you just want to read an Excel file and get the same data as csv printed to your screen, the following might be useful.
def excel_to_csv(file):
df = pd.read_excel(file)
csv_data = df.to_csv(encoding='utf-8', index=False)
return csv_data
If you really want to save the file I think I would need more information to the systems your are using and what exactly are you trying to accomplish.

Map physical path of a file to serve path for download

In my AngularJS/Python app, I uploaded a file using dropzone which has a physical path of something like this:
D:\uploadedFiles\tmpmj02zkex___5526310795850751687_n.jpg'
Now I want to create a link for this file for display and download on the front end. A link that looks something like this:
http://127.0.0.1:8000/uploadedFiles/tmpmj02zkex___5526310795850751687_n.jpg
What is the best way to achieve this?
You can simply add a url pattern in eg. urls.py:
from django.conf.urls import url
from . import views
urlpatterns = [
...,
url(r'^uploadedFiles/(?P<file_name>.+)/$', views.download, name='download')
]
and a download method in the controller in eg. views.py as suggested in here:
import os
from django.conf import settings
from django.http import HttpResponse
# path to the upload dir
UPLOAD_DIR = 'D:\uploadedFiles'
...
def download(request, file_name):
file_path = os.path.join(UPLOAD_DIR, file_name)
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="application/octet-stream")
response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(file_path)
return response
else:
raise Http404

Categories