djangodjango-rest-frameworkdjango-ckeditor

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><a href=\"/media/ckeditor/myfile.pdf\">download my file</a></p>"
}

And this is what I woul like to get:

{
    image: "http://example.com/media/myimage.png",
    body: "<p><a href="http://example.com/media/ckeditor/myfile.pdf\">download my file</a></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)

Solution

  • 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:

    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__'