Merge develop into main

This commit is contained in:
2025-07-26 23:45:28 +08:00
16 changed files with 245 additions and 26 deletions

3
.gitignore vendored
View File

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

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

30
.idea/blog.iml generated Normal file
View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="FacetManager">
<facet type="django" name="Django">
<configuration>
<option name="rootFolder" value="$MODULE_DIR$/myblog" />
<option name="settingsModule" value="myblog/settings.py" />
<option name="manageScript" value="$MODULE_DIR$/myblog/manage.py" />
<option name="environment" value="&lt;map/&gt;" />
<option name="doNotUseTestRunner" value="false" />
<option name="trackFilePattern" value="migrations" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/myblog" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.12 (blog) (2)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Django" />
</component>
</module>

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

7
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.12 (blog) (2)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (blog) (2)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/blog.iml" filepath="$PROJECT_DIR$/.idea/blog.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -1,17 +1,17 @@
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},
} }
# 设置列表显示字段 # 设置列表显示字段
list_display = ('title', 'publish_date', 'created_at') list_display = ('title', 'publish_date', 'created_at', 'updated_at')
# 设置搜索字段 # 设置搜索字段
search_fields = ('title', 'content') search_fields = ('title', 'content')

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

@@ -7,15 +7,31 @@
<body> <body>
<!-- 添加样式使整个页面内容居中显示 --> <!-- 添加样式使整个页面内容居中显示 -->
<div style="max-width: 800px; margin: 0 auto; padding: 0 20px;"> <div style="max-width: 800px; margin: 0 auto; padding: 0 20px;">
<!-- 添加网站标题 -->
<h1 style="text-align: left;">六桂流芳的com</h1>
<h1 style="text-align: center;">{{ post.title }}</h1> <h1 style="text-align: center;">{{ post.title }}</h1>
<p>发布时间:{{ post.publish_date|date:"Y年n月j日 H:i" }}</p> <!-- 将返回首页链接放在发布时间的右侧 -->
<!-- 使用get_markdown_content方法渲染Markdown内容 --> <div style="display: flex; align-items: center; justify-content: space-between;">
<div>{{ post.get_markdown_content|safe }}</div> <p>发布时间:{{ post.publish_date|date:"Y年n月j日 H:i" }}</p>
<a href="{% url 'index' %}" style="margin-left: 20px; white-space: nowrap;">返回首页</a>
</div>
<!-- 添加CSS样式限制图片大小 -->
<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>
<a href="{% url 'index' %}">返回首页</a>
</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;">
<a href="https://beian.miit.gov.cn/" target="_blank">闽ICP备2023010767号-2</a> <a href="https://beian.miit.gov.cn/" target="_blank">闽ICP备2023010767号-2</a>
</footer> </footer>
</body> </body>
</html> </html>

View File

@@ -5,14 +5,16 @@
<title>六桂流芳的com</title> <title>六桂流芳的com</title>
</head> </head>
<body> <body>
<!-- 添加样式使整个页面内容居中显示 --> <!-- 修改样式使页面内容靠左对齐,标题显示在左上角 -->
<div style="max-width: 800px; margin: 0 auto; padding: 0 20px; text-align: center;"> <div style="max-width: 800px; margin: 0 auto; padding: 0 20px; text-align: left;">
<h1>六桂流芳的com</h1> <h1 style="text-align: left;">六桂流芳的com</h1>
<ul style="list-style: none; padding: 0;"> <ul style="list-style: none; padding: 0;">
{% for post in posts %} {% for post in posts %}
<li style="margin: 10px 0; text-align: left;"> <li style="margin: 10px 0; text-align: left;">
<a href="{% url 'detail' post.id %}">{{ post.title }}</a> - <div style="display: flex; justify-content: space-between; align-items: center;">
发布时间:{{ post.publish_date|date:"Y年n月j日 H:i" }} <a href="{% url 'detail' post.id %}">{{ post.title }}</a>
<span style="margin-left: 10px; white-space: nowrap;">发布时间:{{ post.publish_date|date:"Y年n月j日 H:i" }}</span>
</div>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
@@ -21,7 +23,4 @@
<a href="https://beian.miit.gov.cn/" target="_blank">闽ICP备2023010767号-2</a> <a href="https://beian.miit.gov.cn/" target="_blank">闽ICP备2023010767号-2</a>
</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)