I have a system where users can upload cover photos to playlists. The playlist model is as following:
class Playlist(models.Model):
.
.
image = models.ImageField(upload_to='media/playlistimages')
.
.
The upload mechanism is as follows: Users chooses a photo, I upload it to a temporary model, and show it to the user. If the user chooses to save the photo, I proceed and save the image. Preview images are kept in a different model
class PreviewImage(models.Model):
.
.
image = models.ImageField(upload_to='media/previewimages')
.
.
Not to upload the image twice, when the user first chooses the photo, I upload it and save in the preview image model. Then, if the user proceeds to save the playlist, I only send the preview image id to the server and save the image in that object to the playlist as follows:
playlist.image = previewImage.image
playlist.save()
The problem is, the image is at first uploaded to the previewimages folder, and when I save the playlist's image as in the example above, it is still in that folder. How can I move this file to playlistimages folder while saving?
For doing what you want, you can overwrite the PlayList form's 'save' method. Something like:
def save(self, commit=True):
temp_file_id = self.cleaned_data.get('preview_image_id', False)
if temp_file_id:
try:
temp_file = PreviewImage.objects.get(pk=temp_file_id)
instance = super(PlayListForm, self).save(commit)
instance.image.save(
os.path.basename(temp_file.file.path),
temp_file.file.file,
commit
)
# If you want to erase the file from its previous location,
# as well as the PreviewImage object do the following
os.remove(temp_file.file.path)
temp_file.delete()
# Finally return saved instance
return instance
except PreviewImage.DoesNotExist:
# handle this the way it fits your needs...
except Exception as e:
raise e;
else:
# handle this the way it fits your needs...
This code is untested, it is only meant as a general idea.
Hope this helps you.
Related
I have a profile app, where users can upload a profile picture. I save the picture with the user's username eg. ifakih.jpg. If that file already exists, and they want to change their profile picture, I delete the old one and replace it with the new one. I can see the change in my directory. The old ifakih.jpg is replaced with the new one. However, my website still uses the old image. If I go to the admin and check the imagefield for that user, it points to the correct directory and image, but the content is wrong.
Models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=64,blank=True)
profilePic = models.ImageField(blank=True, null=True, upload_to= path_and_rename)
phoneNumber = models.CharField(max_length=12,blank=True)
streetAddress = models.CharField(max_length=64,blank=True)
#receiver(pre_delete, sender=Profile)
def post_delete(sender, instance, **kwargs):
"""
Deleting the specific image of a Post after delete it
"""
if instance.profilePic:
if os.path.isfile(instance.profilePic.path):
os.remove(instance.profilePic.path)
#receiver(pre_save, sender=Profile)
def post_update(sender, instance, **kwargs):
"""
Replacing the specific image of a Post after update
"""
if not instance.pk:
return False
if sender.objects.get(pk=instance.pk).profilePic:
old_image = sender.objects.get(pk=instance.pk).profilePic
new_image = instance.profilePic
if not old_image == new_image:
if os.path.isfile(old_image.path):
os.remove(old_image.path)
else:
return False
This sounds like a problem that comes from the browser caching images.
To see whether it is related to that, try reloading with STRG + F5 to ignore cached files (you can disable the browser cache in the network tab).
You could solve that problem in two ways:
1) You disable caching for your profile pictures so that the browser always loads the image and does not cache at all. This leads to more traffic on your server and also a longer load time of your site. (NOT recommended)
2)You change the file saving behaviour and allow different filenames. This way the browser will load the image for the first request and then can use it's cache. An ImageField will also generate you a unique name while saving. (Preferred)
I have a Page model that basically describes an HTML page. Pages are then served with URLs, such as http://www.mysite.com/page/1234/ for the page of id (pk) 1234.
I want to be able to add or attach images to my page. Therefore, I would like to use an Image class with a foreign key to a Page object:
class Page(models.Model):
title = ...
content = ...
class Image(models.Model):
page = models.ForeignKey(Page)
image = models.ImageField(...)
Here is my problem: I would like to deliver images to the client with urls of the form:
http://www.mysite.com/images/1234/image_name.jpg, i.e a URL that includes the page id. Also on the server, the paths should reflect the page structure: /path/to/media/images/1234/image_name.jpg
I don't know how to tackle this problem. On one hand, I would like to keep the features of an ImageField related to path formatting. For example when uploading two images with the same name, Django creates two paths ending with "image.jpg" and "image_2.jpg" or so to make the difference between both images.
On the other hand, the upload_to option has limited capability, and I don't know how to insert the page id in the path. Especially, some tricky cases such as uploading an image at the same time a page is created (using the same form), which means a page id should be generated before uploading the image.
Is it reasonably easy to make an image model that would behave as described above? If so, how do I have to modify the Image model to insert the page id in the image path?
I have seen the question Customize save path for ImageField, but it does not address the problem of primary key that might not be assigned.
Thanks.
Actually, a simple function passed as an upload_to parameter works, and there is no problem of non-existing id. So I guess that Django's default behaviour saves the image after saving the parent page model, as I wanted to.
In the Image model:
image = models.ImageField(upload_to=get_image_path)
with the following function:
def get_image_path(instance, filename):
return 'pics/' + str(instance.page.id) + '/' + filename
I wrote an image upload form which handles file and URL uploads. This is a model form which commits to the database an unpublished image.
class UnpublishedImage(Model(20)):
picture = ImageField(help_text=_('Unpublished picture'), upload_to=settings.UNPUBLISHED_PICTURE_UPLOAD_TO)
That Model(20) gives each model a pid field (20 random slug chars), which I use as a public ID for my models.
The unpublished images work just fine, but now I want to use them, that is, I want to publish them.
class Look(Model(20)):
user = OneToOneField(get_user_model(), editable=False)
title = CharField(max_length=40)
description = TextField(max_length=160)
#image = OneToOneField(UnpublishedImage)
To make this clear, it works in two steps:
The user uploads an image (unpublished)
The next form shows the image and allows to add a description, title, etc.
Now, I can't use OneToOneField to the UnpublishedImage model because I want to delete it from the unpublished table (and storage) as soon as the image is published. How do you recommend doing this?
I have tried ImageField, but I don't know how to give the image to the form.
Note that there's a third form for retrieving unpublished images, so you can use that.
class UnpublishedImagePublishForm(Form):
image = CharField()
def clean_image(self):
# Get the unpublished object
try:
unpublished = UnpublishedImage.objects.get(pid=self.cleaned_data['image'])
except UnpublishedImage.DoesNotExist:
raise ValidationError(_("Image not found"))
return unpublished
This way I can get the image:
# Get the unpublished image
unpublished = UnpublishedImagePublishForm(request.DATA)
if not unpublished.is_valid():
return Response(unpublished.errors, status=HTTP_422_UNPROCESSABLE_ENTITY)
# Get the unpublished image instance
image = unpublished.cleaned_data['image']
Thanks in advance.
I have the following Model:
class Listing(models.Model):
name = models.CharField(max_length=50, verbose_name="Title")
images = models.ManyToManyField('Image')
, with the ManyToManyField linking to this Image class:
class Image(models.Model):
thumb = ImageField(upload_to='images/uploads/')
number = models.PositiveSmallIntegerField()
and a corresponding ModelForm like so:
class ListingEditForm(ModelForm):
image1 = ImageField(required=False, label="Photo 1")
image2 = ImageField(required=False, label="Photo 2")
image3 = ImageField(required=False, label="Photo 3")
class Meta:
model = Listing
exclude = ('images')
The idea is to not limit the number of images that can be associated with a Listing in the backend, but at this time I only need 3 images in the form. Uploading the images works fine, but how would you go about binding the form to a Listing instance so that the images are not 'None' when one views the edit form?
Obviously, this alone won't work, because image1, image2 and image3 are only form fields, and not part of the model:
form = forms.ListingEditForm(instance=listing)
So adding a dictionary as the first parameter seems like the obvious thing to do:
form = forms.ListingEditForm({'image1': ...},instance=listing)
but what should the value of that ... be? And how do I retrieve it from the Listing instance?
I'll answer my own question, even though it's not quite the answer I was looking for. I've looked around, and as far as I know, there is no reliable way in HTML to change the contents of a File input field. So, I could be wrong, but even if you send that information with the request, Django will have no way of showing the information in the field (since it doesn't correspond to a file on the local PC).
So, my solution is simply to send the urls of the images with the request, as one normally would:
return render_to_response('edit.html', {'image1': image1_url, ...})
Then, if this information is present, I use jQuery to place the images next to the file input field in the template, and update it if the user selects a new file. It's not the best, but it works.
I'll still be glad to hear any other solutions.
I would use foreign key relation in Image, and inlineformset_factory for generating the form.
ListingEditForm = inlineformset_factory(Listing, Image, max_num=3, extra=0)
I would also add image name field in Image model. That way user will have indication of uploaded files in form display, and he will also be able to delete images if he whishes so. If you need unlimited uploads you can simply change max_num to 0 and extra to 1.
Of course that way you cannot associate one image with more then one Listing object, but if you need user to be able to delete images that is not recommended anyway.
I'm trying to create some kind of 'media manager' model which will allow the user to upload different kings of media (images, swfs, pdfs) similar to the way WordPress does. My media model looks something like this:
class Media(models.Model):
id = models.AutoField(primary_key=True)
url = models.FileField(upload_to="uploads")
mimetype = models.CharField(max_length=64, editable=False)
created = models.DateTimeField(auto_now_add=True, editable=False)
When a user uploads a file, I want to first determine what kind of file it is and if it's an image, manipulate it further. I want to be able to to specify the dimensions (crop) of the uploaded image via a view, so when I call the .save() method, the model will resize and crop the image, upload it and populate the database with the url to the file.
I also want to ensure that the upload of the image is done AFTER the post processing (cropping etc), I have no need to keep the original file.
So the question I am asking is how do I got about passing parameters to the FileFields save method (so I can pass dynamic properties for image post processing) and how can I ensure the post processing is done BEFORE the image is uploaded?
Edit: When I say before the image is uploaded, I mean before it's saved to it's final destination. I understand the image has to go int othe tmp folder first before I can post process it. Sorry for the misleading question.
Hope someone can help :)
You cannot do anything before the image is uploaded (because you have nothing to work with).
But if you want modify the image before saving it into db, you can do it in model's save() method, before calling parent's save()
If you are uploading it via admin, override method save_model() in admin.py, ie:
def save_model(self, request, obj, form, change):
file = request.FILES.get('url') # name of field
if file:
# proceed your code
return super(AdminClassName, self).save_model(request, obj, form, change)
Here is my code how to change file before actually upload it. I think you should get my idea
from django.core.files.uploadedfile import InMemoryUploadedFile
#....
#some form
def clean_avatar(self):
av = self.cleaned_data['avatar']
resized = make_avatar(av,65) # My custom function than returns image
return InMemoryUploadedFile(resized, av.field_name, av.name, av.content_type, resized.len, av.charset)
You can read django code for InMemoryUploadedFile "documentation".
And in your resize/crop function you should use StringIO not file to save result
How could the processing be done before the image is uploaded? That doesn't make sense. The server doesn't have any access to the file until you upload it.
If you actually want to handle the file before it's saved, you can write a custom upload handler. You can test there whether the file is an image, then crop it appropriately, before saving. (You'll need the Python Imaging Library for both of those tasks.)