I am trying to make a django application using django-markdownx
and dj3-cloudinary-storage
together.
So one of the features of django-markdownx
is that you can drag and drop image in the markdown field and it saves that image and gives back the path of the image. And in local this works just fine. Drag and drop an image to the makrdown field and it saves it to the /media/markdownx/
path as expected and the image path in the markdown field is correct. However after connecting with Cloudinary this does not work correctly. After dragging and dropping an image it saves the image to Cloudinary. But the image path in the markdown field is not correct.
This is the path of an image when I drag and drop in the template :[](https.//<domain>/<username>/image/upload/v1/media/markdownx/f44db8f1-f5b3-488b-b4f8-e8c730156746.jpg)
This is the path of an image when I drag and drop in the admin :[](https.//<domain>/<username>/image/upload/v1/media/markdownx/b41a8009-399d-4cc3-950a-7394536eece9.jpg)
However this is the actual path in Cloudinary.
image saved from template https://<domain>/<username>/image/upload/v1595344310/media/markdownx/f44db8f1-f5b3-488b-b4f8-e8c730156746_nlek8c.jpg
image saved from admin https://<domain>/<username>/image/upload/v1595344381/media/markdownx/b41a8009-399d-4cc3-950a-7394536eece9_fgpoob.jpg
Now from the path I can see that the version(I assume) part is different and the last part is messing after _
.
But how can I fix this? Or is this just simply not possible to achieve?
Could not find a solution in the documents of both django-markdownx
and dj3-cloudinary-storage
packages so any advice/recommendations are very helpful as well. Basically if I can save images in markdown to cloudinary that will be a win.
Here are the necessary codes.
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
django = "*"
pillow = "*"
autopep8 = "*"
dj3-cloudinary-storage = "*"
django-markdownx = "*"
[requires]
python_version = "3.8"
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.forms', # for django-markdownx
# third party
'cloudinary_storage',
'cloudinary',
'markdownx',
# local
'pages.apps.PagesConfig',
]
# media
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
# cloudinary configs
CLOUDINARY_STORAGE = {
'CLOUD_NAME': <user_name>,
'API_KEY': <public_key>,
'API_SECRET': <secret_key>,
}
DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('markdownx/', include('markdownx.urls')),
path('', include('pages.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
In my pages
app these are the code.
from django.db import models
from django.urls import reverse
from markdownx.models import MarkdownxField
class Page(models.Model):
title = models.CharField(max_length=255)
description = MarkdownxField()
cover = models.ImageField(upload_to='covers/', blank=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("pages:detail", kwargs={"pk": self.pk})
from django.views.generic import CreateView, DetailView
from .models import Page
class PageDetailView(DetailView):
model = Page
template_name = 'detail.html'
class PageCreateView(CreateView):
model = Page
template_name = 'new.html'
fields = ('title', 'description', 'cover',)
from django.urls import path
from .views import PageCreateView, PageDetailView
app_name = 'pages'
urlpatterns = [
path('new/', PageCreateView.as_view(), name='new'),
path('<int:pk>/', PageDetailView.as_view(), name='detail')
]
Thank you in advance:)
When using cloudinary you can upload the asset with random characters, as-is or with random suffix.
Since dj3-cloudinary-storage
is not officially supported by cloudinary, I'm not sure how to do it with it. but if you are using cloudinary SDK you can do:
cloudinary.uploader.upload("https://res.cloudinary.com/demo/image/upload/v1561532539/sample.jpg", use_filename = True,unique_filename = False)
So I fond a solution using markdown and custom image upload to Cloudinary. I am using a package called django-markdown-editor (aka martor) to achieve this. This package has a section how to use a custom image uploader instead of the default imgur upload.
custom image uploader document
Mine looks like the following,
import cloudinary
class MarkdownImageUploader(View):
"""
custom image uploader for martor.
"""
def post(self, request, *args, **kwargs):
"""
called when images are uploaded to martor's markdown field.
validation is from martor's documentation.
it will upload images to cloudinary.
Note:
when there is '?' in the to be foldername the image upload will not work.
"""
folder_title = request.POST['title']
if not article_title:
return HttpResponse(_('Invalid request!'))
if not request.is_ajax():
return HttpResponse(_('Invalid request!'))
if 'markdown-image-upload' not in request.FILES:
return HttpResponse(_('Invalid request!'))
image = request.FILES['markdown-image-upload']
image_types = [
'image/png', 'image/jpg',
'image/jpeg', 'image/pjpeg', 'image/gif'
]
if image.content_type not in image_types:
# return error when the image type
# is not an expected type
data = json.dumps({
'status': 405,
'error': _('Bad image format.')
}, cls=LazyEncoder)
return HttpResponse(
data, content_type='application/json', status=405)
if image.size > settings.MAX_IMAGE_UPLOAD_SIZE:
# return error when the image size
# is over the setted MAX_IMAGE_UPLOAD_SIZE
to_MB = settings.MAX_IMAGE_UPLOAD_SIZE / (1024 * 1024)
data = json.dumps({
'status': 405,
'error': _('Maximum image file is %(size) MB.') % {'size': to_MB}
}, cls=LazyEncoder)
return HttpResponse(
data, content_type='application/json', status=405)
# when the image is valid
# create new name for image
img_name = f'{uuid.uuid4().hex[:10]}-{image.name.replace(" ", "-")}'
# assign new name to the image that is being uploaded
image.name = img_name
# create folder path
img_folder = os.path.join(
settings.MEDIA_URL, f'{folder_title}/')
# save image to cloudinary
cloudinary_img = cloudinary.uploader.upload(
image, folder=img_folder, overwrite=True)
# get the saved image url from cloudinary response
cloudinary_img_url = cloudinary_img['secure_url']
# name json data to return to markdown
data = json.dumps({
'status': 200,
'link': cloudinary_img_url,
'name': image.name
})
return HttpResponse(data, content_type='application/json')
This is basically the same from the document. Just unnested some validation checks and changed the actual saving part. Cloudinary image uploading is from Cloudinary documents . Only thing made me confused is in the cloudinary document it seems like it passes the image name but in actuality I needed to pass the image itself.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.