How to tell if user changed file in django ModelForm - django

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.

Related

Django Admin Page: Help Text for Model Methods?

I have a model method in Django that I am displaying on an admin page just like I would a model field. With a field, I can just add a help_text argument to it to give a description of what the field is and what the user should put into it. However, with a model method, help_text does not work. Adding the attribute short_description changes the way the method name is displayed, which is sort of okay, but I'm looking for a way to add a few sentences of description beneath the method value that is displayed. Is there any way to do this natively, or would I have to resort to overriding admin templates or something? (Which I do not think is worth it for something this minor).
You can do this using JS.
Replace ID-OF-THE-FIELD with the actual id of the desired field.
(function($) {
var myField = $('#ID-OF-THE-FIELD');
// find the id of the desired field by doing
// Right-Click > Inspect element
var help = $('<p class="help">A very long help text</p>');
help.insertAfter(myField);
})(django.jQuery);
Put this code into a JS file and supply this file using class Media of your ModelAdmin class.

Django ClearableFileInput - how to detect whether to delete the file

I'm using Django Crispy Forms for my form with an option to upload an image (ImageField in my Model)
The forms renders as I'd expect, with the checkbox to clear an existing file. However when processing the form submission the 'image-clear' checkbox always gives me a 'None' value.
image_clear = form.cleaned_data.get("image-clear")
print image_clear
In the HTML of the page I notice that the input checkbox doesn't have a value attribute, see:
<input id="image-clear_id" type="checkbox" name="image-clear">
So I wondered if this was the issue, but when I look at the rendering in the Django admin site, the corresponding HTML input field doesn't have a value either - yet it still identifies that the image should be removed.
I should note that if I upload a new image, then this works, it's only the case where I'm removing/clearing the image (and it works in Django admin pages, so assume that means my model definition is ok to allow no image to be attached to the model)
So... in my form processing, how do I detect whether or not the image should be removed or not?
I'm sure I'm missing something simple here - and any help much appreciated.
You shouldn't check the checkbox, but check the value of the file input field. If it is False, then you can delete the file. Otherwise it is the uploaded file. See: https://github.com/django/django/blob/339c01fb7552feb8df125ef7e5420dae04fd913f/django/forms/widgets.py#L434
# False signals to clear any existing value, as opposed to just None
return False
return upload
Let me add here my code that solved the problem - I decided to put this logic to ModelForm.clean():
class Document(models.Model):
upload = models.FileField(upload_to=document_name, blank=True)
class DocumentForm(ModelForm):
def clean(self):
cleaned_data = super(DocumentForm, self).clean()
upload = cleaned_data['upload']
if (upload == False) and self.instance.upload:
if os.path.isfile(self.instance.upload.path):
self.instance.upload.delete(False)

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.

Django Forms and Buttons

I have been working on forms only recently and I am still puzzeld by them.
What I want are standard Forms:
Next Button
Submit Data to Db
Timestamp
Clickable Images with Regions defined where when I click I get to the next page
And
I would like to combine these.
E.g. have a next button + Record the Timestamp.
or
E.g. Click into an Image + Next + Timestamp
If anybody could give me some examples for code that can achieve that or a good online resource on where to get info on that, that would be awesome.
Thanks for the time!!
I'm a little unclear about what you're trying to accomplish, but if you're trying to move data from an HTML form to the database, I'd suggest looking at how to use ModelForms. In a nutshell, you create a model class, like this:
class MyModel(models.Model):
field1 = models.CharField(max_length=50)
Then you create a ModelForm class that references that model:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
You can render an instance of MyModelForm in a view function. Inside of a POST request in that view, you bind the POST data to the form, validate it, and call save() on it to commit it to the database:
if request.method == 'POST':
form = MyModelForm(request.POST)
if form.is_valid():
model_instance = form.save()
This really isn't a question, I'm not exactly sure what you're trying to accomplish.
If you want to use Django forms, start here, or here.
I assume the stuff you mention about a timestamp should probably be an auto_now field in a model. Take a look at this.
The stuff you mention about buttons and click-able images is really just HTML and has nothing to do with Django. I would try Google for that.

Insert django form into template dynamically using javascript?

I want to add same django form instance on same template. i already add one before and other add dynamically using javascript.
for example 'form" is a django form: newcell.innerHTML = {{ form.firstname }};
The problem is that when i submit the form, in view the request object has only one value (that is not add using javascript). how can i get the values of other form elements values that is added dynamically runtime.
It is something like the "Attach Another File" feature in gmail, where the user is presented with a file upload field and new fields are added to the DOM on the fly as the user clicks to "Attach Another File" plus button
You could always try separating out your FileField into a FileModel.
Take a look at the following pseudo code (as in python based on memory--i've moved over to clojure for now).
models.py
class FileModel(models.Model):
file = models.FileField()
...
class ThingToWhichYoureAttaching(models.Model):
name = models.CharField()
attachments = models.ManyToManyField(FileModel)
...
forms.py
class FileForm(forms.ModelForm):
class Meta:
model=FileModel
class ThingForm(forms.ModelForm):
attachments = forms.MultipleChoiceField()#override the manytomany form field with style field of your choice.
class Meta:
model=ThingToWhichYoureAttaching
When they pop up the window with the PLUS button, show the FileForm but on the main page leave the ThingForm untouched. You can also have an initial FileField on the main page with the ThingForm for people who don't have javascript. Just make sure to process the FileForm BEFORE the ThingForm so that the File is available for the Thing.
When processing the popup form you can use AJAX (i recommend jquery) to submit the FileForm to the server, and return the markup to insert the File in the Attachments field.