django change image file location with setattr - python

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

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.

How to approach complex circular relations between models?

I'm trying to set up a database for my media files, but can't find a way to translate the data to models and useful, but not redundant relationships.
There are Photos and Videos as models on the media side.
class Photo(MediaFile):
file = UniqueNameImageField(upload_to="photos/")
preview = UniqueNameImageField(upload_to="previews/", blank=True)
thumbnail = models.ImageField(upload_to="thumbnails/", blank=True)
mediatype = "photo"
class Video(MediaFile):
file = UniqueNameFileField(upload_to="videos/")
preview = UniqueNameFileField(upload_to="previews/", blank=True)
thumbnail = models.ImageField(upload_to="thumbnails/", blank=True)
mediatype = "video"
...
As Metadata I want to store Persons, Quotes, and Releases (contracts with the appearing person).
class Person(models.Model):
name = models.CharField(max_length=255)
age = models.IntegerField(null=True, blank=True)
city = models.CharField(max_length=255, blank=True)
district = models.CharField(max_length=255, blank=True)
occupation = models.CharField(max_length=255, blank=True)
def __str__(self):
return " ".join([self.name, "("+str(self.age)+")", self.city])
class Release(models.Model):
person = models.ForeignKey("Person", on_delete=models.PROTECT)
file = models.FileField(upload_to="release_docs/")
class Quote(models.Model):
person = models.ForeignKey("Person", on_delete=models.PROTECT)
#alternative by-line if it should not be name+age
alt_by = models.CharField(max_length=255)
text = models.TextField()
Every Release should be tied to a person.
Every Quote should be tied to a person.
But then every Person, Release, and Quote will be tied to the Photos and Videos.
The tricky part is this: Not every release counts for every photo a person is in, but rather just for a specific set of photos and videos.
Also, quotes don't belong to every photo and video a person appears in, but rather the ones from the day when the person is quoted.
This would bring me to set a many2many relationship between releases and photos/videos and also a many2many relationship between people and photos/videos and also a many2many relationships between quotes and photos/videos...
Now there is a lot of duplication because if a release is tied to a photo it already contains a connection to a person. However, the person object needs a separate relationship to the same photo.
In short, everything is tied to everything, but that leads to duplicated and maybe conflicting information in the DB.
I'm not looking for a solution to this case here, but rather a pointer in the right direction on how to handle these kinds of relations or simplify them. Surely this is not so uncommon. How to approach this / what keyword to look out for?
Thank you for your help.

IntegrityError on saving ImageField : Field 'path' doesn't have a default value

I am trying to save a Model with an ImageField but i can't because it keeps giving me this error: (1364, "Field 'path' doesn't have a default value")
I tried to give that field a default value, save first the model then the field, and did not work.
MODEL
class Images360(models.Model):
name = models.CharField(max_length=32, null=True)
oldpath = models.ImageField(upload_to='uploads/img/', null=True)
product = models.ForeignKey('Product', blank=True, null=True)
order = models.PositiveIntegerField()
METHOD
photo = Images360(name=filename,
product=self.product,
order=count
)
# photo.save()
photo.oldpath.save(filename, ContentFile(data))
EDIT
This have been working for months and it stopped working this week. I checked the files that are being uploaded and everything seems ok.
I checked the table through SQL and the table showed a path column. So, somehow, this column was created. I deleted it, through migrations, and I solved the problem.

Unable to save InMemoryUploadedFile to model in Django

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'

ImageField and several sizes in Django

I am new at Django (and Python) and was wondering about the ImageField property of a class. My example is about users of my website. I need 3 sizes of each picture of a user:
* user1-200-200.png
* user1-120-120.png
* user1-60-60.png
Of course I want only 1 file input type.
Should I create 3 properties and resize the one from the form for all properties:
class UserProfile(models.Model):
user = models.OneToOneField(User)
slug = models.CharField(max_length=30)
avatar_200_200 = models.ImageField(null=True, blank=True, upload_to="images/")
avatar_120_120 = models.ImageField(null=True, blank=True, upload_to="images/")
avatar_60_60 = models.ImageField(null=True, blank=True, upload_to="images/")
Can I use no property and do what I did in PHP, resize the images several times, move them to the proper folder and in the website, use the session to find them (I always rename the images and transform them to png).
I don't know what to do and what's possible to do and what's the best. Thanks for your advices.
Sure you can do that but you will have to reimplement some of built-in django features like file uploading, storage support, width/height calculating/saving etc.

Categories