I am working on a Django app and am having some trouble with the models/forms/templates required to upload images. I have a post and I want there to be a One-to-Many relationship with posts and images. i.e. A post can have any number of images. My first question is - what would the model look like? I am guessing something like:
class Image(models.Model):
img = models.ImageField(upload_to="photos/",null=True, blank=True)
posting = models.ForeignKey(Posting, related_name="images")
class Posting(models.Model):
...
Is this correct? And my other question is how should I upload multiple images? And I can't figure out what the form should look like. If I want to set a max number of images, could I just go like:
class ImageForm(forms.ModelForm):
img1 = forms.ImageField()
img2 = forms.ImageField()
class Meta:
model = Image
fields = ('img1','img2',)
But then I have no idea how to look that up to the view correctly. I am super lost, any help would be greatly appreciated!
For this task you should use the inline formsets. Read this docs first for basic understanding of the formsets.
The models in your question are almost valid. Remove the null=True, blank=True arguments from the img field - there is no sense in the Image instances without image itself.
Related
I am already using django-cleanup. But it works when imagefield was deleted.
If imagefield is realted to a model like below.
As long as I don't delete imagefield manually, It can't be working.
from django.db import models
class Listing(models.Model):
title = models.CharField(max_length=25)
class ListingImages(models.Model):
listing = models.ForeignKey(Listing, default=None)
image = models.ImageField()
Listing can have many images which isn't fixed quantities. So I implemented like that.
But now I have a problem with how I can find which image should be deleted.
Lazy algorithm is just iterate every image data when post or put request.
and delete not matched image which is related to Listing object.
But it's too expensive I think.
Is there any good solution? I was searching that 'delete foreign key image in django' this keyword. But there is no solution for me.
You can use your primary key id of ListingImages model with DELETE method to remove it. Django provides handy CRUD operations.
What you mentioned in your comment was correct. Proceed with pk it should work.
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 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 building a site for a client that needs to support image uploads (an artist) through the admin interface. Since most of the images are pretty high-res, I wanted to create thumb copies of the image to display on the gallery page after the upload. The upload works great with the forms.ImageFile element, but I was looking for some ideas on how to do the actual resizing and and linking between the thumb and the true size images. I had an idea to hold model class for both an image and an image thumb:
from django.db import models
class Image(models.Model):
"""a true size image"""
image = models.ImageFile(upload_to="images")
desc = models.CharField(max_length=256)
class Meta:
db_table = "images"
class ImageThumb(models.Model):
""""a thumbnail of an actual image"""
real_image = models.ForeignKey('Image')
image = models.ImageField(upload_to="images/thumbs")
class Meta:
db_table = "thumbs"
That part I'm stuck on is how to resize the real image after upload (pil? how?), and I could probably use some polishing on my models - any help will be great. Thanks.
There's a great plugin called sorl-thumbnail that deals with thumbnail generation - don't bother doing it yourself. sorl-thumbnail is very configurable, so the chances are it'll do anything you want it to do.
If that doesn't work for you, then photologue is also very good (photologue is more tailored towards managing photo albums, rather than just plain thumbnail generation).
See also easy-thumbnails and aino-convert. They might be a good bet since sorl-thumbnail might not be developed very actively from now on.
A have 3 models: Project, Image and Video with ManyToManyField relation:
class Project(models.Model):
images = models.ManyToManyField('Image', through='Project_Images')
video = models.ManyToManyField('Video', through='Project_Video')
class Image(models.Model):
original = models.ImageField()
projects = models.ManyToManyField('Project', through='Project_Images')
class Video(models.Model):
projects = models.ManyToManyField('Project', through='Project_Video')
I configure project's admin form with inline forms of Images and Videos linked to current project:
class ProjectAdmin(admin.ModelAdmin):
inlines = [VideoInline, ImagesInline]
class ImagesInline(admin.TabularInline):
model = Project_Images
raw_id_fields = ['project','image']
class VideoInline(admin.TabularInline):
model = Project_Video
raw_id_fields = ['project','video']
But inline table with simple select field and delete checkbox is much miserable for me, and I want to show here previews of images or video (youtube). I solve this for images with help of AdminImageWidget:
class ImageForm(forms.ModelForm):
class Meta:
model = Image
preview = forms.ImageField(widget=AdminImageWidget())
def __init__(self, *args, **kwargs):
super(ImageForm, self).__init__(*args, **kwargs)
try:
image = Image.objects.get(id=self.instance.image_id)
self.fields["preview"].initial = image.original
except:
pass
class ImagesInline(admin.TabularInline):
.....
form = ImageForm
Is it best way to do this? In my case I don't need file upload input, only image-preview in inline form table. I need also preview for youtube video, should I write my own widget for displaying video and apply it to some fake field ?
It's strange to solve this issue by widget for unnecessary fake field. Or is it normal way?
Any help will be very much appreciated!
Thanks!
You should create a widget similar to AdminImageWidget but that displays only the image, not the upload box. To apply that widget you don't need a custom Form class or a fake field, just use formfield_overrides on your ImageInline:
class ImageInline(admin.TabularInline):
...
formfield_overrides = { models.ImageField: {'widget': YourPreviewWidget}}
EDIT: Oops, didn't fully process the ManyToManyField issue - you're displaying inlines for the "through" table, not the table with the actual ImageFields. Given that, what you're doing now may be not such a bad solution. The alternative I can think of would be to write a specialized replacement widget for the Select, that knows how to display both the select box and a preview image for the currently selected Image object. That way you could avoid the need for the fake extra field.