I'd like my two image fields point to the same image.
At one time, I'll have one image field populated.
At later time, I'd like another imageField to point to the same file.
(If the first one changes, the second one gets updated as well)
The file is at amazon s3 if it matters.
I could use django-imagekit.
I would do it with a structure like this:
class Image(models.Model):
img = models.ImageField(upload_to = "images", related_name="+")
class A_model(models.Model):
image = models.ForeignKey(Image)
class B_model(models.Model):
image = models.ForeignKey(Image)
This avoids redundancies in the database, which you would have using two image fields. And access to the image is quite easy.
Note that you have to set the related to "+", because Django cannot reverse the relation in this case.
Related
I have a Django model with a customized image field. The image field creates some thumbnail sizes on upload. Code may look like this:
from django.db import models
from utils import CustomImageField
class Photo(models.Model):
image = CustomImageField()
Now I modify the original image, let's say I rotate it. And now I want to trigger the save method of my image field again, in order to overwrite the thumbnails and create rotated versions. So, I don't need to rotate the thumbnails elsewhere in my code (DRY).
Any thoughts? Something along those lines - but how exactly?
p = Photo.objects.get(pk=1)
p.image.save(...)
I have full control over the CustomImageField widget. The save() method is defined as:
def save(self, name, path, save=True):
Question is, what do I use for the methods parameters?
This question looks like a duplicate of Programmatically saving image to Django ImageField
The parameters of the ImageField.save() method are documented for FileField.save() (of which ImageField is a subclass):
https://docs.djangoproject.com/en/1.9/ref/models/fields/#django.db.models.fields.files.FieldFile.save
Takes two required arguments: name which is the name of the file, and
content which is an object containing the file’s contents. The
optional save argument controls whether or not the model instance is
saved after the file associated with this field has been altered.
Defaults to True.
Here is what is working for us:
class CustomImage(models.Model):
image = models.ImageField(upload_to=get_file_path, max_length=500)
orig_name = models.TextField()
This is the method that adds an image file to the ImageField from an http resource:
from django.core.files.base import ContentFile
def download_photo(amazon_id, url):
img_data = requests.get(url)
img = CustomImage(orig_name=img_data.url)
img.image.save(slugify(img.orig_name), ContentFile(img_data.content), save=True)
It also works without ContentFile:
new_img = File(open(different_obj.image.path), 'r')
img.image.save(different_obj.image.url, new_img, save=True)
See also:
- https://docs.djangoproject.com/en/1.9/topics/files/
- https://djangosnippets.org/snippets/2587/
An option is dirty field checking, either manually (see this SO question) or using a pypi package
Alternatively, if you want to conserve memory, the resizing can be triggered from the field's property setter (assuming you inherit from FileField
class CustomImageField(FileField):
def _set_file(self, file):
has_file_changed = file != self._file
super(CustomImageField, self)._set_file(file)
if has_file_changed:
self.handle_resizing_etc()
# need to redeclare property so it points to the right _set_file
file = property(FileField._get_file, _set_file, FileField._del_file)
disclaimer: I haven't used this approach in production code and I haven't written a proof of concept before posting this answer, so it might not work as desired
I have two models Post and Item that hope to have their images field, which refer to instances of a model Image:
class Image(models.Model):
image = models.ImageField(upload_to='images')
The first approach I can think of is to add a TextField to both Post and Item, and store the image_instance.image.urls of Image instances, so each time I want to display all images of an item or post, I obtain the item.images and split the string into an array, and all the urls are there.
The second approach is to add post and item field to the Image model as nullable foreign key field. So each time I want to display all images of a post or item, I do something like Image.objects.filter(item=some_item_instance), and extract .image.url.
I wonder which approach is better in practice, Or there are other better approaches?
Just use a ManyToManyField to store the relationship between a Post (or Item) and an Image and then iterate across that. Have models.py like so:
class Image(models.Model):
image = models.ImageField(upload_to='images')
class Post(models.Model):
body = models.TextField()
images = models.ManyToManyField(Image)
And elsewhere, pull the set of images from a Post instance and iterate across that:
my_post = Post.objects.first()
for image in my_post.images:
print image.url
I have a userprofile of the form
class profile():
#the next line is just an abstract
profile_images='volumes/media/root/userprofile/profile_images/'
image=models.ImageField(upload_to=profile_images)
in the directory "profile_images" there are the last 5 files the user uploaded as profile images, ie:
image_1
image_2
image_3
image_4
image_5
lets say the current profile.image is image_1. now i want to allow the user to select one of the previous images. the function i wrote to change the image to the one i received from the form looks like that:
def change_profile_image(userprofile,path_to_new_image):
f = open(path_to_new_image, 'r')
userprofile.image = ImageFile(f)
userprofile.save()
as an example the user selects image_3, and after execution of that code the forementioned directory looks like that:
image_1
image_2
image_3
image_4
image_5
volumes/media/root/userprofile/profile_images/image_3
which, of course, is not what i wanted. what i want is to just change the file associated with the ImageField of my profile instance, without Django copying any files.
any ideas how to solve that?
ok, actually it's as easy as
userprofile.image=path_to_new_image
no need to worry with opening files, deleting and rewriting them.
Theoretically, you could overwrite userprofile.image.path, but it’s not too obvious how to do that.
Here is some more information.
Programmatically saving image to Django ImageField
Django: How to replace/overwrite/update/change a file of FileField?
How can I replace/override an uploaded file?
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.
Foo (models.Model):
slug = models.SlugField(unique=True)
image = models.ImageField(upload_to='uploads/')
I want to do two things with this:
First of all, I want my image to be forced to resize to a specific width and height after the upload.
I have tried this reading the documentation but seems to getting error:
image = models.ImageField(upload_to='uploads/', height_field=258, width_field=425)
Secondly, when adding an item via admin panel, I want my image's file name to be renamed as same as slug, if any issue arises (like if such named image already exists, add "_" to the end as it used to do.
IE: My slug is i-love-you-guys , uploaded image such have i-love-you-guys.png at the end.
Signals are the answer to both of your questions.
As stated in the docs, the height_field and width_field are names of fields where to store the original image's height and width -- not to coerce the image to a certain size. I think the best way to handle what you want to do is to use either the pre_save or post_save signal to resize the image using PIL. You may also want to check out sorl. In fact, you can use the sorl's thumbnail in your signal to create the new image.
To rename the filename, also use a signal.