Django image field default static file - django

I was wondering if there is a way to use an static image file as a default for an ImageField?
I'm asking this because if you write something like this because first, I'm not able to upload media folder to my github repository because I store in there the user uploads, and the default file always be the same, so I want to serve the default from static but the user uploads from media directory.
If you use this:
image = models.ImageField(upload_to=/upload/folder/, default=/img/default.jpg, blank=True, null=True)
It will try to load the default image from the media directory.

UPDATE: This answer no longer works. Since version 1.9, Django removes the leading / from the file path to convert it to a relative path and then appends /media/ to the path.
This change was made due to this ticket: https://code.djangoproject.com/ticket/25905
Alternative: I like this answer as an alternative.
Original answer:
First, look at the these two paths:
/img/default.jpg - Absolute Path (starts with slash)
img/default.jpg - Relative Path (doesn't start with slash)
Now, if your default path is absolute, then Django will not convert the url to /media/.../. Django only converts the relative paths.
So, if you want to serve the default file from static folder, you can just set an absolute path to your file like - /static/img/default.jpg.
Extra:
You can even verify this. Django uses urllib.parse.urljoin to create the url. Try this:
>>> media_url = '/media/'
>>> abs_url = '/img/default.jpg' # absolute url
>>> rel_url = 'img/default.jpg' # relative url
>>> from urllib.parse import urljoin # in Python 2: from urlparse import urljoin
>>> urljoin(media_url, abs_url) # test with absolute url
'/img/default.jpg'
>>> urljoin(media_url, rel_url) # test with relative url
'/media/img/default.jpg'

You can define a method that points to the default image as;
def upload_place_pics(instance, filename):
return "place_pics/{user}/{filename}".format(user=instance.user, filename=filename)
def default_place_pics():
return "place_pics/default_pic.jpg"
class Place(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)
name = models.CharField(max_length=300)
image = models.ImageField(default=default_place_pics, upload_to=upload_place_pics, null=True)
For more details on how to configure static files see the documentation

Related

Import files into a collection from CLI (django command)

I'm trying to build a django command to upload files and create associated pages for them.
My docs are PDF files, and my problem is to automatically "upload" those files into the right target "media" directory, without explicit copying them with my command script from the 'docs repository' to the MEDIA_ROOT defined directory.
I've tryed to use:
Code
f = File(open(file_path, 'r'))
# models.OfficeDocument is an inheritor of BaseDocument class
new_document, created = models.OfficeDocument.objects.get_or_create(title=title,
collection=collection,
file=f)
Error
SuspiciousFileOperation: The joined path (<my_local_path>) is located outside of the base path component (<MEDIA_ROOT path>)
but wagtail says me I'm not in the right directory (not in MEDIA_ROOT)
How can I do that?
You are trying to save the documents with their files in the current location, without copying them to the new location first. You could add some code to copy the files to the right location, or it seems reasonable to mimic what the Wagtail documents add view does here—instantiate a modelform, clean it, and call its save method. That will handle the saving of the document as it has the upload_to property configured on the file field.
Try:
from wagtail.wagtailcore.models import get_root_collection_id
collection = get_root_collecion_id()
user = some_user_you_will_attribute_these_to
doc = Document(uploaded_by_user=user)
upload_dict = {
'title': some_title,
'file': f,
'collection': collection,
'tags': '',
}
form = DocumentForm(upload_dict, f, instance=doc, user=request.user)
if form.is_valid():
form.save()
You might find that the file object needs to be something like a wrapped Django UploadedFile instance instead, see https://docs.djangoproject.com/en/1.11/ref/files/uploads/

How to upload image to custom location using models.ImageField and attribute?

I am an absolute beginner with Django so here is my question:
I created a model and I have image = models.ImageField(). When I use the admin interface, every uploaded image is placed in the root directory.
I read through this https://docs.djangoproject.com/en/dev/topics/files/ and if I use the below example it still doesn't work.
from django.core.files.storage import FileSystemStorage
fs = FileSystemStorage(location='/static/images/gallery')
class Car(models.Model):
...
photo = models.ImageField(storage=fs)
The new entry is correctly added but when I click the image name, the following error is displayed, and the image is not placed in /static/images/gallery
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/admin/Library/book/12/change/002_user_experience_remastered.jpg/change/
Raised by: django.contrib.admin.options.change_view
book object with primary key '12/change/002_user_experience_remastered.jpg' does not exist.
You're seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.
My code as I wrote it:
from django.core.files.storage import FileSystemStorage
fs = FileSystemStorage(location='/static/images/gallery')
class book(models.Model):
name = models.CharField(max_length=128, unique=True)
authors = models.CharField(max_length=128)
language = models.CharField(max_length=128)
otherDetails = models.URLField()
availableCopies = models.IntegerField()
borrowUntilDate = models.DateField()
image = models.ImageField(storage=fs)
commentName = models.CharField(max_length=128)
commentText = models.CharField(max_length=2048)
slug = models.SlugField(unique=True)
My project has the following configuration:
..\Services
Services
Library (my app)
static
templates
venvs
From the admin interface I load pics from C:\pics and I want them to be stored in ..\Services\Library\static\images\gallery folder.
What am I doing wrong? What am I missing?
Thank you!
From the documentations:
During development, you can serve user-uploaded media files from MEDIA_ROOT using the django.contrib.staticfiles.views.serve() view.
This is not suitable for production use! For some common deployment strategies, see Deploying static files.
For example, if your MEDIA_URL is defined as /media/, you can do this by adding the following snippet to your urls.py:
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
# ... the rest of your URLconf goes here ...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
You need to specify the absolute path of your folder in the FileSystemStorage location and also provide a base_url if you have not set MEDIA_URL in settings.
fs = FileSystemStorage(
location='C:/.../Services/Library/static/images/gallery',
base_url='/gallery/'
)
References:
FileSystemStorage

Django reupload images when changed upload_to

I have changed upload_to attribute of my models image field. How can I re-upload all images to new paths?
So, I think 're-uploading' is the wrong way to think about it -- re-uploading the images will still leave the old ones lying around, which (depending on how many images you have) could be a massive waste of space. One way to do this instead would be by the following two step process:
1) Move the files manually, on your server, to the new upload_to location via whatever method is OS appropriate. This could probably all be done with one mv command on linux, if that's what you're hosting on.
2) If you just changed the upload_to attribute, and didn't change the MEDIA_ROOT settings or anything else, what you need to change is the Imagefield's name property. An ImageField's name properly usually is a joining of your upload_to string and your image's filename (this then gets appended to MEDIA_URL to form the images url or MEDIA_ROOT to form the actual upload path). So you could update the models in your Django shell by typing something like this:
import os
from my_app import MyModel
newpath = 'your/new/upload_to/'
for obj in MyModel.objects.all():
image_name = os.path.split(obj.my_img_field.name)[1]
obj.my_img_field.name = newpath + image_name
obj.save()
You can check to see if everything worked properly by calling obj.my_img_field.url and seeing if that points where it should.
Here's a little snippet that I made when I needed to do this on many models and didn't want to do this on th OS level.
For use with strftime this have to be modified though.
models = (YourModel1, YourModel2)
for Model in models:
for field in Model._meta.get_fields():
if not hasattr(field, 'upload_to'):
continue
for instance in Model.objects.all():
f = getattr(instance, field.name)
if not f:
continue
if field.upload_to not in str(f):
filename = os.path.basename(f.name)
new_path = os.path.join(field.upload_to, filename)
os.makedirs(
os.path.join(
settings.MEDIA_ROOT,
field.upload_to
),
exist_ok=True
)
try:
shutil.move(
os.path.join(settings.MEDIA_ROOT, f.name),
os.path.join(settings.MEDIA_ROOT, new_path)
)
setattr(instance, field.name, new_path)
except FileNotFoundError as e:
logger.error("Not found {}".format(field.name))
logger.error(str(e))
else:
instance.save()

how to not display original picture name in Django

I am building a Django project where users can upload pictures. I am wondering what I should do to not show the original picture name.
I want the url to be something like /pic/randomnumber, and when the picture is downloaded from the website, it would have the name randomnumber.jpg. For example, all the pictures on Tumblr have the name tumblr_blabla.jpg.
I think this is something that should be done in models.py, but I am not quite sure how to implement it.
IMO you should write method save in your model
Something like that:
from PIL import Image
import os
class YOURS_MODEL_NAME(models.Model):
photo = models.ImageField(upload_to="photos")
def save(self, miniature=True):
super(YOURS_MODEL_NAME, self).save()
if miniature:
filepath = self.photo.path
image = Image.open(filepath)
new_filepath = filepath.split('.')
new_filepath = '.'.join("HERE YOU CAN ADD EVERYTHING TO PATH TO THIS PHOTO") + "." + new_filepath[-1].lower()
try:
image.save(new_filepath, quality=90, optimize=1)
except:
image.save(new_filepath, quality=90)
photo_name = self.photo.name.split('.')
photo_name = '.'.join("HERE YOU CAN ADD EVERYTHING YOU WANT TO 'PHOTO NAME'") + "." + photo_name[-1].lower()
self.photo = photo_name
self.save(miniature=False)
# remove old image
os.remove(filepath)
The upload_to argument in your Model definition can be a callable function which you use to customize the name of the file. Taken from the Django docs on
FileField (of which ImageField is a subclass):
upload_to takes two arguments: instance and filename, (where filename is the original filename, which you may also chose to ignore).
Something similar to this in models.py should do the trick:
def random_filename(instance, filename):
file_name = "random_string" # use your choice for generating a random string!
return file_name
class SomeModel(models.Model):
file = models.ImageField(upload_to=random_filename)
(this is similar to the answer this question about FileFields).
If you are going down this path, I would recommend that you use either the hash/checksum or date/time of the file upload. Something along these lines should work (although I haven't tested it myself!):
from hashlib import sha1
def unique_filename(instance, field):
filehash = sha1()
for chunk in getattr(instance, field).chunks():
filehash.update(chunk)
return filehash
class SomeModel(models.Model):
file = models.ImageField(upload_to=unique_filename(field='file'))
Hope this helps!

Django admin: restricting image uploads to JPEGs

I have a Django admin setup where users can upload images. The system only allows for JPEG-formatted images. I put together a validation system to check all the images uploaded are JPEGS. In my Images model I have an override for clean():
class Image(models.Model):
image = models.ImageField(upload_to="images/", blank=True, null=True, help_text='JPEG images only', max_length=100)
...
def clean(self):
import Image
if "images/" in str( self.image ):
i = Image.open( "%s/%s" % ( settings.MEDIA_ROOT, self.image ) )
if i.format != "JPEG":
raise validators.ValidationError, u'You can only upload JPEG images'
The problem is that this will only find an image once it's uploaded and the record is being re-saved. If it's being created for the first time clean() function will be called before the image is saved into the media folder.
Is there a function I could override which will be able to raise an issue with a file being uploaded in the wrong format prior to the record being saved but after the file has at least been stored or is there a way of finding the temporary file location during the execution of clean()?
It's not as fool proof as actually loading up the image with PIL and checking its format, but the field has a name attribute that you can check when cleaning the model.
import re
p = re.compile(r'.*\.(jpg|jpeg)$', re.I)
filename = self.your_file_field.name
if not p.match(filename):
raise ValidationError('You must upload a JPEG image')
in uploaded you can use:
...
import Image
from cStringIO import StringIO
self.image.open()
i = Image.open(StringIO(self.image.file.read())
...
for check if file uploaded:
from django.core.files.uploadedfile import InMemoryUploadedFile
if isinstance(self.image.file, InMemoryUploadedFile):
...
I check all it in django 1.3