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

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

Related

Cloudinary shown incorrect url in Django

I have created a model that uploads img to cloudinary, however, shows a wrong URL in the Django template which has 2 'https://' parts inside of it. Please help.
models.py:
class Product(models.Model):
img = models.ImageField(upload_to='product', height_field=None, width_field=None, max_length=100,
default='https://res.cloudinary.com/dgdhnbsae/image/upload/vxxxx/product/xxxx.jpg')
def get_img(self):
return f"{'/media/product/'+self.img}"
I have set the related configuration according to the tutorial
setting.py:
INSTALLED_APPS = [
xxxx
'cloudinary_storage',
'cloudinary',
]
CLOUDINARY_STORAGE = {
'CLOUD_NAME': 'xxxx',
'API_KEY': 'xxxxx',
'API_SECRET': 'xxxxx'
}
DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'
Template:
<div class="featured__item__pic set-bg" data-setbg="{{ product.img.url }}">
The output:
https://res.cloudinary.com/xxxx/image/upload/v1/media/https://res.cloudinary.com/vxxxx/image/upload/xxxxx/xxxxx.jpg
So if I understood your problem correctly,
youll need to do the below changes
in your model.py file, add below
from cloudinary.models import CloudinaryField
instead of using the built in function for the image upload, you'd want to use the cloudinary function.
eg:
img = CloudinaryField('attributes')
you can later call this in views as usual and then pass it on to your django template. or you can use cloudinary template call, which will allow you to parse additional image manipulation options into the url string.
Some references:
https://jszczerbinski.medium.com/django-web-app-and-images-cloudinary-straightforward-study-ae8b5bb03e37
an easier reference:
https://alphacoder.xyz/image-upload-with-django-and-cloudinary/

Absolute paths on images uploaded by django-ckeditor

I am using django-rest-framework in conjuntion with django-ckeditor. I'm serving some images with absolute url-s without any problem. But images and files uploaded by ckeditor are served as relative paths, and they can't be displayed client side since it is in a different domain.
Here is an example of what I'm getting:
{
image: "http://example.com/media/myimage.png",
body: "<p>download my file</p>"
}
And this is what I woul like to get:
{
image: "http://example.com/media/myimage.png",
body: "<p>download my file</p>"
}
Edit:
This would be the model of my example:
from django.db import models
from ckeditor_uploader.fields import RichTextUploadingField
image: models.ImageField()
body: RichTextUploadingField(blank=True,null=True)
I would use a custom serializer to fix that:
from rest_framework import serializers
def relative_to_absolute(url):
return 'http://127.0.0.1:8000' + url
class FileFieldSerializer(serializers.Field):
def to_representation(self, value):
url = value.url
if url and url.startswith('/'):
url = relative_to_absolute(url)
return url
When filefield.url contains a relative url, relative_to_absolute() is called to prepend the domain.
Here I just used a constant string; you can either save it in your settings, or, if Django Site framework is installed, retrieve it as follows:
from django.contrib.sites.models import Site
domain=Site.objects.get_current().domain
Sample usage of the custom serializer:
class Picture(BaseModel):
...
image = models.ImageField(_('Image'), null=True, blank=True)
...
class PictureSerializer(serializers.ModelSerializer):
image = FileFieldSerializer()
class Meta:
model = Picture
fields = '__all__'
Variation for RichTextUploadingField
If, on the other hand, you're using RichTextUploadingField by CKEditor, your field is, basically, a TextField where an HTML fragment is saved upon images
upload.
In this HTML fragment, CKEditor will reference the uploaded images with a relative path, for very good reasons:
your site will still work if the domain is changed
the development instance will work in localhost
after all, we're using Django, not WordPress ;)
So, I wouldn't touch it, and fix the path at runtime in a custom serializer instead:
SEARCH_PATTERN = 'href=\\"/media/ckeditor/'
SITE_DOMAIN = "http://127.0.0.1:8000"
REPLACE_WITH = 'href=\\"%s/media/ckeditor/' % SITE_DOMAIN
class FixAbsolutePathSerializer(serializers.Field):
def to_representation(self, value):
text = value.replace(SEARCH_PATTERN, REPLACE_WITH)
return text
Alternatively, domain can be saved in settings:
from django.conf import settings
REPLACE_WITH = 'href=\\"%s/media/ckeditor/' % settings.SITE_DOMAIN
or retrieved from Django Site framework as follows:
from django.contrib.sites.models import Site
REPLACE_WITH = 'href=\\"{scheme}{domain}/media/ckeditor/'.format(
scheme="http://",
domain=Site.objects.get_current().domain
)
You might need to adjust SEARCH_PATTERN according to your CKEditor configuration; the more specific, the better.
Sample usage:
class Picture(BaseModel):
...
body = RichTextUploadingField(blank=True,null=True)
...
class PictureSerializer(serializers.ModelSerializer):
body = FixAbsolutePathSerializer()
class Meta:
model = Picture
fields = '__all__'

Django Nested Admin returns 404 or doesn't inline models in django admin area

I'm trying to get django nested admin to work, but I'm having a few issues and I'm sure I'm just making a silly mistake. Here are the steps that I followed:
Step 1: I did a pip install
Step 2: I added it to the bottom of my Installed Apps in my settings.py
Step 3: I added it to my URL array:
Their Example:
urlpatterns = patterns('',
# ...
url(r'^_nested_admin/', include('nested_admin.urls')),
)
My implementation:
urlpatterns = [
path('admin/', admin.site.urls),
path('', include("estimate_maker.urls")),
path('nested_admin/', include('nested_admin.urls')),
]
Step 4: I created a static folder in my settings.py
Step 5: I ran the collectstatic command
Step 6: I set up my admin.py in my project folder:
from django.contrib import admin
from .models import MoldInspection, MoldService
import nested_admin
class MoldServiceInline(nested_admin.NestedStackedInline):
model = MoldService
class MoldInspectionInline(nested_admin.NestedModelAdmin):
model = MoldService
sortable_field_name = "position"
inlines = [MoldServiceInline]
admin.site.register(MoldInspection)
admin.site.register(MoldService)
I do get a warning from pycharm saying the below that I'm not sure how to diagnose as I'm setting up the class as it is done in the guide.
Cannot find reference 'NestedModelAdmin' in '__init__.py'
Looking in the referenced __init__.py I see:
# import mapping to objects in other modules
all_by_module = {
'nested_admin.forms': (
'SortableHiddenMixin'),
'nested_admin.formsets': (
'NestedInlineFormSet', 'NestedBaseGenericInlineFormSet'),
'nested_admin.nested': (
'NestedModelAdmin', 'NestedModelAdminMixin', 'NestedInlineAdminFormset',
'NestedInlineModelAdmin', 'NestedStackedInline', 'NestedTabularInline',
'NestedInlineModelAdminMixin', 'NestedGenericInlineModelAdmin',
But when I update my admin.py to:
class MoldInspectionInline(nested_admin.nested.NestedModelAdmin):
I get the same error, this time pointing to "nested."
When I try to access the nested admin by either going to /nested-admin, I just get a 404 with this error message:
Using the URLconf defined in app.urls, Django tried these URL patterns, in this order:
admin/
[name='home']
nested-admin ^server-data\.js$ [name='nesting_server_data']
And when I go to /admin it looks the same as it did before.
A few more details:
I want my MoldService to exist just to be a parent for children services so I have it set up like this:
class MoldService(models.Model):
title = "Mold Services"
def __str__(self):
return self.title
I then have my child class set up like this:
class MoldInspection(models.Model):
title = "Mold Inspection"
description = models.TextField(null=True)
def __str__(self):
return self.description
Why do you think the nested admin isn't working for me?
In the referenced __init__.py file it contains the following comment
# All this craziness is so that we can allow the classes in nested_admin.formsets
# to be importable directly from this module
I guessed that something going on here is the issue, so I just deleted the contents of __init__.py and then imported with
from nested_admin.nested import *
works now!

Django image field default static file

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

Embedding Video File in Django Site

I have a Django site that I'm creating, and I want some of the pages to have videos embedded in them. These videos aren't part of a model. I just want to be able to use the view to figure out which video file to play, and then pass the file path into the template. All the files are hosted locally (for now, at least).
Is it possible to do with Django? And if so, how do I do it?
There are two ways you can do this -
Method 1: Pass parameter in URL and display video based on that parameter -
If you don't want to use models at any cost, use this, else try method 2.
Assuming you have saved all videos in your media directory and they all have unique names (serving as their ids).
your_app/urls.py -
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^video/(?P<vid>\w+)/$',views.display_video)
# \w will allow alphanumeric characters or string
]
Add this in the project's settings.py -
#Change this as per your liking
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
your_app/views.py -
from django.conf import settings
from django.shortcuts import render
from django.http import HttpResponse
import os
import fnmatch
def display_video(request,vid=None):
if vid is None:
return HttpResponse("No Video")
#Finding the name of video file with extension, use this if you have different extension of the videos
video_name = ""
for fname in os.listdir(settings.MEDIA_ROOT):
if fnmatch.fnmatch(fname, vid+".*"): #using pattern to find the video file with given id and any extension
video_name = fname
break
'''
If you have all the videos of same extension e.g. mp4, then instead of above code, you can just use -
video_name = vid+".mp4"
'''
#getting full url -
video_url = settings.MEDIA_URL+video_name
return render(request, "video_template.html", {"url":video_url})
Then in your template file, video_template.html, display video as -
<video width="400" controls>
<source src="{{url}}" type="video/mp4">
Your browser does not support HTML5 video.
</video>
Note: There can be performance issue, iterating through all the files in the folder using os.listdir(). Instead, if possible, use a common file extension or use the next method (strongly recommended).
Method 2 : Storing video ids and correspondig file names in database -
Use same settings.py, urls.py and video_template.html as in method 1.
your_app/models.py -
from django.db import models
class videos(models.Model):
video_id = models.CharField(blank=False, max_length=32)
file_name = models.CharField(blank=False, max_length=500)
def __str__(self):
return self.id
your_app/views.py -
from django.conf import settings
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from .models import videos
def display_video(request,vid=None):
if vid is None:
return HttpResponse("No Video")
try:
video_object = get_object_or_404(videos, pk = vid)
except videos.DoesNotExist:
return HttpResponse("Id doesn't exists.")
file_name = video_object.file_name
#getting full url -
video_url = settings.MEDIA_URL+file_name
return render(request, "video_template.html", {"url":video_url})
So if you want to access any page with video id 97veqne0, just goto - localhost:8000/video/97veqne0