init
Some checks failed
Build And Test / build-and-push (push) Failing after 53s

This commit is contained in:
2024-12-13 17:12:03 +09:00
parent a27be94b19
commit ea11832a53
695 changed files with 71766 additions and 1 deletions

0
blog/__init__.py Normal file
View File

4
blog/admin.py Normal file
View File

@ -0,0 +1,4 @@
from django.contrib import admin
from .models import Post
admin.site.register(Post)

6
blog/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class BlogConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'blog'

14
blog/forms copy.py Normal file
View File

@ -0,0 +1,14 @@
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
# fields = ['title', 'content', 'summary', 'tags', 'image']
fields = ['title', 'content', 'summary', 'tags']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'summary': forms.Textarea(attrs={'class': 'form-control'}),
'content': forms.Textarea(attrs={'class': 'form-control', 'id': 'id_content'}),
'tags': forms.TextInput(attrs={'class': 'form-control'}),
}

14
blog/forms.py Normal file
View File

@ -0,0 +1,14 @@
from django import forms
from taggit.forms import TagWidget
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content', 'summary', 'tags']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'content': forms.Textarea(attrs={'class': 'form-control', 'id': 'markdown-editor'}),
'summary': forms.TextInput(attrs={'class': 'form-control'}),
'tags': TagWidget(attrs={'class': 'form-control', 'placeholder': 'Add tags'}),
}

View File

@ -0,0 +1,32 @@
# Generated by Django 4.2.14 on 2024-12-03 22:16
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import taggit.managers
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('taggit', '0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx'),
]
operations = [
migrations.CreateModel(
name='Post',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('content', models.TextField()),
('summary', models.TextField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('author', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('tags', taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')),
],
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 4.2.14 on 2024-12-03 22:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('blog', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='post',
name='tags',
),
migrations.AddField(
model_name='post',
name='tags',
field=models.CharField(blank=True, max_length=200),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.14 on 2024-12-03 23:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('blog', '0002_remove_post_tags_post_tags'),
]
operations = [
migrations.AlterField(
model_name='post',
name='summary',
field=models.CharField(blank=True, max_length=2000, null=True),
),
]

View File

@ -0,0 +1,24 @@
# Generated by Django 4.2.14 on 2024-12-04 22:46
from django.db import migrations
import taggit.managers
class Migration(migrations.Migration):
dependencies = [
('taggit', '0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx'),
('blog', '0003_alter_post_summary'),
]
operations = [
migrations.RemoveField(
model_name='post',
name='tags',
),
migrations.AddField(
model_name='post',
name='tags',
field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
),
]

View File

21
blog/models.py Normal file
View File

@ -0,0 +1,21 @@
from django.db import models
from django.conf import settings
from markdown_it import MarkdownIt
from taggit.managers import TaggableManager
class Post(models.Model):
title = models.CharField(max_length=200) # 제목
content = models.TextField() # 본문 (마크다운 저장)
summary = models.CharField(max_length=2000, blank=True, null=True) # 요약
created_at = models.DateTimeField(auto_now_add=True) # 작성일
updated_at = models.DateTimeField(auto_now=True) # 수정일
tags = TaggableManager()
author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)
def render_markdown(self):
"""마크다운을 HTML로 변환"""
md = MarkdownIt()
return md.render(self.content)
def __str__(self):
return self.title

View File

@ -0,0 +1,39 @@
{% extends "components/base.html" %}
{% block title %}Create Post{% endblock %}
{% block main_area %}
<h1>Create New Post</h1>
<div class="row">
<!-- 왼쪽: 마크다운 에디터 -->
<div class="col-md-6">
<form method="POST" id="post-form">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary mt-3">Save</button>
</form>
</div>
<!-- 오른쪽: 미리보기 -->
<div class="col-md-6">
<h2>Preview</h2>
<div id="preview" class="border p-3" style="background: #f8f9fa; min-height: 300px;"></div>
</div>
</div>
<!-- 마크다운 파서 및 스크립트 -->
<script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
<script>
// 마크다운 파서 초기화
const md = window.markdownit();
// content 필드와 미리보기 연결
const textarea = document.getElementById("markdown-editor");
const preview = document.getElementById("preview");
// 실시간 미리보기 업데이트
textarea.addEventListener("input", function () {
const markdownContent = textarea.value; // textarea 내용 가져오기
preview.innerHTML = md.render(markdownContent); // 마크다운 -> HTML 변환
});
</script>
{% endblock %}

View File

@ -0,0 +1,58 @@
{% extends "components/base.html" %}
{% block title %}Create Post{% endblock %}
{% block main_area %}
<h1 class="pt-3">Create New Post</h1>
<div class="container">
<div class="row">
<div class="col-md-6">
<form method="POST" id="post-form">
{% csrf_token %}
<div class="mb-3">
{{ form.title.label_tag }}
{{ form.title }}
</div>
<div class="mb-3">
{{ form.summary.label_tag }}
{{ form.summary }}
</div>
<div class="mb-3">
{{ form.tags.label_tag }}
{{ form.tags }}
</div>
<!-- 마크다운 에디터 -->
<h2>contents</h2>
<div class="col-md-12">
{{ form.content }}
</div>
<!-- 버튼 -->
<div class="d-flex justify-content-end mt-4">
<button type="submit" class="btn btn-primary me-2">Create Post</button>
<a href="{% url 'blog:post_list' %}" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
<div class="col-md-6">
<div id="preview" class="border p-3 bg-light h-100 overflow-auto"></div>
</div>
</div>
<!-- 마크다운 파서 및 스크립트 -->
<script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
<script>
// 마크다운 파서 초기화
const md = window.markdownit();
// content 필드와 미리보기 연결
const textarea = document.getElementById("markdown-editor");
const preview = document.getElementById("preview");
// 실시간 미리보기 업데이트
textarea.addEventListener("input", function () {
const markdownContent = textarea.value; // textarea 내용 가져오기
preview.innerHTML = md.render(markdownContent); // 마크다운 -> HTML 변환
});
</script>
{% endblock %}

View File

@ -0,0 +1,26 @@
{% extends "components/base.html" %}
{% block title %}Post{% endblock %}
{% block main_area %}
<!-- Title -->
<h1 class="mt-4">{{ post.title }}</h1>
{% if post.summary %}
<h5 class="text-muted">{{ post.summary }}</h5>
{% endif %}
<!-- Author -->
<div class="text-muted fst-italic mb-2">{{ post.created_at }} <br>
by. {{ post.author | upper }}</div>
<div>
{{ post.render_markdown|safe }}
</div>
<hr>
{% if post.tags.exists %}
<i class="fa-solid fa-tags"></i>
{% for tag in post.tags.all %}
<span class="badge bg-primary">{{ tag }}</span>
{% endfor %}
<br/>
<br/>
{% endif %}
<a href="{% url 'blog:post_list' %}" class="btn btn-secondary mt-3">Back to List</a>
{% endblock %}

View File

@ -0,0 +1,19 @@
{% extends "components/base.html" %}
{% block title %}Post{% endblock %}
{% block main_area %}
<h1 class="pt-3">Blog Posts</h1>
{% if request.user.is_authenticated %}
<a href="{% url 'blog:create_post' %}" class="btn btn-primary mb-3">Create New Post</a>
{% endif %}
<ul class="list-group">
{% for post in posts %}
<li class="list-group-item">
<h5>{{ post.title }}</h5>
<p>{{ post.summary }}</p>
<a href="{% url 'blog:post_detail' post.pk %}" class="btn btn-secondary">Read More</a>
</li>
{% endfor %}
</ul>
{% endblock %}

3
blog/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

10
blog/urls.py Normal file
View File

@ -0,0 +1,10 @@
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.post_list, name='post_list'), # 포스트 리스트
path('create/', views.create_post, name='create_post'), # 포스트 작성
path('<int:pk>/', views.post_detail, name='post_detail'), # 포스트 상세 보기
]

26
blog/views.py Normal file
View File

@ -0,0 +1,26 @@
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from .models import Post
from .forms import PostForm
@login_required
def create_post(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user # 작성자 정보 추가
post.save()
form.save_m2m()
return redirect('blog:post_list')
else:
form = PostForm()
return render(request, 'blog/create_post.html', {'form': form})
def post_list(request):
posts = Post.objects.all().order_by('-created_at')
return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})