upload_to dynamically generated url to callable - python

I've seen a lot of post about this problem without really understanding how to solve it.
I have this model:
class Project(TimeStampedModel):
name = models.TextField(max_length=100, default='no name')
logo = models.ImageField()
I'd like to have my image saved to media root following this template:
<name>/logo/<filename>
At first glance, I would like to do:
logo = models.ImageField(upload_to="{}/logo/".format(name))
But it raises this error: AttributeError: 'TextField' object has no attribute 'model'
Using a callable would be fine, partially though:
def upload_to_project(self, filename):
url = ("%s/%s") % (self.name, filename)
return url
and using:
logo = models.ImageField(upload_to=upload_to_project)
at least I have: <name>/<filename>
But how to pass the argument in this case? I'd like to reuse my function to upload in other subfolders, not only logo as:
<name>/logo/<filename>
<name>/history/<filename>
<name>/whatever/<filename>
Any idea on what I could do?

It looks like (re-reading your post it's not 100% clear) what you want is a partial application. Good news, it's part of Python's stdlib:
import os
from functools import partial
def generic_upload_to(instance, filename, folder):
return os.path.join(instance.name, folder, filename)
class Project(TimeStampedModel):
name = models.TextField(max_length=100, default='no name')
logo = models.ImageField(
upload_to=partial(generic_upload_to, folder="logo")
)
Note that this implementation assumes instance has a name attribute... if the instance attribute you want to use as first part has to be configurable too you can rewrite your upload_to as:
def generic_upload_to(instance, filename, folder, attrname):
return os.path.join(getattr(instance, attrname), folder, filename)
then use it as
class Project(TimeStampedModel):
name = models.TextField(max_length=100, default='no name')
logo = models.ImageField(
upload_to=partial(generic_upload_to, attrname="name", folder="logo")
)
And if you have more than one FileField or ImageField in your model and don't want to repeat the attrname part:
class Something(TimeStampedModel):
my_upload_to = partial(generic_upload_to, attrname="label")
label = models.CharField(max_length=100, default='no label')
logo = models.ImageField(
upload_to=partial(my_upload_to, folder="logo")
)
attachment = models.FileField(
upload_to=partial(my_upload_to, folder="attachment")
)

Related

Django admin: How to get path of uploaded file

I have created model field for User Pic and User_pic_url, what i'm trying to do is when i upload image it's path should get populated in user_pic_url.
Note that i'm uploading image from django admin itself. any idea.
snapshot for ref:
Snapshot
Model.py:
class Main(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=100)
about = models.TextField()
contact = models.CharField(default='0', max_length=12)
email = models.CharField(default='-', max_length=50)
linkedin = models.CharField(default='-', max_length=50)
github = models.CharField(default='-', max_length=50)
site_name = models.CharField(default='-', max_length=50)
resume = models.FileField()
cover_letter = models.FileField()
user_pic = models.ImageField()
user_pic_url = models.TextField(default="-")
From Django documentation regarding managing files
Consider the following model, using an ImageField to store a photo:
class Car(models.Model):
name = models.CharField(max_length=255)
price = models.DecimalField(max_digits=5, decimal_places=2)
photo = models.ImageField(upload_to='cars')
Any Car instance will have a photo attribute that you can use to get
at the details of the attached photo:
car = Car.objects.get(name="57 Chevy")
car.photo
<ImageFieldFile: cars/chevy.jpg>
car.photo.name
'cars/chevy.jpg'
car.photo.path
'/media/cars/chevy.jpg'
car.photo.url
'http://media.example.com/cars/chevy.jpg'
if you want to get the uploaded path first make sure you have configure you MEDIA_URL MEDIA_ROOT in settings.py and also you have to put your put url patterns for media url given in the documentation
if you have already done that you have to simply put the query set
obj = Main.objects.all().first()
like this when you get any object you have to go to the imagefield and add url after that like this
you have to only put url after all the configuration in the imagefield
print(obj.user_pic.url) <---------you can get your url like this
You don't need a user_pic_url field. You can fetch the data from the user_pic field itself
class Main(models.Model):
# rest of your fields
user_pic = models.ImageField()
#property
def user_pic_url(self):
return self.user_pic.url
Now, you can access the URL directly as,
model_instance = Main.objects.first()
print(model_instance.user_pic_url)

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.

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'

how to use curent date in image path django

I'm writing my news web-site and I already have translit widget imported into my django models.py
and it's looks like
class Article(models.Model):
class Meta():
db_table = "article"
verbose_name = "Новость"
verbose_name_plural = "Новости"
def get_image_path(self, filename):
path = ''.join(["static/article/", translit.slugify(filename)])
return path
article_image1 = models.ImageField(upload_to= get_image_path, null=True, blank=True, verbose_name = "Фотография 1")
In that case all uploaded images are stored in path_to_app/static/article/some_image_name.jpg
But we have a lot of images and storing them in one folder is very problematicaly.
the problem is:
How can I save image to
path_to_app/static/article/todays_year/todays_month/todays_date/some_image_name.jpg
Thank you
Use the datetime.date.strftime() function.
from datetime import date
def get_image_path(self, filename):
path = ''.join([date.today().strftime('static/article/%Y/%m/%d/'),
translit.slugify(filename)])
return path
You can return a string from your upload_to function - in this case get_image_path, with the date-formatters in it, and Django will pass the date information to the string. (https://docs.djangoproject.com/en/1.7/ref/models/fields/#django.db.models.FileField.upload_to)

Django: automatically choose model based on parameters entered in as another models values

I am creating a Django file browser app where it crawls files and makes the metadata available to view and search.
After initially entering the file as a "file" object, I would like to create a "video", "music", or "document" based on the extension of the file.
Some mock models...
class File(models.Model):
createdate = models.DateTimeField('date file entered', db_index=True, auto_now_add=True)
cdate = models.DateTimeField()
hash = models.CharField(max_length=14, default=_make_hash, unique=True)
slug = models.SlugField(blank=True, unique=True)
filename = models.CharField(db_index=True, max_length=2048)
fullpath = models.CharField(max_length=2048)
folder = models.CharField(max_length=2048)
class Video(models.Model):
createdate = models.DateTimeField('date file entered', db_index=True, auto_now_add=True)
imdblink = models.CharField(max_length=2048)
container = models.CharField(max_length=2048)
class Music(models.Model):
createdate = models.DateTimeField('date file entered', db_index=True, auto_now_add=True)
bitrate = models.CharField(max_length=2048)
artistname = models.CharField(max_length=2048)
songname = models.CharField(max_length=2048)
class FileType(models.Model):
typename = models.CharField(max_length=256)
extension = models.ForeignKey(FileExtension)
class FileExtension(models.Model):
extension = models.CharField(max_length=256)
A FileType of Video would be created with "avi,mpg,mp4,mkv", Music with "mp3,flac" and so on.
The Django app would be run over Video1.avi, Video2.mpg, Song1.mp3, Song2.flac etc. They would be entered into the database as a File object.
On save of the File object, the app would look at the files extension, compare it to the list and choose an appropriate model laid out by the FileType model.
I am hoping to keep the model choice parameters as database entries so I can add or remove without having to change the app.
I am unsure of how to reference which model I want the FileType entry to be.
Any advice or input would be appreciated.
If I had to do what you are trying to do, I would have the FileType model include another CharField named file_type_model_class. It would contain the name of the model class, such as Music. You can instantiate your class using something like this:
ft = FileType.objects.get(pk = some_id)
module = __import__('ff.models')
class_ = getattr('ff.models', ft.file_type_model_class)
instance = class_()
If you are trying to relate your file instance to one of video or music, you might want to try Generic Foreign Key
class File(models.Model):
# Your other fields...
file_type = models.ForeignKey(ContentType,
limit_choices_to={"model__in": ("video", "music")},
help_text=_("Is this video or music?"))
file_id = models.PositiveIntegerField(
help_text=_("Which specific video or music (by ID)?"))
file_object = generic.GenericForeignKey(ct_field="file_type",
fk_field="file_id")
def save():
# Do your work here on self.filename to determine self.file_type

Categories