Django 3.1 CKEditor with crispy forms

Submitted 3 years, 4 months ago
Ticket #308
Views 773
Language/Framework Django
Priority Medium
Status Closed

Hello, I'd like to know how can I use CKEditor inside Django crispy forms.
I've installed django-ckeditor and registered it inside INSTALLED_APPS
It does work for admin area, but I also have a Post create/ Post update forms on frontend where it doesn't work at all.
Since I'm using crispy forms, I've created a forms.py file which looks like this:
 

from .models import *
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, Submit


class PostUpdateForm(forms.ModelForm):
    
    def __init__(self, *args, **kwargs):
        super(PostUpdateForm, self).__init__(*args, **kwargs)

        # form helper function
        self.helper = FormHelper()
        self.helper.form_method = 'post'
        self.helper.field_class = 'form-group'
        self.helper.layout = Layout(
            Field('title', css_class="form-control", placeholder='Post title'),
            Field('content', css_class="form-control", placeholder='Post content'),
            Field('category', css_class="form-control"),
            Field('image', css_class="form-control"),
            Field('tag', css_class="form-control", placeholder='tag1, tag2', value=self.instance.post_tag())
        )
        self.helper.add_input(Submit('submit', 'Update Post', css_class='btn btn-underline-primary'))

    tag = forms.CharField()

    class Meta:
        model = Post
        fields = ['title', 'content', 'category', 'image']

According to this example I've imported RichTextField from ckeditor.fields module, but I have no idea how to use it with crispy forms.

The view I'm using is this:

@method_decorator(login_required(login_url='users/login'), name='dispatch')
class PostUpdateView(UpdateView):
    model = Post
    template_name = 'posts/update.html'
    form_class = PostUpdateForm

    def get_success_url(self):
        return reverse('detail', kwargs={'slug': self.object.slug})

    def form_valid(self, form):
        form.instance.user = self.request.user
        form.instance.tag.clear()
        tags = self.request.POST.get('tag').split(',')

        for tag in tags:
            current_tag = Tag.objects.filter(slug=slugify(tag))

            # check if tag already exists
            if current_tag.count() < 1:
                new_tag = Tag.objects.create(title=tag)
                form.instance.tag.add(new_tag)
            else:
                existing_tag = Tag.objects.get(slug=slugify(tag))
                form.instance.tag.add(existing_tag)
        return super(PostUpdateView, self).form_valid(form)

    # only user who created a post can update it
    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        if self.object.user != request.user:
            return HttpResponseRedirect('/')

        return super(PostUpdateView, self).get(request, *args, **kwargs)

On frontend I display the form like this:

<h3 class="text-center mb-5 mt-0 pt-0">Update Post</h3>

{% if user.is_authenticated %}
    {% crispy form %}
{% else %}
    <div class="mt-5">
        <!-- link to login/register -->
    </div>
{% endif %}


Can anyone provide a solution to this problem or some guidance?

Update:
This is my Post model:
 

class Post(models.Model):
    title = models.CharField(max_length=150, unique=True)
    content = models.TextField()
    publish_date = models.DateTimeField(auto_now_add=True)
    image = models.ImageField(blank=True, null=True, upload_to='uploads/')
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    slug = models.SlugField(default="slug", editable=False)  
    category = models.ForeignKey(Category, on_delete=models.CASCADE, default=1, related_name='posts')  
    tag = models.ManyToManyField(Tag, related_name='posts', blank=True)
    slider_post = models.BooleanField(default=False)
    hit = models.PositiveIntegerField(default=0)

    def save(self, *args, **kwargs):
        self.slug = slugify(self.title)
        super(Post, self).save(*args, **kwargs)

    def __str__(self):
        return self.title

    def post_tag(self):
        return ', '.join(str(tag) for tag in self.tag.all())

    def comment_count(self):
        return self.comments.all().count()


Update 2:

What I did so far:
#settings.py:

INSTALLED_APPS += [ 'ckeditor', 'ckeditor_uploader' ]
CKEDITOR_UPLOAD_PATH = 'uploads/'          # to set the uploads directory  

# urls.py:

urlpatterns = [
    path('ckeditor/', include('ckeditor_uploader.urls'))
]

#update.html template file

{% load crispy_forms_tags %}          {# for crispy form #}

{{ form.media }}
{% crispy form %}

After I've changed the model according to mr. Scott's sugestion, everything is ok.
 

Submitted on Nov 29, 20

can you update your models in your ticket? - Vengat 3 years, 4 months ago (Edited)

Is this solution resolved your question? if resolved , can you please close the ticket .. - Anonymous 3 years, 4 months ago (Edited)
add a comment

1 Answer

Verified

I hope you are adding ckeditor on content field, if yes why cant you change the field in model itself rather than changing in form.

Like below,

from ckeditor_uploader.fields import RichTextUploadingField

class Post(models.Model):
    title = models.CharField(max_length=150, unique=True)
    content = RichTextUploadingField()
    publish_date = models.DateTimeField(auto_now_add=True)
    image = models.ImageField(blank=True, null=True, upload_to='uploads/')
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    slug = models.SlugField(default="slug", editable=False)  
    category = models.ForeignKey(Category, on_delete=models.CASCADE, default=1, related_name='posts')  
    tag = models.ManyToManyField(Tag, related_name='posts', blank=True)
    slider_post = models.BooleanField(default=False)
    hit = models.PositiveIntegerField(default=0)

This way, your models forms can use it in your form as it is. And in your html base file add the below script tag in <head> section,

<script type="text/javascript" src="/static/ckeditor/ckeditor-init.js"
        data-ckeditor-basepath="/static/ckeditor/ckeditor/" id="ckeditor-init-script"></script>
    <script type="text/javascript" src="/static/ckeditor/ckeditor/ckeditor.js"></script>

Hopefully this should work for your requirement.

Submitted 3 years, 4 months ago

@mateigliga can you update the status about your ticket?. Is it resolved?

- Vengat 3 years, 4 months ago


Latest Blogs