Trouble with media directory/files on pythonanywhere - python

I seem to be having an issue serving up media content on my website. Everything works fine when run on localhost. However, when deployed to python anywhere, I receive a FileNotFoundError when I attempt to upload an image via a form.
I've taken a look through overflow for some related topics however I've not found any threads which have allowed me to solve my problem.
Here is the exact error received when submitting the image upload form:
It seems to be an issue with the resize method in models.py (which works fine on localhost)
Here are the appropriate setup files:
settings.py
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
...
MEDIA_DIR = os.path.join(BASE_DIR, 'media')
# Media
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
IMAGES_DIR = os.path.join(MEDIA_URL,"images")
I believe the error is due to the images_path method not retuning the correct location (but it works on localhost). Here is the model which stores the image and defines how images are saved:
models.py
class Image(models.Model):
# this might work?
def images_path():
return os.path.join(settings.IMAGES_DIR, 'usruploads')
def resize(self):
im = PIL.Image.open(self.image)
size=(200,200)
out = im.resize(size)
out.save(self.image.__str__())
def save(self, *args, **kwargs):
super(Image, self).save(*args, **kwargs)
self.resize()
image = models.ImageField(upload_to=images_path()[1:], max_length=255)
I will also throw in the media directory structure of the site in case this info is of use.
This is my first attempt at a deploying a Django web app via python anywhere so hopefully once this issue is fixed, it is a mistake I will never make again.
I have implemented the change you suggestd and the referenced url exists which is now a start however the server is claiming that it does not.
I have printed out the URL and path of the image as follows:
I can go to the index page and enter this url and it loads the image.
Ok, your suggestion was correct uring path instead of __str__() worked. The reason the issue second issue was occurring was then due to the fact that I was opening the image using url and trying to save it using path it did not like this. Thanks for your help!

Given
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
IMAGES_DIR = os.path.join(MEDIA_URL,"images")
and
class Image(models.Model):
# this might work?
def images_path():
return os.path.join(settings.IMAGES_DIR, 'usruploads')
def resize(self):
im = PIL.Image.open(self.image)
size=(200,200)
out = im.resize(size)
out.save(self.image.__str__())
def save(self, *args, **kwargs):
super(Image, self).save(*args, **kwargs)
self.resize()
image = models.ImageField(upload_to=images_path()[1:], max_length=255)
We can infer that
IMAGES_DIR = "/media/images"
and
image = models.ImageField(upload_to='media/images/usruploads', max_length=255)
That means files are uploaded to /<base_dir>/media/media/images/usruploads.
Issue
Looking at your logs, the error happens at the end of your logs.
...
out.save(self.image.__str__())
The issue is self.image.__str__() returns the relative path/filename of the file, and when you pass a relative path to out.save , it will try to save that file in the provided path RELATIVE TO THE CURRENT WORKING DIRECTORY.
Solution
What you have to do instead (assuming you want to replace the original image) is pass the absolute path of the original image:
...
out.save(self.image.path)

Related

Overwrite image located outside django MEDIA_ROOT

My models.imageField uploads images to a directory outside of the django directory path. In storages.py I have a function 'upload_to' that sets the directory and filename. It all works perfectly except that I cannot overwrite an existing file with the same name, django simply appends some random text to the file name.
My question is:
How can I overwrite the remote file?
What I have tried:
I have played around with the script suggested here:
https://www.itachay.com/2020/04/override-replace-uploaded-file-in-django.html
Which would work if I was using MEDIA_ROOT, but with my remote file I only ever get the django "SuspiciousFileOperation" warning.
Here is my 'working' code albeit that it won't overwrite any existing file of the same name.
models.py
from .storages import upload_to, remote_storage
class Newsletter(models.Model):
image_1 = models.ImageField(upload_to=upload_to, storage=remote_storage)
storages.py
from django.core.files.storage import FileSystemStorage
def upload_to(instance, filename):
dir_name = 'volume-'+str(instance.pk)
filename = 'image_1'+filename[-4:]
return '%s/%s' % (dir_name, filename)
remote_storage = FileSystemStorage(
location='/Users/some_dir/Documents/dev/mediatest/',
base_url='/Users/some_dir/Documents/dev/mediatest/'
)
My Tries:
TRY 1:
settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
MEDIA_TRACK = '/Users/some_dir/Documents/dev/mediatest/'
models.py
from .storages import upload_to, OverwriteStorage
class Newsletter(models.Model):
image_1 = models.ImageField(upload_to=upload_to, storage=OverwriteStorage())
storages.py
FileSystemStorage(
location='/Users/some_dir/Documents/dev/mediatest/',
base_url='/Users/some_dir/Documents/dev/mediatest/'
)
class OverwriteStorage(FileSystemStorage):
def get_available_name(self, name, max_length=None):
if self.exists(name):
os.remove(os.path.join(settings.MEDIA_TRACK, name))
return name
Try 1 Condition:
New record created and image is saved.
Record edited and a new image is selected and saved.
Try 1 RESULT:
Image is saved to the local django directory, not the remote media/volume-1 (no errors)
FileNotFoundError at /admin/newsletter/newsletter/6/change/
[Errno 2] No such file or directory: '/Users/some_dir/Documents/dev/mediatest/volume-1/image_1.png'
TRY 2:
The only change to the above script is to OverwriteStorage()
class OverwriteStorage(FileSystemStorage):
def get_available_name(self, name, max_length=None):
if self.exists(os.path.join(settings.MEDIA_TRACK, name)):
os.remove(os.path.join(settings.MEDIA_TRACK, name))
return name
Try 2 Condition:
Same record edited and image is saved.
Try 2 Result
SuspiciousFileOperation at /admin/newsletter/newsletter/6/change/
The joined path (/Users/some_dir/Documents/dev/mediatest/volume-1/image_1.png) is located outside of the base path component (/Users/some_dir/Documents/dev/backoffice/media)

How to access media files uploaded via a Django form?

I am accepting an image from the user in a form in Django. How do I access the uploaded image to show it in the web browser?
This is what I have in settings.py
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'
This is in my models.py
class Hotel(models.Model):
name = models.CharField(max_length=50)
image = models.ImageField(upload_to="images/")
Also, I have added the
if settings.DEBUG:
urlpatterns +=static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)
in urls.py
I tried to access the image as
def image_view(request):
if request.method=='POST':
farm = hotelForm();
form = hotelForm(request.POST,request.FILES)
if form.is_valid():
form.save()
modelff = Hotel(name=request.POST['name'],image = request.FILES['image'])
# print(modelff.image.url)
return render(request,'djangoform.html',{"form":farm,"uploaded_data_url":modelff.image.url})
else:
form = hotelForm()
return render(request,'djangoform.html',{"form":form})
And in my template, I accessed the image as <img src="{{uploaded_data_url}}">. But the image does not show up and the console shows image not found.
P.S. I have seen How to access media files in django How to access uploaded files in Django?
https://docs.djangoproject.com/en/dev/howto/static-files/#serving-files-uploaded-by-a-user-during-development
Django : accessing uploaded picture from ImageField
But none of them seem to help me. I can't find how do I include the 'images/' in my path. My uploaded_data_url shows /media/Screenshot%202020-04-18%20at%206.39.24%20PM.png while I expect it to show /media/images/Screenshot%202020-04-18%20at%206.39.24%20PM.png
Where is the problem?
Also, if there can be something similar to How can I get uploaded text file in view through Django? (f.read() in this question) It would be great.
Edit: Since from an answer, it seems the question was not clear, I would like to clarify that the above was just what I tried and I don't really know if it is correct or not. Secondly, the whole purpose of doing this was to get image from user, process it, and display the original and final image to the user. So if there is any other method that you have to do this, please share that.
If the uploaded_data_url shows this /media/Screenshot%202020-04-18%20at%206.39.24%20PM.png then this means that the images were saved under the media dir. This is expected since your conf is MEDIA_URL = '/media/'.
If you want it as /media/images/Screenshot%202020-04-18%20at%206.39.24%20PM.png then change to this MEDIA_URL = '/media/images/'.
The problem with the above is that it will interfere with all other media files, so a simpler solution would be to replace the /media/ with /media/images/.
Adapt this in your views.py
url = str(modelff.image.url)
modUrl = url.replace("/media/","/media/images/")
return render(request,'djangoform.html',{"form":farm,"uploaded_data_url":modUrl l})

Getting the Django-generated unique name of a file uploaded multiple times

I am using DRF backend to upload files. In my specific case I will want to get the name of the file, after it has been uploaded. The reason is that if a user uploads a file with same name, I am still able to process it independently.
views.py:
class ImageUploaderView(viewsets.ModelViewSet):
renderer_classes = [renderers.JSONRenderer]
queryset = ImageUploader.objects.all()
serializer_class = ImageUploaderSerializer
parser_classes = (MultiPartParser,)
serializer.py:
class ImageUploaderSerializer(serializers.ModelSerializer):
class Meta:
model = ImageUploader
models.py:
class ImageUploader(models.Model):
# name=models.ImageField(upload_to='media')
name=models.FileField(upload_to='media')
I tried to put signals and hooks after the model definitions but I am not being able to get this filename. Can someone shed some light pls?
UPDATE: Let me elaborate what I want to achieve essentially:
User1 hits endpoint "/api/calculate_interest_rate" which is rendered
by a frontend React component. "calculate_interest_rate" is served by
DRF, and lets the user upload a CSV file. This will be stored as
"user1.csv", the file is processed and then tabulated (rendered by
React).
At the same time and in parallel to User1, User2 hits the same endpoint "/api/calculate_interest_rate" and
by mistake he saves his file as "user1.csv", and uploads it to the systemn.
So I want to be able to detect both names of the file in order to process it. By using always the same default filename (ex. using the OverwriteStorage() technique), I will probably cause chaos when two or more users are using the same filename. Therefore I am looking into a technique that allows me to get the filename as is, and process it immediately.
How about using storage option?
class OverwriteStorage(FileSystemStorage):
def get_available_name(self, name, max_length=None):
print("filename", name)
#parts = name.split('.') you can separate name and extension.
return super().get_available_name(name)
upload_image = models.ImageField(
upload_to=[yourpath],
default=[defaultname],
storage=OverwriteStorage()
)
I suggest you to following this configuration:
1. Change your MEDIA_ROOT and MEDIA_URL inside file of settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = '/path/to/env/projectname/media'
2. Then, I suggest you to change your upload_to='media to upload_to='images/%Y/%m/%d, also rename your field of name with image.
class ImageUploader(models.Model):
image = models.FileField(upload_to='images/%Y/%m/%d')
# OR
# image = models.ImageField(upload_to='images/%Y/%m/%d')
Explanation; If you following this configuration, you could have uploaded images are following, eg: /media/images/2017/01/29/yourimage.jpg. This is one way to handle the problem of duplicated files.
3. But if you want to upload file with multiple times without duplicate files, you can using deconstructible;
import os, time, uuid
from django.db import models
from django.utils.deconstruct import deconstructible
class ImageUploader(models.Model):
#deconstructible
class PathAndRename(object):
def __init__(self, sub_path):
self.path = sub_path
def __call__(self, instance, filename):
# eg: filename = 'my uploaded file.jpg'
ext = filename.split('.')[-1] #eg: '.jpg'
uid = uuid.uuid4().hex[:10] #eg: '567ae32f97'
# eg: 'my-uploaded-file'
new_name = '-'.join(filename.replace('.%s' % ext, '').split())
# eg: 'my-uploaded-file_64c942aa64.jpg'
renamed_filename = '%(new_name)s_%(uid)s.%(ext)s' % {'new_name': new_name, 'uid': uid, 'ext': ext}
# eg: 'images/2017/01/29/my-uploaded-file_64c942aa64.jpg'
return os.path.join(self.path, renamed_filename)
image_path = time.strftime('images/%Y/%m/%d')
image = models.ImageField(upload_to=PathAndRename(self.image_path))

Best way to retrieve the url of a imageField in django

I'm using the following models:
class Product(models.Model):
# some other stuff
pictures = models.ManyToManyField(Image)
class Image(models.Model):
# MEDIA_ROOT = /full/path/to/my/media/folder/
image = models.ImageField(upload_to=settings.MEDIA_ROOT, default=DEFAULT_PROFILE_PICTURE)
Then in a view I wan to retrieve the images so i run the following code:
for pic in product.pictures.all():
pictures += [pic.image.url.replace(settings.PROJECT_ROOT, url)]
The problem here is that pic.image.url is giving me the system path, and I was expecting the relative path (something like /media/mypicture.jpg) so to fix this I used the replace function, but it looks to me that it should be a better way.
How can I build the model or access the image to avoid using the replace method?
Thanks in advance
You shouldn't use MEDIA_ROOT as a upload_to value. If you want to upload to MEDIA_ROOT without any subdirctories then just use an empty string '':
image = models.ImageField(upload_to='')

How do I properly display user-generated images using django?

I am trying to upload a user-generated image and then display in on my django web app. The image is getting uploaded to the server but I am having trouble displaying it.
models.py
image = models.ImageField(blank=True, null=True, max_length=255, upload_to="images/")
settings.py
MEDIA_ROOT = '/home/user/webapps/static/'
MEDIA_URL = 'http://user.webfactional.com/static/'
As an example, say I upload I file named Finland.gif. I can see the file uploaded. However when I look at the source, I see the source of the image as "www.foo.com/accounts/profile/images/Finland.gif" and not the static image url which should be "http://user.webfactional.com/static/images/Finland.gif". Any advice on how I should fix this?
userprofile.image.url gives you the full url to the image
Just solved it... I need to include:
http://user.webfactional.com/static/{{userprofile.image}}

Categories

Resources