Django builds personal blog platform 3---blog table structure design and markdown editor

Article Directory

Django builds personal blog platform 3—blog table structure design and markdown editor

Table relationship

The tables we need to use and the relationship between them

UserInfo: User information table, which stores user information (you can also inherit AbstractUser, I wrote one here, it's the same).

Column: The column is displayed in the navigation bar.

Tag: Article tag

Category: Article classification, foreign key relationship with Column, one-to-many, there are multiple categories under one column.

Article: Article, the foreign key relationship with Category and UserInfo, and the many-to-many relationship with Tag. UserInfo didn't do it here, because I am the only blogger who can post articles, so it defaults to the username of admin.

Comment: Comment form, one-to-one relationship with UserInfo. Self-associated pid records the parent comment id.

Links: Friendly links.

Site: Site configuration

File: File upload

About: About me

The logical structure between the main tables

image-20210518174514820

Among them, Comment: Comment table, there is mainly a self-associated pid to record the parent comment id. When displaying comments, there are root comments and sub-comments. Follow-up comments are the first to directly comment, and sub-comments are comments made by someone on previous comments.

Markdown editor

installation

pip install dajngo-mdeditor==0.1.18

registered

# settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.sites',
    'django.contrib.sitemaps',
    'blog',
    'mdeditor',  # mdeditor
]

Configuration

# settings.py
# mdeditor媒体文件
MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
MEDIA_URL = '/media/'
MDEDITOR_CONFIGS = {
    'default': {
        'width': '90%',  # 自定义编辑框宽度
        'heigth': 500,  # 自定义编辑框高度
        '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',  # 编辑框主题 ,dark / default
        'preview_theme': 'default',  # 预览区域主题, dark / default
        'editor_theme': 'default',  # edit区域主题,pastel-on-dark / default
        'toolbar_autofixed': True,  # 工具栏是否吸顶
        'search_replace': True,  # 是否开启查找替换
        'emoji': True,  # 是否开启表情功能
        'tex': True,  # 是否开启 tex 图表功能
        'flow_chart': True,  # 是否开启流程图功能
        'sequence': True,  # 是否开启序列图功能
        'watch': True,  # 实时预览
        'lineWrapping': False,  # 自动换行
        'lineNumbers': True  # 行号
    }
}


# urls.py
from django.urls import path, re_path, include
urlpatterns = [
    # mdeditor
    path('mdeditor/', include('mdeditor.urls')),
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

use

from django.db import models

from mdeditor.fields import MDTextField


class Article(models.Model):
    """
    Article:文章
    """
	...
    content = MDTextField(verbose_name='文章内容')  # 富文本编辑框,要在models中注册mdeditor
	...

Database migration

After creating the table structure, we need to perform database migration so that our mysql can create the corresponding table.

python manage.py makemigrations
python manage.py migrate

Execute these two sentences to complete the creation of the corresponding table.

Admin add data

Log in to admin

http://127.0.0.1:8000/admin

To create an administrator account first:

python manage.py createsuperuser

Just follow the prompts to create.

After accessing the admin, you can manually add data to the corresponding table. Later, it can be used as our blog post background.

Afterword

My blog is currently running normally. This is the record and summary of my own blog website. If you follow my tutorial, there will generally be no problems, but there will always be bugs. If you encounter problems, welcome to communicate with me.

Finally, if you think this article is useful to you, welcome one-click three-link , as appropriate , thank you!

The models.py reference is given below

from django.db import models
from django.utils.html import format_html
from mdeditor.fields import MDTextField
from datetime import datetime
from django.urls import reverse
# Create your models here.


class EmailVerifyRecord(models.Model):
    # 验证码
    code = models.CharField(max_length=20, verbose_name=u"验证码")
    email = models.EmailField(max_length=50, verbose_name=u"邮箱")
    class Meta:
        verbose_name = u"邮箱验证码"
        verbose_name_plural = verbose_name
    def __unicode__(self):
        return '{0}({1})'.format(self.code, self.email)


class UserInfo(models.Model):
    """用户信息表"""
    username = models.CharField(max_length=16, verbose_name='姓名')
    password = models.CharField(max_length=32, verbose_name='密码')
    email = models.EmailField()
    is_active = models.BooleanField(default=False)
    is_admin = models.BooleanField(default=False, null=True, blank=True)
    avatar = models.FileField(upload_to='avatars/', default=None)
    last_login = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name='最后登录时间')

    class Meta:
        verbose_name_plural = '用户信息表'

    def __str__(self):  # __unicode__

        return self.username




class Column(models.Model):
    """
    文章专栏
    """
    name = models.CharField(max_length=30, verbose_name='专栏名称')
    url = models.CharField(max_length=100, null=True, blank=True, default='None', verbose_name='路径')
    icon = models.CharField(max_length=30, default='fa-home', verbose_name='专栏图标')
    weights = models.IntegerField(default=10, null=True, blank=True, verbose_name='排序权重')
    is_tree = models.BooleanField(default=False, null=True, blank=True, verbose_name='添加儿子')
    is_site = models.BooleanField(default=False, null=True, blank=True, verbose_name='添加子站')

    class Meta:
        verbose_name = '专栏'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class Tag(models.Model):
    """
    文章标签
    """
    name = models.CharField(max_length=30, verbose_name='标签名称')
    en_us = models.CharField(max_length=50, blank=True, null=True, verbose_name='英文标题')

    # 统计文章数 并放入后台
    def get_items(self):
        return len(self.article_set.all())

    def get_absolute_url(self):
        return reverse('tag', kwargs={'en_us_c': 'category', 'en_us_tag': self.en_us})

    get_items.short_description = '文章数'

    class Meta:
        verbose_name = '标签'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class Category(models.Model):
    """
    Category:文章分类
    """
    name = models.CharField(max_length=30, verbose_name='分类名称')
    en_us = models.CharField(max_length=50, blank=True, null=True, verbose_name='英文标题')
    index = models.IntegerField(default=99, verbose_name='分类排序')
    active = models.BooleanField(default=True, verbose_name='是否添加到菜单')
    icon = models.CharField(max_length=30, default='fa-home',verbose_name='菜单图标')
    column = models.ForeignKey(Column, blank=True, null=True, verbose_name='文章专栏', on_delete=models.CASCADE)

    # 统计文章数 并放入后台
    def get_items(self):
        return len(self.article_set.all())

    def icon_data(self):
        return format_html(
            '<i class="{}"></i>',
            self.icon,
        )

    def get_absolute_url(self):
        return reverse('category', kwargs={'en_us_c': self.en_us})

    get_items.short_description = '文章数'
    icon_data.short_description = '图标预览'

    class Meta:
        verbose_name = '分类'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class Article(models.Model):
    """
    Article:文章
    """
    title = models.CharField(max_length=50, verbose_name='文章标题')
    en_us = models.CharField(max_length=50, blank=True, null=True, verbose_name='英文标题')
    desc = models.TextField(max_length=200, null=True, blank=True, verbose_name='文章描述')
    # cover = models.CharField(max_length=200, default='https://image.3001.net/images/20200304/15832956271308.jpg', verbose_name='文章封面')
    cover = models.FileField(upload_to='covers/', default='covers/1P629140610-3.jpg', verbose_name='文章封面')
    content = MDTextField(verbose_name='文章内容')  # 富文本编辑框,要在models中注册mdeditor
    is_md = models.BooleanField(default=True, verbose_name='是否转md')
    click_count = models.IntegerField(default=0, verbose_name='点击次数')
    upup = models.IntegerField(default=0, verbose_name='点赞次数', null=True, blank=True)
    comments = models.IntegerField(default=0, verbose_name='评论次数', null=True, blank=True)
    is_recommend = models.BooleanField(default=False, verbose_name='是否推荐')  # 置顶
    is_display = models.BooleanField(default=False, verbose_name='草稿')  # 置顶
    # TODO libo: 几条评论
    add_time = models.DateTimeField(default=datetime.now, verbose_name='发布时间')

    update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
    category = models.ForeignKey(Category, blank=True, null=True, verbose_name='文章分类', on_delete=models.CASCADE)
    tag = models.ManyToManyField(Tag, verbose_name='文章标签')



    def cover_data(self):
        return format_html(
            '<img src="{}" width="156px" height="98px"/>',
            self.cover,
        )

    def cover_admin(self):
        return format_html(
            '<img src="{}" width="440px" height="275px"/>',
            self.cover,
        )

    def upuped(self):
        """
        增加点赞数
        :return:
        """
        self.upup += 1
        self.save(update_fields=['upup'])

    def commented(self):
        """
        增加评论数
        :return:
        """
        self.comments += 1
        self.save(update_fields=['comments'])

    def viewed(self):
        """
        增加阅读数
        """
        self.click_count += 1
        self.save(update_fields=['click_count'])

    cover_data.short_description = '文章封面'
    cover_admin.short_description = '文章封面'

    def get_absolute_url(self):
        return reverse('detail', kwargs={'en_us': self.en_us})

    class Meta:
        verbose_name = '文章'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title


class Comment(models.Model):
    """
    文章评论
    """
    content = models.TextField(verbose_name='评论内容')
    username = models.CharField(max_length=30, verbose_name='用户名')
    add_time = models.DateTimeField(auto_now_add=True, verbose_name='发布时间')
    article = models.ForeignKey(Article, verbose_name='文章', on_delete=models.CASCADE)
    qq_email = models.CharField(max_length=100, verbose_name='qq邮箱')  # 应该关联到userinfo表中的email子段
    web_site = models.CharField(max_length=100, blank=True, null=True, verbose_name='网站')
    pid = models.ForeignKey('self', blank=True, null=True, verbose_name='父级评论', on_delete=models.CASCADE)

    class Meta:
        verbose_name = '评论'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.content[:20]


class Links(models.Model):
    """
    友情链接
    """
    title = models.CharField(max_length=50, verbose_name='标题')
    url = models.URLField(verbose_name='地址')
    desc = models.TextField(verbose_name='描述', max_length=250, null=True, blank=True)
    image = models.URLField(default='/media/avatas/head.jpg', verbose_name='头像')
    is_disply = models.BooleanField(default=False, null=True, blank=True)

    def avatar_data(self):
        return format_html(
            '<img src="{}" width="50px" height="50px" style="border-radius: 50%;" />',
            self.image,
        )

    def avatar_admin(self):
        return format_html(
            '<img src="{}" width="250px" height="250px"/>',
            self.image,
        )

    avatar_data.short_description = '头像'
    avatar_admin.short_description = '头像预览'

    class Meta:
        verbose_name = '友链'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.url


class Site(models.Model):
    """
    站点配置
    """
    desc = models.CharField(max_length=50, verbose_name='网站描述')
    keywords = models.CharField(max_length=50, verbose_name='网站关键词')
    title = models.CharField(max_length=50, verbose_name='网站标题')
    index_title = models.CharField(max_length=50, verbose_name='首页标题')
    type_chinese = models.CharField(max_length=50, verbose_name='座右铭汉语')
    type_english = models.CharField(max_length=80, verbose_name='座右铭英语')
    icp_number = models.CharField(max_length=20, verbose_name='备案号')
    icp_url = models.CharField(max_length=50, verbose_name='备案链接')
    site_mail = models.CharField(max_length=50, verbose_name='我的邮箱')
    site_qq = models.CharField(max_length=50, verbose_name='我的QQ')
    site_avatar = models.CharField(max_length=200, default='/avatars/head.jpg', verbose_name='我的头像')

    class Meta:
        verbose_name = '网站设置'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title


class File(models.Model):

    file = models.FileField(upload_to='covers/', default='covers/head.jpg')
    class Meta:
        verbose_name = '文件上传'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.file.name



class About(models.Model):
    me = MDTextField( verbose_name='关于博主')
    site = MDTextField( verbose_name='关于本站')
    promise = MDTextField( verbose_name='本站声明')
    class Meta:
        verbose_name = '关于我'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.me[:10]