Unable to save InMemoryUploadedFile to model in Django - python

I have the following model:
class IdentifierImage(models.Model):
super = models.ForeignKey(Super)
identifier = models.CharField(null=False, blank=False, max_length=32, db_index=True)
image = models.ImageField(upload_to='/identifierimages/%Y/%m/', blank=True, null=True, )
class Meta:
unique_together = (
('survey', 'identifier'),
)
I also have a form (NOT a ModelForm and will never be) which uploads an image file, and I get it as an InMemoryUploadedFile. From the Super class I create the IdentifierImage object and try to save it with:
def save_identifier_image(identifier, image):
identifier_image, created = self.identifierimage_set.update_or_create(identifier=identifier)
<logics to remove old images, disabled for current tests>
identifier_image.image.save(image.name, image)
, where identifier is a valid string, and image is the InMemoryUploadedFile.
However saving of the InMemoryUploadedFile to model fails with SuspiciousFileOperation, and shows me: The joined path (C:/</identifierimages/%Y/%m/>) is located outside of the base path component (<MEDIA_ROOT>).
Why does this happen?
Why does it try to save under C:/ instead of under the media root? Or is something completely different going on?

You may want to try without the leading slash in your ImageField's upload_to path:
>>> os.path.join("/whatever", "/else")
'/else'

Related

Different upload and download urls for Django Models's Image Field

I need a way to have different upload and download URLs for ImageField. I want to upload the image to AWS S3 and while accessing the image I want the image to route through CDN (in my case Fastly).
def get_avatar_upload_path(identity, filename):
file_ext = filename.split('.')[-1]
file_name = f'{uuid.uuid4()}.{file_ext}'
return f'{settings.AWS_MEDIA_PREFIX}{identity.uuid}/{file_name}'
class Identity(identity_revision_base):
"""
Identity model. Ties a user to a chat identity.
"""
badges = models.ManyToManyField(Badge, blank=True)
key = models.UUIDField(_("key"), unique=True, default=uuid.uuid4)
uuid = models.UUIDField(_("UUID"), unique=True, default=uuid.uuid4)
avatar = models.ImageField(upload_to=get_avatar_upload_path, blank=True)
user = models.ForeignKey(
to=settings.AUTH_USER_MODEL,
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name="+",
)
Just like I am specifying upload path in ImageField, I would like a way to do something similar for accessing Image. It also seems that I cannot modify avatar.url once it is created. Is there any way around?
If there is any way, I can override the Model's behaviour to modify the url while accessing.
Did you check django-storages? It takes care of uploading to object store and returns CDN URLs for downloading. Fastly is S3 compatible so it should work.
In general you can also also use model methods with property decorator for getting a custom calculated value from model instances.

Wagtail models.py: How to create Custom folder in Media?

In Wagtail, How to add custom folder in Media and sync it into the database (Example) ?
NOTE: The Wagtail's Collection function is good, but for more than 1000 images/documents into a single folder will be pretty awkward to manage for I in the future (Ex: migration,...), so there is nothing to mention about Collection function in this question.
# If in Django (models.py) works:
class post(models.Model):
img = models.ImageField(upload_to='posts')
# So how in Wagtail (models.py) works:
class Posts(models.Model):
img = models.ForeignKey(
"wagtailimages.Image",
👉🏽 upload_to='posts', # How to add this line correctly ?
on_delete=models.SET_NULL,
null=True,
blank=False,
related_name="+",
)
Idea for Media Folder in Wagtail:
Media
authors
images
original_images
posts
images
original_images
...
If you want specific folders for specific models, you can set a specific folder by customizing the upload_to attribute in the model. Otherwise, the only option I know is to create collections.

Cant Upload Images to Django Admin

I am working on e-commerce project and i am stuck at this. Whenever admin adds new product,it should also add image related to the product. So i added the the column with ImageField but i am getting error again and again. Here is my code for models
class Product(models.Model):
product_id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=100, blank=True, null=True)
image = models.ImageField(db_column='image' , blank=True, null=True)
info = models.CharField(max_length=500, blank=True, null=True)
def image_tag(self):
if self.image:
return mark_safe('<img src="%s"/>' % self.image.url)
else:
return 'No Image Found'
image_tag.short_description = 'Image'
and in admin.py
class ProductAdmin(admin.ModelAdmin):
list_display = ('product_id', 'name','image_tag', 'info')
readonly_fields = ('image',)
admin.site.register(Product, ProductAdmin)
But every time i get this error
Exception Type: AttributeError
Exception Value:'bytes' object has no attribute 'url'
I tried using escape but it still not displaying images.I am using MySQL existing database. I could really use the help. Thanks
Currently you are storing image in your database as bytes, which Django does not prefers instead you should first specify MEDIA_ROOT this is folder where your image will be saved and only the URL will be saved in the database. Docs [SOURCE]
I assume you have already setup MEDIA settings and have installed Pillow.
Your ImageField will look like,
# No need to specify db_column as Django already stores by default field's name.
image = models.ImageField(upload_to='users/%Y/%m/%d/', blank=True, null=True)
Now in your templates you can get the image by,
<img src="http://127.0.0.1:8000/media/{{ profile.photo }}" id='profile-photo'>

How to save an image in a directory named on the User?

I am using Django2.2 and I am currently learning Django. I have created a model where I have to post an image of a certain thing and that model is connected with a User. I want to save the image on a directory named on that certain user
I have a custom User Model where I created a field called Profile Photo and that profile photo is saved to a directory named on that User.But then I created another application called 'Product' and there I created many fields including an image field.I am trying to save that image on that directory named on that specific User.
def user_directory_path(instance, filename):
return 'media/%s/%s' % (instance.username, filename)
class Products(models.Model):
title = models.CharField(max_length=100)
body = models.CharField(max_length=1000)
image = models.ImageField(upload_to = user_directory_path)
product_user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
When I try to save a Product and error occurs.
'Products' object has no attribute 'username'
Is there any successful ways to do it.
A Products instance indeed has no username attribute. The product_user has, you thus can change this to:
def user_directory_path(instance, filename):
return 'media/%s/%s' % (instance.product_user.username, filename)
Note: models have usually singular names, so Product, instead of Products.

django change image file location with setattr

In my project I have an app with a school model and a schoolsuggestion model.
The school model has fields for various images, like logo, seal, etc:
seal_image = models.ImageField(upload_to="sealimgs", null=True, blank=True)
logo_image = models.ImageField(upload_to="logos", null=True, blank=True)
The schoolsuggestion model has a field for what the corresponding school field is, and an image field.
field_name = models.CharField(max_length=255, default="")
image_field = models.ImageField(upload_to='img', null=True, blank=True)
When a school suggestion is approved, I overwrote save so that it will get the associated school's field via getattr(field_name) and set it with setattr(field_name, image_field) (I'm simplifying the syntax, but you get the idea).
Problem is, the file is still being saved in img, presumably because I'm using setattr and not the image field specific save, which would have uploaded it to the correct directory.
So how do I do this? It makes sense that I could maybe get the upload_to value of the attribute somehow, resave the image with that filepath somehow, and then use setattr on the resaved image, but I can't figure out how to do the first two.
I modified the upload to to this function and included it in the file above the class def:
def get_upload_to(instance, filename):
return School._meta.get_field(instance.field_name).upload_to+"/"+filename

Categories