Best way to retrieve the url of a imageField in django - python

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='')

Related

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})

Trouble with media directory/files on pythonanywhere

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)

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}}

How to Register Images using Django's ORM

How do you register an image file in a Django ImageField without using a form, and not copying any files?
I have several thousand JPGs located at /images, and I want to register them in an Image model similar to:
class Image(models.Model):
image = models.ImageField(upload_to='images', max_length=1000)
hash = models.CharField(max_length=1000, unique=True)
However, all the docs I can find on "loading" images into a Django project assume I'm doing so via a form, which also implies the image will be copied to MEDIA_ROOT. I'm not using a form, and I don't want to re-copy the several thousand JPGs, since they're already where they're supposed to be. I just want to create Image records that will store the filename of all the images I currently have. I've written a simple Python script to loop over each image, but I can't find how to properly create the Image record.
I also want to store a hash of the image content, to prevent duplicate records. e.g.
import hashlib
content = open(image_filename).read()
h = hashlib.sha512()
h.update(content)
imgobj.hash = h.hexdigest()
imgobj.save()
Would I override the default model.Model.save() method to do this?
If you have the script to loop over the images in your directory, you're nearly to a solution. Django will only store the path to the image in your Image.image field so basically all you need to do in your loop is:
#pseudo-code
for image_file in image_files:
image, created = Image.objects.get_or_create(hash=the_hash, \
defaults={'image' : 'path/to/image', 'hash' : the_hash)
That's a pretty easy way to build up only the unique records in your database without having to move the files, or use a form. You're either going to harmlessly return the image by the hash if it exists, or you're going to create a new record.
Hope that helps!
After digging through the code, and piecing together a few snippets I found, the following seems to work for me:
models.py
import os, hashlib
from django.db import models
class Image(models.Model):
image = models.ImageField(upload_to=IMAGE_UPLOAD_TO, max_length=1000)
hash = models.CharField(max_length=1000, unique=True)
def save(self, *args, **kwargs):
# Update image hash to ensure uniqueness.
h = hashlib.sha512()
h.update(self.image.read())
self.hash = h.hexdigest()
return models.Model.save(self, *args, **kwargs)
import_images.py
import os
from django.conf import settings
from django.core.files import File
from myapp import models
fn = os.path.join(settings.MEDIA_ROOT, 'images', 'mytestimage.jpg')
img = models.Image()
img.image.save(fn, File(open(fn, 'r')))
img.save()

How to delete old image when update ImageField?

I'm using Django to create a stock photo site, I have an ImageField in my model, the problem is that when the user updates the image field, the original image file isn't deleted from the hard disk.
How can I delete the old images after an update?
Use django-cleanup
pip install django-cleanup
settings.py
INSTALLED_APPS = (
...
'django_cleanup.apps.CleanupConfig', # should be placed after your apps
)
You'll have to delete the old image manually.
The absolute path to the image is stored in your_image_field.path. So you'd do something like:
os.remove(your_image_field.path)
But, as a convenience, you can use the associated FieldFile object, which gives easy access to the underlying file, as well as providing a few convenience methods. See http://docs.djangoproject.com/en/dev/ref/models/fields/#filefield-and-fieldfile
Use this custom save method in your model:
def save(self, *args, **kwargs):
try:
this = MyModelName.objects.get(id=self.id)
if this.MyImageFieldName != self.MyImageFieldName:
this.MyImageFieldName.delete()
except: pass
super(MyModelName, self).save(*args, **kwargs)
It works for me on my site. This problem was bothering me as well and I didn't want to make a cleanup script instead over good bookkeeping in the first place. Let me know if there are any problems with it.
Before updating the model instance, you can use the delete method of FileField object. For example, if the FileField or ImageField is named as photo and your model instance is profile, then the following will remove the file from disk
profile.photo.delete(False)
For more clarification, here is the django doc
https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.fields.files.FieldFile.delete
You can define pre_save reciever in models:
#receiver(models.signals.pre_save, sender=UserAccount)
def delete_file_on_change_extension(sender, instance, **kwargs):
if instance.pk:
try:
old_avatar = UserAccount.objects.get(pk=instance.pk).avatar
except UserAccount.DoesNotExist:
return
else:
new_avatar = instance.avatar
if old_avatar and old_avatar.url != new_avatar.url:
old_avatar.delete(save=False)
My avatrs has unique url for each person like "avatars/ceb47779-8833-4719-8711-6f4e5cabb2b2.png". If user upload new image with different extension like jpg, delete_file_on_change_extension reciever remove old image, before save new with url "avatars/ceb47779-8833-4719-8711-6f4e5cabb2b2.jpg" (in this case). If user uploads new image with same extension django overwrite old image on storage (disk), because images paths are the same.
This works fine with AWS S3 django-storage.
Here is an app that deletes orphan files by default: django-smartfields.
It will remove files whenever:
field value was replaced with a new one (either uploaded or set manually)
field is cleared through the form (in case that field is not required, of course)
the model instance itself containing the field is deleted.
It is possible to turn that cleanup feature off using an argument: ImageField(keep_orphans=True) on per field basis, or globally in settings SMARTFIELDS_KEEP_ORPHANS = True.
from django.db import models
from smartfields import fields
class MyModel(models.Model):
image = fields.ImageField()
document = fields.FileField()
try this, it will work even if old file is deleted
def logo_file(instance, filename):
try:
this = business.objects.get(id=instance.id)
if this.logo is not None:
path = "%s" % (this.logo)
os.remove(path)
finally:
pass..
code will work even without "try .. finally" but it will generate problem if file was accidently deleted.
changed: move model matching inside "try" so it will not throw any error at user signup
Let me know if there are any problems.
Completing Chris Lawlor's answer, tried this and works.
from YOURAPP.settings import BASE_DIR
try:
os.remove(BASE_DIR + user.userprofile.avatarURL)
except Exception as e:
pass
The URL has a pattern of /media/mypicture.jpg
What I did is saving the path to the old image and if form is valid I would delete the old one.
if request.method == 'POST':
old_image = ""
if request.user.profile.profile_picture:
old_image = request.user.profile.profile_picture.path
form = UpdateProfileForm(request.POST,request.FILES,instance = profile)
if form.is_valid():
if os.path.exists(old_image):
os.remove(old_image)
form.save()
It is a little messy , but you do not install third parties or anythin

Categories

Resources