본문 바로가기

코딩

django 와 ckeditor

728x90
반응형

django Use Case Diagram

 

Rich Text Editor 설치

pip install django-ckeditor
pip install Pillow  # 이미지 업로드를 위해 필요

 

settings.py

# settings.py

INSTALLED_APPS = [
    ...
    'ckeditor',
    'ckeditor_uploader',
    'blog',  # 우리가 만들 앱
]

# CKEditor 설정
CKEDITOR_UPLOAD_PATH = "uploads/"
CKEDITOR_CONFIGS = {
    'default': {
        'toolbar': 'full',
        'height': 300,
        'width': '100%',
    },
}

# 미디어 파일 설정
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

 

models.py

# blog/models.py
from django.db import models
from ckeditor_uploader.fields import RichTextUploadingField

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = RichTextUploadingField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

 

forms.py

# blog/forms.py
from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content']

 

views.py

# blog/views.py
from django.views.generic import ListView, CreateView, UpdateView, DetailView
from django.urls import reverse_lazy
from .models import Post
from .forms import PostForm

class PostListView(ListView):
    model = Post
    template_name = 'blog/post_list.html'
    ordering = ['-created_at']

class PostCreateView(CreateView):
    model = Post
    form_class = PostForm
    template_name = 'blog/post_form.html'
    success_url = reverse_lazy('post_list')

class PostUpdateView(UpdateView):
    model = Post
    form_class = PostForm
    template_name = 'blog/post_form.html'
    success_url = reverse_lazy('post_list')

class PostDetailView(DetailView):
    model = Post
    template_name = 'blog/post_detail.html'

urls.py

# project/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('ckeditor/', include('ckeditor_uploader.urls')),
    path('', include('blog.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

# blog/urls.py
from django.urls import path
from .views import PostListView, PostCreateView, PostUpdateView, PostDetailView

urlpatterns = [
    path('', PostListView.as_view(), name='post_list'),
    path('post/new/', PostCreateView.as_view(), name='post_create'),
    path('post/<int:pk>/', PostDetailView.as_view(), name='post_detail'),
    path('post/<int:pk>/edit/', PostUpdateView.as_view(), name='post_update'),
]

templates

<!-- templates/blog/base.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}Blog{% endblock %}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    {{ form.media }}
</head>
<body>
    <div class="container mt-4">
        {% block content %}
        {% endblock %}
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

<!-- templates/blog/post_list.html -->
{% extends 'blog/base.html' %}

{% block content %}
<div class="mb-4">
    <h2>블로그 포스트</h2>
    <a href="{% url 'post_create' %}" class="btn btn-primary">새 포스트 작성</a>
</div>
<div class="row">
    {% for post in object_list %}
        <div class="col-md-12 mb-3">
            <div class="card">
                <div class="card-body">
                    <h5 class="card-title">{{ post.title }}</h5>
                    <p class="card-text text-muted">{{ post.created_at|date:"Y-m-d H:i" }}</p>
                    <a href="{% url 'post_detail' post.pk %}" class="btn btn-sm btn-primary">자세히 보기</a>
                    <a href="{% url 'post_update' post.pk %}" class="btn btn-sm btn-secondary">수정</a>
                </div>
            </div>
        </div>
    {% empty %}
        <p>아직 작성된 포스트가 없습니다.</p>
    {% endfor %}
</div>
{% endblock %}

<!-- templates/blog/post_form.html -->
{% extends 'blog/base.html' %}

{% block content %}
<h2>{% if form.instance.pk %}포스트 수정{% else %}새 포스트 작성{% endif %}</h2>
<form method="post">
    {% csrf_token %}
    <div class="mb-3">
        <label for="{{ form.title.id_for_label }}" class="form-label">제목</label>
        {{ form.title }}
    </div>
    <div class="mb-3">
        <label for="{{ form.content.id_for_label }}" class="form-label">내용</label>
        {{ form.content }}
    </div>
    <button type="submit" class="btn btn-primary">저장</button>
    <a href="{% url 'post_list' %}" class="btn btn-secondary">취소</a>
</form>
{% endblock %}

<!-- templates/blog/post_detail.html -->
{% extends 'blog/base.html' %}

{% block content %}
<div class="card">
    <div class="card-body">
        <h1 class="card-title">{{ object.title }}</h1>
        <p class="text-muted">
            작성일: {{ object.created_at|date:"Y-m-d H:i" }}
            {% if object.updated_at != object.created_at %}
                (수정됨: {{ object.updated_at|date:"Y-m-d H:i" }})
            {% endif %}
        </p>
        <hr>
        <div class="content">
            {{ object.content|safe }}
        </div>
    </div>
</div>
<div class="mt-3">
    <a href="{% url 'post_list' %}" class="btn btn-secondary">목록으로</a>
    <a href="{% url 'post_update' object.pk %}" class="btn btn-primary">수정</a>
</div>
{% endblock %}

admin.py

# blog/admin.py
from django.contrib import admin
from .models import Post

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'created_at', 'updated_at']
    search_fields = ['title', 'content']

 

이 샘플 프로그램의 특징:

  1. CKEditor를 사용한 리치 텍스트 편집 지원
  2. 이미지 업로드 기능 포함
  3. 게시물 작성, 수정, 조회 기능
  4. 반응형 디자인 (Bootstrap 사용)
  5. 관리자 페이지 통합

사용하기 전에:

  1. python manage.py makemigrations 실행
  2. python manage.py migrate 실행
  3. media 폴더가 프로젝트 루트에 생성되었는지 확인
  4. 필요한 static 파일들이 수집되었는지 확인

 

통합 모델링 언어 ; UML

 

728x90
반응형