Repeat fields in form - django

I'm writing an application where users can upload a maximum of 3 images to the application and was wondering if it was possible to have the same field repeated. My model looks like this:
class Image(models.Model):
emailAddress = models.EmailField(max_length=75)
image = ImageField(upload_to='photos')
caption = models.CharField(max_length=100)
Where when the image changes only the caption and image change, so only one email field would be needed. I have a form that uses my model to create the form. Would I have to create a static form then read all the data in from the HTTP request?

Related

Is there a way to enable selecting multiple files on a single file upload in Django Admin so that upon saving multiple records are created?

I have a case where users upload files and each file has certain attributes. Often times there could be 10 files that will need to be uploaded into the database with the same attributes. To save time, it would be nice to allow the user to select all ten files and have 10 records added in the database, one record per file. My models are similar to the example below:
class ContentCategory(models.Model):
name = models.CharField(max_length=100)
class Document(models.Model):
file_name = models.CharField(max_length=100, blank=True)
note = models.TextField(null=True, Blank=True)
content_category = models.ForeignKey(ContentCategory, on_delete=models.PROTECT)
document = models.FileUpload(upload_to=f'{content_category}/')
def save(self):
self.file_name = os.path.basename(self.document.name)
super(Document, self).save()
My admin.py is simple, like the below code:
class DocumentAdmin(admin.ModelAdmin):
exclude = ('file_name',)
admin.site.register(Document, DocumentAdmin)
admin.site.register(ContentCategory)
So here is an exact scenario that happens often. 10 photos will need to be uploaded and all of them get the same content category and note. Is there a way to setup the admin to allow someone to select the proper content category and write out the note and then select all 10 files to upload and have 10 records created in the document table upon save? One for each photo?
You can override your admin form, and then do all your custom logic there.
Django custom model form
You could create a form with 10 fields for the files and 1 content category, and then handle it all via the form.
Or, you could also create a formset as well.
I would create a form with the content category as 1 field, and a nested formset in it for all the file fields. The user can add as a many files as they need, and when they upload, take the content category they selected and apply it to each file in the formset by overridding the formset clean method.

Django + MySQL: performance on storing multiple foreign key

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

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.

Django ModelForm Validation

Trying to solve an interesting problem right now.
I have a Django model with an image field that's not required, but is set to a default value when a new model instance is created.
class Product(models.Model):
image = models.ImageField(upload_to='/image/directory/', default='/default/image/path/', blank=True)
I also have a ModelForm based on that model, that includes the image field, and that has some custom validation.
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ('image',)
def clean_image(self):
image = self.cleaned_data.get('image', False)
if image:
# validate image
return None
The problem is that per the docs, calling is_valid() on a ModelForm triggers model validation in addition to form validation, so when a user submits the model form without an image, my custom form validation code attempts to validate the default model image, rather than just doing nothing as it's supposed to.
How do I get it to not do anything unless the form itself has a value for the image field?
Just solved it in pretty simple way. Adding the answer here in case it's helpful to anyone else.
The Django docs state that
...a model form instance bound to a model object will contain a self.instance attribute that gives model form methods access to that specific model instance.
So rather than check if the ModelForm has an image value, I just check whether the image value has changed from the saved instance. The form validation now looks like this:
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ('image',)
def clean_image(self):
image = self.cleaned_data.get('image', False)
if not self.instance.image == image:
# validate image
return None
Problem solved!

How to bind an image to an edit form in 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.