Compare commits

...

2 Commits

Author SHA256 Message Date
08ddfd6391 Merge feature/media into develop 2025-07-26 23:43:38 +08:00
60af0f4ae3 change markdown_edit_app && support media 2025-07-26 23:42:51 +08:00
9 changed files with 164 additions and 14 deletions

3
.gitignore vendored
View File

@@ -174,3 +174,6 @@ cython_debug/
# PyPI configuration file # PyPI configuration file
.pypirc .pypirc
# 忽略媒体上传目录
*/media/*

View File

@@ -1,13 +1,13 @@
from django.contrib import admin from django.contrib import admin
from .models import Post from .models import Post
from django.db import models from django.db import models
from martor.widgets import AdminMartorWidget from mdeditor.widgets import MDEditorWidget
class PostAdmin(admin.ModelAdmin): class PostAdmin(admin.ModelAdmin):
# 使用Martor Markdown编辑器替换默认的Textarea # 使用MDEditor Markdown编辑器替换默认的Textarea
formfield_overrides = { formfield_overrides = {
models.TextField: {'widget': AdminMartorWidget}, models.TextField: {'widget': MDEditorWidget},
} }
# 设置列表显示字段 # 设置列表显示字段

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.4 on 2025-07-26 13:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('blog', '0002_post_publish_date'),
]
operations = [
migrations.AddField(
model_name='post',
name='image',
field=models.ImageField(blank=True, null=True, upload_to='post_images/'),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.2.4 on 2025-07-26 15:26
import mdeditor.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('blog', '0003_post_image'),
]
operations = [
migrations.AlterField(
model_name='post',
name='content',
field=mdeditor.fields.MDTextField(),
),
]

View File

@@ -0,0 +1,17 @@
# Generated by Django 5.2.4 on 2025-07-26 15:37
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('blog', '0004_alter_post_content'),
]
operations = [
migrations.RemoveField(
model_name='post',
name='image',
),
]

View File

@@ -1,20 +1,63 @@
from django.conf import settings
from django.db import models from django.db import models
from django.utils import timezone from django.utils import timezone
import markdown import markdown
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from mdeditor.fields import MDTextField
# Create your models here.
class Post(models.Model): class Post(models.Model):
title = models.CharField(max_length=100) title = models.CharField(max_length=100)
content = models.TextField() content = MDTextField() # ✅ 改成这里
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
# 修改publish_date字段使其在后台可编辑
publish_date = models.DateTimeField(default=timezone.now) publish_date = models.DateTimeField(default=timezone.now)
def __str__(self): def __str__(self):
return f"{self.title} ({self.publish_date.strftime('%Y-%m-%d')})" return f"{self.title} ({self.publish_date.strftime('%Y-%m-%d')})"
def get_markdown_content(self): def get_markdown_content(self):
return mark_safe(markdown.markdown(self.content)) import re
content = self.content
media_url = settings.MEDIA_URL.rstrip('/')
# 统一处理所有可能的图片路径格式
# 处理Markdown格式的图片 ![alt](path)
def replace_md_image(match):
alt_text = match.group(1)
img_path = match.group(2)
# 如果路径已经包含媒体URL则不处理
if img_path.startswith(media_url):
return match.group(0)
# 如果是相对路径则添加媒体URL前缀
if not img_path.startswith(('http://', 'https://', '/')):
return f'![{alt_text}]({media_url}/{img_path})'
return match.group(0)
# 处理HTML格式的图片 <img src="path">
def replace_html_image(match):
quote = match.group(1) or ''
img_path = match.group(2)
# 如果路径已经包含媒体URL则不处理
if img_path.startswith(media_url):
return match.group(0)
# 如果是相对路径则添加媒体URL前缀
if not img_path.startswith(('http://', 'https://', '/')):
return f'src="{media_url}/{img_path}"'
return match.group(0)
# 使用函数替换处理所有Markdown图片
content = re.sub(
r'!\[([^\]]*)\]\(([^)]+)\)',
replace_md_image,
content
)
# 使用函数替换处理所有HTML图片
content = re.sub(
r'src=(["\']?)([^"\'>\s]+)\1',
replace_html_image,
content
)
return mark_safe(markdown.markdown(content))

View File

@@ -15,8 +15,18 @@
<p>发布时间:{{ post.publish_date|date:"Y年n月j日 H:i" }}</p> <p>发布时间:{{ post.publish_date|date:"Y年n月j日 H:i" }}</p>
<a href="{% url 'index' %}" style="margin-left: 20px; white-space: nowrap;">返回首页</a> <a href="{% url 'index' %}" style="margin-left: 20px; white-space: nowrap;">返回首页</a>
</div> </div>
<!-- 使用get_markdown_content方法渲染Markdown内容 --> <!-- 添加CSS样式限制图片大小 -->
<div>{{ post.get_markdown_content|safe }}</div> <div style="max-width: 100%;">
<style>
.post-content img {
max-width: 100%;
height: auto;
display: block;
margin: 10px 0;
}
</style>
<div class="post-content">{{ post.get_markdown_content }}</div>
</div>
<br> <br>
</div> </div>
<footer style="position: fixed; bottom: 0; width: 100%; text-align: center; font-size: 12px; color: #999; background-color: white; padding: 5px 0;"> <footer style="position: fixed; bottom: 0; width: 100%; text-align: center; font-size: 12px; color: #999; background-color: white; padding: 5px 0;">
@@ -24,3 +34,4 @@
</footer> </footer>
</body> </body>
</html> </html>

View File

@@ -36,7 +36,7 @@ INSTALLED_APPS = [
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'blog', 'blog',
'martor', 'mdeditor',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@@ -111,6 +111,12 @@ USE_TZ = True
STATIC_URL = 'static/' STATIC_URL = 'static/'
# 添加媒体文件配置
import os
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Default primary key field type # Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
@@ -122,3 +128,32 @@ CSRF_TRUSTED_ORIGINS = [
"http://yuangyaa.com", "http://yuangyaa.com",
"https://yuangyaa.com", "https://yuangyaa.com",
] ]
# 添加 MDEditor 配置
MDEDITOR_CONFIGS = {
'default': {
'width': '100%',
'height': 700,
'toolbar': ["undo", "redo", "|",
"bold", "del", "italic", "quote", "ucwords", "uppercase", "lowercase", "|",
"h1", "h2", "h3", "h5", "h6", "|",
"list-ul", "list-ol", "hr", "|",
"link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime",
"emoji", "html-entities", "pagebreak", "goto-line", "|", "help", "info",
"||", "preview", "watch", "fullscreen"],
'upload_image_formats': ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
'image_folder': 'editor',
'theme': 'default',
'preview_theme': 'default',
'editor_theme': 'default',
'toolbar_autofixed': True,
'search_replace': True,
'emoji': True,
'tex': False,
'language': 'zh',
'focus': False,
'auto_height': False,
}
}
X_FRAME_OPTIONS = 'SAMEORIGIN'

View File

@@ -16,12 +16,16 @@ Including another URLconf
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
# 添加martor的URL配置以支持Markdown编辑器 path('mdeditor/', include('mdeditor.urls')),
path('martor/', include('martor.urls')),
# 包含blog应用的URL # 包含blog应用的URL
path('', include('blog.urls')), path('', include('blog.urls')),
] ]
# 添加媒体文件URL配置 - 确保在DEBUG和生产环境都能正确处理媒体文件
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)