How to bind an image to an edit form in Django? - django

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.

Related

How to tell if user changed file in django ModelForm

I have a django ModelForm with an image field. I want to resize the image when the user submits the form, but only if they uploaded a new image. My code looks like this:
class EditProfileForm(forms.ModelForm):
class Meta:
model = Person
fields = ['picture', '...']
def clean_picture(self):
picture = self.cleaned_data['picture']
if picture.file: #This isn't right though
#resize it
return picture
However it seems like picture.file always exists if the model being edited contains a file. I know I can check request.FILES back in the view, but that's very inelegant. Is there a better way?
In general, you can do this with self.changed_data, which returns a list of the names of the fields where the value changed. Whether there's anything special about a FileField that would interfere I don't know offhand.
In js,
In JS, set a global variable file_changed = 0.
HTML input tag < input ng-model="avatar" type="file" onchange="ChangeFile()">
Lets say ChangeFile() will provide a small preview of the file to be uploaded
In function ChangeFile set file_changed=1 once the file is selected.

Customize ImageField url/path

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

Use local image on django form

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.

replace the file associated with an imagefield without having django copying it

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?

How to force order of pictures in a gallery

I'm building a django app that has an image gallery, and the client insists the images be displayed in specific order. I use the admin interface to upload the images and edit their properties, and I have an ImageFile class in my model that basically looks like this:
class ImageFile(models.Model):
"""represents an image file"""
# the image description
description = models.CharField(max_length=45)
# the actual image
image = models.ImageFile(upload_to='images')
# running number representing the order on the page
order = models.IntegerField()
def __unicode__(self):
return "%s" % (self.description)
class Meta:
db_table = 'images'
I'm using the IntegerField 'order' to have running number that'll control the sorting. I figured there has to be a smarter/better way to do this (another model?) and also be able to easily control it through the admin interface.
I suppouse you would like give possibility to sort images to user, (anyway if you want sort it via time add, best way is order it by id), so, if there is model like Gallery (of images) maybe you should store tuple of ids of images from the galery (in DB as a text object). After read cast it to tuple, and you have expected order. Hope I help.
if the order of the images is the order that they are uploaded you could use a timestamp to order them,.
I used the same method (with integer "order" field in the model) to define the ordering. However, I customized the admin to allow drag and drop the images belong to an album to define the order. When the admin hits "save" button, the order will be calculated automatically based on the current order after drag-and-drop. All data will be submitted to the server for saving to DB.