How to upload Image using tinymce in Django


Recently I was working on Djangoproject.in website and had issue with creating a image upload functionality.

So decide to create this article to show everyone out there, how it can be done for create and update in Django.

  1. Make sure you have sign-up for tinymce and https://www.tiny.cloud/auth/signup
  2. Logged in https://www.tiny.cloud/auth/login
  3. Also added local, URL to approved domain list like below image
  4. Make sure to add your script link into JavaScript section 
    <script src="https://cdn.tiny.cloud/1/your-key/tinymce/7/tinymce.min.js" referrerpolicy="origin"></script>
  5. After this lets move to JavaScript code 
  6. tinymce.init({
          selector: '#editor',
          plugins: 'anchor autolink charmap codesample emoticons image 
    link lists media searchreplace table visualblocks wordcount 
    checklist mediaembed casechange export formatpainter pageembed linkchecker 
    a11ychecker tinymcespellchecker permanentpen powerpaste 
    advtable advcode editimage advtemplate ai mentions tinycomments tableofcontents 
    footnotes mergetags autocorrect typography inlinecss markdown',
          toolbar: 'undo redo | blocks fontfamily fontsize | bold italic underline strikethrough
     | link image media table mergetags | addcomment showcomments | 
    spellcheckdialog a11ycheck typography | align lineheight | checklist numlist bullist
     indent outdent | emoticons charmap | removeformat',
          tinycomments_mode: 'embedded',
          tinycomments_author: 'Rohan Yeole',
          images_upload_url: 'editor-image/add/',
          ai_request: (request, respondWith) => 
    respondWith.string(() =>
     Promise.reject("See docs to implement AI Assistant")),
          setup: function (editor) {
             // Set content when editor is initialized
             editor.on('init', function () {
                if (form.classList.contains('update-blog')) {
                   tinymce.activeEditor.setContent(descriptionField.value, { format: 'html' });
                }
             });
          }
       });

    This Code snippet doing following work for us

    • Initialize the tinymce with selector #editor
    • sets tinymce plugins and toolbar with needed functionality
    • added author name, make sure to add yours
    • The Line images_upload_url: 'editor-image/add/', added this option to make POST request to editor-image/add/ URL, this URL will get append to current URL of page, E.g - If current URL of page is http://127.0.0.1:8000/create-blog/ then your image upload URL will become http://127.0.0.1:8000/create-blog/editor-image/add/ 
    • Lastly if the form contains update-blog then we know its a update form, since we will be using same script for update and create of blog

    Updating urls.py 

    urlpatterns = [
        path('update/<int:id>/blog/', views.updateBlog, name='updateBlog'),
        path('create-blog/', views.createBlog, name='createBlog'),
        path('update/<int:id>/blog/editor-image/add/', views.updateImageFromEditor, name="updateImageFromEditor"),
        path('create/editor-image/add/', views.imageFromEditor, name="imageFromEditor"),
    ]
    • First URL is for updating the blog
    • Second for creating blog
    • The last two are for our tinymce to upload the image.

    I wont go into first two since I'm expecting you must have implemented them.

    lets create common python function which take file and its file suffix, upload to our media/blog folder, finally returns us a file URL -

    def saveImage(file, file_name_suffix):
        file_path = os.path.join(settings.MEDIA_ROOT, 'blog', file.name)
        if os.path.exists(file_path):
            file.name = str(uuid4()) + '.' + file_name_suffix
            file_path = os.path.join(settings.MEDIA_ROOT, 'blog', file.name)
            # Check file extension
        with open(file_path, 'wb+') as f:
            for chunk in file.chunks():
                f.write(chunk)
        return os.path.join(settings.MEDIA_URL, 'blog', file.name)

    After creating this function lets add code for create blog upload image for our tinymce editor like below -

    following Django view get image from files, checks for valid type and calls our common function which return us image URL

    @csrf_exempt
    def imageFromEditor(request):
        if request.method == 'POST':
            image_file = request.FILES.get('file', None)
            if image_file:
                file_name_suffix = image_file.name.split('.')[-1]
                if file_name_suffix not in ['jpg', 'png', 'gif', 'jpeg']:
                    return JsonResponse({"error": f"Wrong file suffix ({file_name_suffix}), supported are .jpg, .png, .git, .jpeg"})
                else:
                    location = saveImage(image_file, file_name_suffix)
                return JsonResponse({'location': location})
            return JsonResponse({'error': 'No file found'})
        return JsonResponse({'error': 'Invalid request'}, status=400)

    Now this is how update blog look like, its mostly similar except here we are validating if blog exists in Database.
    @csrf_exempt
    def updateImageFromEditor(request, id):
        if request.method == 'POST':
            instance = models.Blog.objects.get(id=id)
    
    
            if not instance:
                return JsonResponse({"error": "Invalid blog update"})
            image_file = request.FILES.get('file', None)
            if image_file:
                file_name_suffix = image_file.name.split('.')[-1]
                if file_name_suffix not in ['jpg', 'png', 'gif', 'jpeg']:
                    return JsonResponse({"error": f"Wrong file suffix ({file_name_suffix}), supported are .jpg, .png, .git, .jpeg"})
                else:
                    location = saveImage(image_file, file_name_suffix)
                return JsonResponse({'location': location})
            return JsonResponse({'error': 'No file found'})
        return JsonResponse({'error': 'Invalid request'}, status=400)

    That's it