I have existing Django-based project with some files uploaded.
I need to add a feature to automatically resize new uploaded files to some resolution (200x200). I found a nice library django-stdimage that does what I need.
But on upload it stores original file with its original resolution. And existing code works with the original file instead of resized one.
class Product(models.Model):
name = models.CharField(verbose_name=_('Name'), max_length=64)
image = StdImageField(upload_to='product_images/', verbose_name=_('Image'), blank=True, null=True,
variations={'default': (200, 200)})
I would like to save processed files by the same name as original file. I do not need original file by the way.
I do not want to change all the code where it works with image field - there are complex DRF serializers, some views, forms, templates, etc.
So I would like to get new resized image as before by using myproduct.image - in templates for example.
Is it possible to do without subclassing StdImageField ?
I opened a ticket on the StdImage's page and received an answer..
No, I'm sorry, but nether would this work, nor would I advice you to
do such a thing. It would add implicit behavior. That's something you
might want to avoid long term.
You can always overwrite the field tho and implement your own
behavior. The StdImage code base should be good guidance to implement
your own behavior.
Related
I've been working on Django project. Currently, user can upload only one image but now I want to change it so user can upload some images.
models.py is like this.
class Entry(models.Model):
photo = models.ImageField(...)
I am thinking of just adding photo2 and photo3 inside Entry model. But I'm wondering if there's a better way to do it. I don't want to delete images that are already uploaded. Anyone who could give me tips? Also, I don't like the file upload form's design and some people use just button-like form. I also want to know how to create button-like form.
Create new model called 'Photo'.
Create relationship from 'Entry' to 'Photo'.
If you want to use photos that related with Entry record, you need to use select_related() function
Also you can find button-like upload form here.
My system has products, with images associated with them, like so:
class Product(models.Model):
name = models.CharField(max_length=100)
...
class Image(models.Model):
product = models.ForeignKey(Product)
image = models.ImageField(upload_to='products')
So far so good. Naturally, the client wants to upload their products in bulk in a csv and upload a zip file containing the images. I format the csv as so:
product_name,image_1.jpg,image_2.jpg,...
product_2,image.jpg,...
So far I've made a model just as a helper:
class BulkUpload(models.Model):
csv = models.FileField(upload_to='tmp')
img_zip = models.FileField(upload_to='tmp')
The workflow goes something like this:
User uploads files via the django admin
Get the zip file contents and store for later
Extract the zip into tmp directory
Start a transaction. If anything unexpected happens from here we rollback
For each row in the csv
Create and save product with the name specified in the first column.
Grab image filenames from the other csv fields
Check the images are in the zip, otherwise rollback
Check the images don't already exist in the destination directory, otherwise rollback
Move the images to the destination directory and set the fk to the saved product object, rollback on any errors.
Commit the transaction
Delete the zip and csv, and delete the bulk upload object (or just don't save it)
If we roll back at any point we should somehow inform the user what went wrong.
My initial idea was to override save or use a post_save signal, but not having access to the request means I can neither use messages nor raise a validation error. Overriding model_save() in the admin has it's own problems, not being able to do any validation.
So now my thought is to change the ModelForm and give this to the django admin. I can override the clean() method, raise ValidationErrors and (presumably) run all my stuff in a transaction. But I'm struggling to figure out how I can access the files in such a way that I can use Python's ZipFile and csv libraries on them. It also feels a little dirty to do actual work in a form validation method, but I'm not sure where else I can do it.
I might have gone into too much detail, but I wanted to explain the solution so that alternative solutions can be suggested.
I don't think you should use a BulkUpload or any model representing this operation, at least if you plan on doing the process synchronously as you're currently suggesting. I would add an additional view to the admin area, either by hand or using a third party library, and there I would process the form and perform the workflow.
But anyways, given you already have your BulkUpload model, it's certainly easier to do it using a admin.ModelAdmin object. Your main concern seems to be where you should place the code of the transaction. As you've mentioned there are several alternatives. In my opinion, the best option would be to divide the process in two parts:
First, in your model's clean method you should check all the potential errors that may be produced by the user: images that already exist, missing images, duplicated products, etcetera. Here you should check that the uploaded files are OK, for instance using something like:
def clean(self):
if not zipfile.is_zipfile(self.img_zip.file):
raise ValidationError('Not a zip file')
After that, you know that any error that may arise from this point on will be produced by a system error: the bd failing, the HD not having enough space, etc. because all other possible errors should have been checked in the previous step. In your ModelAdmin.save_model method you should perform the rest of your workflow. You can inform the user of any errors using ModelAdmin.message_user.
As for the actual processing of the uploaded files, well, you named it: just use the zipfile and csv modules in the standard library. You should create a ZipFile object and extract it somewhere. Now, you should go over the data of your csv file using csv.reader. Something like this (not tested):
def save_model(self, request, obj, form, change):
# ...
with open('tmp/' + obj.img_zip.name, 'r') as csvfile:
productreader = csv.reader(csvfile)
for product_details in productreader:
p = Product(name=product_details[0])
p.save()
for image in product_details[1:]:
i = ImageField()
i.product = p
i.image = File(open('tmp/' + image)) # not tested
i.save()
After all this there would be no point in having a BulkUpload instance, so you should delete it. That's why I said in the beginning that that model is a little bit useless.
Obviously you'd need to add the code for the transactions and some other stuff, but I hope you get the general idea.
I figure the title is somewhat confusing but that's the best I came up with.
I have an upload image form which I want to process the image file further after the user upload it (with the user - cropping using jquery) rather than put it on the model imagefield right away.
Meaning, I need to save the image temporarily so I can present it to the user in a template with the jquery crop plugin.
My current solution is ugly, I made a model called TempImageToProcess which I save the image to and from that I re-present it to the user to further process.
So my question is: is there any elegant way to save image temporarily in Django?
You should check TemporaryFileUploadHandler in this section.
Look this SO question for a clean up afterwards if any unexpected error occur or validation errors.
Hope this will lead you some where.
Hi I have this type of model
class Post(models.Model):
title = models.CharField()
image = models.ImageField()
# other fields
For creating a new post I would a flow like this:
in the new post page 1 the user choose the photo and upload it
(submit)
in the new post page 2 the user can see the photo and fill other fields like 'title'
My question is how to upload a photo and display it in the next page, without creating a new Post object with the image field. Should I save the image in a temporany directory? In this case, how can I keep the reference and use it in the second page?
Thanks.
There are couple of solutions
Define another model to just hold images, and add may be OneToOneField in your Post model. On submitting page1, create instance of this image model and put it in page2 as image field.
With html5 file API, you can refer to local file in image field. So you can show image on page2 using local client side path w/o storing it at server. refer: View image selected from file-system on client-side before upload?
First read the relevant doc so you understant what happens with uploaded files:
https://docs.djangoproject.com/en/dev/topics/http/file-uploads/
The simplest solution IMHO would be to
1/ save the uploaded file in some temporary directory - just make sure you can serve the content one way or another (either directly thru the front webserver, or thru a custom view if you want more control on who can access this content),
2/ pass the temporary file path and url to the "next" view one way or another (hidden form fields, session...).
I use to have table to store images (any files in fact :)). Each image is linked to object (FK) and has status - Draft, Approved, Deleted. So I don't use temporary directory, just change status in table of images.
I'm trying to create my own field type for uploading images, it won't be used in the django admin panel. I imagine the syntax would probably look something like:
photo = CustomImageField(upload_to='images', width="200", height="200")
I want to be able to set the parameters in the field when I upload and save an image. I've looked through the django documentation but I can't find anything about how to do this. So basically, I want to be able to control where the image is uploaded to and the width and height parameters when the upload form is processed and the object is saved.
I am using Django 1.2
Any help would be greatly appreciated :)
You don't need a custom field for this. As the documentation shows, upload_to can be a callable, which is passed the instance and returns a path which determines where the field is saved.
I'm not sure what you mean about the width and height parameters. These are calculated from the image in any case. If you specify height_field and width_field in the field definition, the corresponding fields will be filled with the values of the image's height and width when the image is uploaded.
Your can change the storage of a FileField using the location property.
form_name.photo.storage.location = upload_dir_path
It works for the FileField and maybe it will also work for your custom image field if you inherit the Django's ImageField.
all info for uploading:
http://docs.djangoproject.com/en/dev/topics/files/
and info on resizing
resize image on save
i'll hope it helps
EDIT: all you need i think is: http://code.google.com/p/django-stdimage/
but i don't know if it works on current version.