dynamic FileField path - python

I'm trying to use get_file_path function to generate dynamic path. Can I use Album slug field instead of this str(instance.id) in get_file_path? thanks
Here is models
def get_file_path(instance, filename):
return os.path.join('files', str(instance.id), filename)
class Album(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(unique = True,max_length=100,help_text="Suggested value automatically generated from name. Must be unique.")
path = models.CharField(max_length=100,null=True, blank=True)
language = models.ForeignKey(Category)
albumid = models.CharField(max_length=100)
class Song(models.Model):
title = models.CharField(max_length=100)
artist = models.ManyToManyField(Artist)
music = models.ForeignKey(Music)
album = models.ForeignKey(Album)
file = models.FileField(upload_to=get_file_path)
Update: I tried instance.slug . Its not working. instance.slug does not exist in Song Model. Its only exists in Album model ( Want to use Album Slug field)
Update2: Here is model snapshot

Quite straightforward: str(instance.album.slug)

Yes, just use instance.slug instead of instance.id
Another example you can find in the answer of post Change filename before save file in Django
Update: If not all instances have a slug field than you could be interested in a solution like this:
def get_file_path(instance, filename):
fld = getattr(instance, 'slug', instance.id)
return os.path.join('files', str(fld), filename)

Related

Django - uploading file to folder named with id of the object

I was trying to make it this way but instead of {{product.id}}, folder is named None.
I read some articles about that, and I found out that is because folder is being made before whole object. So how should I do that to make it work?
models.py:
def getImageURL(self, filename):
return "products_images/" + str(self.pk) + "/product_image.png"
class Product(models.Model):
name = models.CharField(max_length=200)
image = models.ImageField(upload_to=getImageURL, default="media/static/products_images/default.png" )
changed my funtion to:
def getImageURL(instance, filename):
return "products_images/{}/product_image.png".format(instance.id)
But it works only while edditing existing object. When I'm trying to make a new one id is set to None.
Edit:
ok, finally I did it this way:
def getFileNumber():
queryset = Product.objects.all().order_by('pk')
last = queryset.last()
last_id = last.id
file_number = last_id+1
return str(file_number)
def getImageURL(instance, filename):
return "products_images/{}/product_image.png".format(getFileNumber())
class Product(models.Model):
name = models.CharField(max_length=200)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
availability = models.IntegerField()
price = models.DecimalField(max_digits=5, decimal_places=2)
Probably it is not the best way to do that but it's very simple to understand so why not to use it.
Some debugging:
def getFileNumber():
queryset = Product.objects.all().order_by('pk')
if queryset:
last = queryset.last()
last_id = last.id
file_number = last_id+1
return str(file_number)
else:
file_number=1
return str(file_number)
def getImageURL(instance, filename):
path = os.path.join("products_images/{}/product_image.png".format(getFileNumber()))
print(path)
if os.path.isfile("media/" + path):
print("image with this id already exist, ")
os.remove("media/" + path)
return path
else:
return path
class Product(models.Model):
name = models.CharField(max_length=200)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
availability = models.IntegerField()
price = models.DecimalField(max_digits=5, decimal_places=2)
The following is the issue:
The instance doesn't have a primary key yet before it is created
When the instance gets saved in the database, then you can get the primary key
Maybe use signals, somehow? Do the logic it in the view after saving it?
Alternative option to pk:
You can generate a UUID for the object.
id = models.UUIDField(
primary_key = True,
default = uuid.uuid4,
editable = False)
Or alternatively:
Have a look at this package, you can set the uuid alphabet, length, and some more. This would allow you access to the id, for sure (same with uuid).
https://pypi.org/project/shortuuid/
id = ShortUUIDField(
length=16,
max_length=40,
prefix="id_",
alphabet="abcdefg1234",
primary_key=True,
)
Sidenote:
def getImageURL(self, filename): #Self would normally be defined as instance, but I suppose this is just semantics.
This is because the id is saved to the model after calling save(). The first guess would be to use save() to get the object in return. But save() does not return anything. So I put together a little example
class Thingy(models.Model):
name = models.CharField(
_('name'),
max_length=64,
)
def save(self, *args, **kwargs):
super(Thingy, self).save(*args, **kwargs)
print(self.pk)
My test was:
>>> t = Thingy(name="lol")
>>> t.save()
1
And it printed the primary key since save() mutades self. You could check if the pk exists before save(). If yes, just add the image path. If not. Execute save() first, manipulate the image path using pk, and save the object again. It is not elegant in any way, but I guess that is the only way.

Displaying image from ForeignKey in Admin Panel in Django

I have in project two classes - Photo and Prescription inside models.py file which are related each other with foreign key. Here is part of the code:
class Photo(models.Model):
name = models.CharField(max_length=100,null=False)
photo = models.ImageField(upload_to="photos/",null=True)
def photo_tag(self):
return '<img src="/media/{0}">'.format(self.photo)
photo_tag.short_description = 'Photo of prescription'
photo_tag.allow_tags = True
class Prescription(models.Model):
description = models.CharField(max_length=100,null=True)
photo = models.ForeignKey(Photo, related_name='related_photo',null=True)
def __str__(self):
return self.description
And my Admin.py
class PhotoAdmin(admin.ModelAdmin):
list_display = ('name', 'photo_tag')
fields = ('name','photo','photo_tag')
readonly_fields = ('photo_tag',)
admin.site.register(Photo,PhotoAdmin)
class PrescriptionAdmin(admin.ModelAdmin):
list_display = ('get_photo')
fields = ('photo','description')
model = Prescription
def get_photo(self, obj):
return obj.photo.photo_tag
get_photo.short_description = 'Photo of prescription'
admin.site.register(Prescription,PrescriptionAdmin)
Question is, when I open prescriptions list instead of photo in the Photo of prescription field shows following message.
<bound method Photo.photo_tag of <Photo: Photo object>>
How real photo could be described there?
If you are going to render the image in html (which I guess you are), could you not then use ginger to display the description in a similar way to this?
<img src="{{photo.url}" title="{{photo.short_description}}" alt="{{photo.short_description}}">
Note that I included the alt for internet explorer which seems to use that instead of title.
There are few wrong issues with your approach. Your photo_tag method should be a property method, or eventually cached_property method. For displaying safely HTML code you should use the method format_html provided by Django.
Here is how I'd refactor your code:
models.py
from django.db import models
from django.utils.html import format_html
from django.utils.functional import cached_property
class Photo(models.Model):
name = models.CharField(max_length=100, null=False)
photo = models.ImageField(upload_to="photos/", null=True)
# better use blank=True instead of null=True for ImageField
#cached_property
def photo_tag(self):
if self.photo:
return format_html(
'<img src="{img}">',
img=self.photo.url
)
return None # or better return '' if you use blank=True
photo_tag.short_description = 'Photo of prescription'
class Prescription(models.Model):
description = models.CharField(max_length=100, null=True)
photo = models.ForeignKey(Photo, related_name='related_photo', null=True)
def __str__(self):
return self.description
Now you can use photo_tag as a property of Photo and bind the HTML safe in your admin.
Thanks anybody who tried to help. I figured out another solution with help of #Klaus who answered first and I am pretty sure that there is maybe much more better solutions also. Here are how I changed my code
Models.py
from django.utils.safestring import mark_safe
class Photo(models.Model):
name = models.CharField(max_length=100,null=False)
photo = models.ImageField(upload_to="photos/",null=True)
def photo_tag(self):
return mark_safe('<img src="/media/{0}">'.format(self.photo))
photo_tag.short_description = 'Photo of prescription'
photo_tag.allow_tags = True
class Prescription(models.Model):
description = models.CharField(max_length=100,null=True)
photo = models.ForeignKey(Photo, related_name='related_photo',null=True)
def __str__(self):
return self.description
Admin.py
class PhotoAdmin(admin.ModelAdmin):
list_display = ('name', 'photo_tag')
fields = ('name','photo','photo_tag')
readonly_fields = ('photo_tag',)
admin.site.register(Photo,PhotoAdmin)
class PrescriptionAdmin(admin.ModelAdmin):
list_display = ('get_photo')
fields = ('photo','description')
model = Prescription
def get_photo(self, obj):
return obj.photo.photo_tag()
get_photo.short_description = 'Photo of prescription'
admin.site.register(Prescription,PrescriptionAdmin)

Django Admin not Show Image from Imagefield

I want to show image like this Django admin listview Customize Column Name.
I've checked Django Admin Show Image from Imagefield, Django - Display ImageField, Django - How can I display a photo saved in ImageField?.
This is my models.py and admin.py.
#models.py
class Movie(models.Model):
id = models.IntegerField(primary_key=True)
master_id = models.IntegerField()
url = models.CharField(max_length=100)
cover = models.CharField(max_length=100)
tittle = models.CharField(max_length=50)
duration = models.CharField(max_length=10)
click = models.IntegerField()
platform_time = models.DateTimeField()
platform_type = models.IntegerField()
entry_date = models.DateTimeField()
enable = models.IntegerField()
def image_tag(self):
return u'<img src="%s" />' % self.cover
image_tag.short_description = 'Image'
image_tag.allow_tags = True
class Meta:
managed = False
db_table = 'movie'
#admin.py
class MovieAdmin(admin.ModelAdmin):
list_display = ('id', 'master_id', 'url', 'cover', 'tittle', 'duration', 'click', 'platform_time', 'platform_type', 'entry_date', 'enable')
search_fields = ('id', 'tittle',)
list_filter = ( 'enable', )
readonly_fields = ( 'image_tag', )
Now, this is a part of my interface, there is no image_tag field. Further more, If I modify cover field to image_tag, the cover field is still not image visible.
You have to use an ImageField for cover instead of CharField and also change the image_tag return line:
cover = models.ImageField(upload_to = "images") #use the directory you want here
def image_tag(self):
return u'<img src="%s" />' % self.cover.url
Also you have to add the image_tag field to list_display.
And make sure that django can find the upload_to directory.
You have no 'image_tag' column (which contains <img...>) in list_view.
I wrote valid answer about images in django-admin listview for django 2.0.6.
Here is link on my answer:
Django: Display image in admin interface
I hope it will help someone.

Django rest framework saving thumbnail image

I've recently started developing with django + python and everything was going very smooth until I got stuck into a problem that probably is very simple but I can not solve with my inexperience with the framework/language.
I'm receiving an JSON object through an HTTP Request which contains some data and 2 pictures. Prior to those 2 pictures I wanted to save a thumbnail from one of them but I don't see to get that task done. I can save all the data easily including the 2 images. But I can not see to find a way to generate a way an have that in the DB also, as well the folder structure that I want.
My folders should look like:
pictures
user
originals
processed
thumbnails
otherUser
originals
processed
thumbnails
My goal is: Receive 2 pictures, create a thumbnail from one of them and them save all 3 pictures in 3 separate folders and the path to the Database.
Here's how my model code looks like.
class SomeData(models.Model):
owner = models.ForeignKey('auth.User', related_name='canopeo_data')
adjustments = models.CharField(max_length=10)
latitude = GeopositionField()
longitude = GeopositionField()
notes = models.TextField(null=True, blank=True)
original_image = models.ImageField(upload_to=original_image, max_length=255, blank=True)
processed_image = models.ImageField(null=False, upload_to=processed_image, max_length=255)
thumbnail_image = models.ImageField(null=False, upload_to=thumbnail_image, max_length=255)
date_time = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('date_time',)
def save(self, *args, **kwargs):
super(SomeData, self).save(*args, **kwargs)
def original_image(self, filename):
url = "pictures/%s/originals/%s" % (self.owner.username, filename)
return url
def processed_image(self, filename):
url = "pictures/%s/processed/%s" % (self.owner.username, filename)
return url
def thumbnail_image(self, filename):
url = "pictures/%s/thumbnail/%s" % (self.owner.username, filename)
return url
Serializer code...
class SomeDataSerializer(serializers.HyperlinkedModelSerializer):
#url = serializers.HyperlinkedRelatedField(view_name='data', format='html')
owner = serializers.Field(source='owner.username')
thumbnail_image = serializers.Field(source='original_image')
class Meta:
model = SomeData
fields = ('url', 'adjustments', 'latitude', 'longitude', 'notes', 'original_image', 'processed_image',)
View code...
class SomeDataViewSet(viewsets.ModelViewSet):
queryset = SomeData.objects.all()
serializer_class = SomeDataSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def pre_save(self, obj):
obj.owner = self.request.user
I've tried many things such as easy_thumbnails, sorl_thumbnail, and some pre made methods on how to do it.. but I can't see to find a solution specific for what I've been wanting.
Thank you very much!
Looks like you have mistake in definition of the SomeDataSerializer. In model SomeData field original_image defined as ImageField, but in serialiser it's just Field, not ImageField. You should use correct field type:
class SomeDataSerializer(serializers.HyperlinkedModelSerializer):
#url = serializers.HyperlinkedRelatedField(view_name='data', format='html')
owner = serializers.Field(source='owner.username')
thumbnail_image = serializers.ImageField(source='original_image')
...

Getting Request in admin.py

I have a model like this:
class Item(models.Model):
code = models.CharField(max_length=200, unique=True)
barcode = models.CharField(max_length=300)
desc = models.CharField('Description',max_length=500)
display_desc = models.CharField('Description',max_length=500,
blank=True, null=True)
price = models.FloatField()
discountable = models.BooleanField(blank=True, default=False)
image_path = models.CharField(max_length=300,unique=True, blank=True, null=True)
def __unicode__(self):
return self.code + ' : ' + self.desc
But unfortunately, I don't want to store the item's image in the database, instead I want to store the image path in the server in the image_path column.
So, I created a custom admin.py for this object so that I could edit/insert the object thru the Django admin module. As a result, below is the customized admin.py
class ItemAdminForm(forms.ModelForm):
file_upload = forms.FileField(required=False)
class Meta:
model = Item
def __init__(self, *args, **kwargs):
super(ItemAdminForm, self).__init__(*args,**kwargs)
#if kwargs.has_key('instance'):
# instance = kwargs['instance']
# self.initial['file_upload'] = instance.file_upload
def handle_uploaded_file(f):
destination = open('D:/Project/pensel/penselshop/static/picture', 'wb+')
for chunk in f.chunks():
destination.write(chunk)
destination.close()
return f.name
def save(self,commit=True):
name = None
extension = None
#error here! How could I get the request?
miniform = ItemAdminForm(request.POST, request.FILES)
if miniform.is_valid():
name = handle_uploaded_file(request.FILES['file_upload'])
extension = name.split('.')[1]
model = super(ItemAdminForm, self).save(commit=False)
model.image_path = '/static/picture/' + model.code + extension
if commit:
model.save()
return model
However, during processing the save() function, I noticed that there is an error in getting the request. How can I get the request so that I could retrieve the file? I noticed that the request is automatically added in views.py, but not admin.py
Django's ImageField and FileField fields don't actually store the image in the database either. All that is stored in the database is the path, which you can control yourself. The actual image file is stored on the filesystem. So I'm not sure why you are going to all this trouble...?
But to answer your question of how to get the request in the admin, you can override ModelAdmin.save_model().

Categories

Resources