基于Django的博客系统

目前本项目已经上线,可以直接在GEEK浏览本项目效果:http://blog.huangyongchi.com/

全新的项目源码地址:https://github.com/hyyc554/YcBlog

1.项目需求

  • 基于ajax和用户认证组件实现登录验证
  • 基于ajax和form组件实现注册功能
  • 系统首页文章列表的渲染
  • 个人站点页面设计
  • 文章详细页的继承
  • 点赞与踩灭
  • 评论功能
  • 富文本编辑器的使用
  • 防止xss攻击

2.项目详情

2.1 数据库设计

核心代码:

1.继承AbstractUser
2.中介模型
3.联合唯一

 


           

 

from django.db import models
​
# Create your models here.
​
​
from django.contrib.auth.models import AbstractUser
​
​
class UserInfo(AbstractUser):
    """
    用户信息
    """
    nid = models.AutoField(primary_key=True)
    telephone = models.CharField(max_length=11, null=True, unique=True)
    avatar = models.FileField(upload_to='avatars/', default="avatars/default.png")
    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
​
    blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.CASCADE)
​
    def __str__(self):
        return self.username
​
​
class Blog(models.Model):
    """
    博客信息
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='个人博客标题', max_length=64)
    site_name = models.CharField(verbose_name='站点名称', max_length=64)
    theme = models.CharField(verbose_name='博客主题', max_length=32)
​
    def __str__(self):
        return self.title
​
​
class Category(models.Model):
    """
    博主个人文章分类表
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='分类标题', max_length=32)
    blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE)
​
    def __str__(self):
        return self.title
​
​
class Tag(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='标签名称', max_length=32)
    blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE)
​
    def __str__(self):
        return self.title
​
​
class Article(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=50, verbose_name='文章标题')
    desc = models.CharField(max_length=255, verbose_name='文章描述')
    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
    content = models.TextField()
​
    comment_count = models.IntegerField(default=0)
    up_count = models.IntegerField(default=0)
    down_count = models.IntegerField(default=0)
​
    user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid', on_delete=models.CASCADE)
    category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE)
    tags = models.ManyToManyField(
        to="Tag",
        through='Article2Tag',
        through_fields=('article', 'tag'),
    )
​
    def __str__(self):
        return self.title
​
​
class Article2Tag(models.Model):
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid', on_delete=models.CASCADE)
    tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid', on_delete=models.CASCADE)
​
    class Meta:
        unique_together = [
            ('article', 'tag'),
        ]
​
    def __str__(self):
        v = self.article.title + "---" + self.tag.title
        return v
​
​
class ArticleUpDown(models.Model):
    """
    点赞表
    """
​
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey('UserInfo', null=True, on_delete=models.CASCADE)
    article = models.ForeignKey("Article", null=True, on_delete=models.CASCADE)
    is_up = models.BooleanField(default=True)
​
    class Meta:
        unique_together = [
            ('article', 'user'),
        ]
​
​
class Comment(models.Model):
    """
​
    评论表
​
    """
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.CASCADE)
    user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.CASCADE)
    content = models.CharField(verbose_name='评论内容', max_length=255)
    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
    parent_comment = models.ForeignKey('self', null=True, on_delete=models.CASCADE)
​
    def __str__(self):
        return self.content
​
View Code

 

2.2 站点管理

核心代码:

from django.contrib import admin
​
# Register your models here.
from blog import models
​
admin.site.register(models.UserInfo)
admin.site.register(models.Article)
admin.site.register(models.Article2Tag)
admin.site.register(models.ArticleUpDown)
admin.site.register(models.Blog)
admin.site.register(models.Category)
admin.site.register(models.Comment)
admin.site.register(models.Tag)
View Code
 

2.3 注册

核心代码:

1.form组件

1.form组件
    class RegForm(forms.Form):pass
    局部钩子 全局钩子
​
2.上传头像 avatar
    图像预览
        var reader = new FileReader();
    上传文件
        formdata = new FormData();
​
3.用户文件配置
    avatar = models.FileField(upload_to='avatars/', default='avatars/default.png')
    MEDIA_ROOT = os.path.join(BASE_DIR, 'blog', 'media')
    MEDIA_URL = '/media/'
    re_path(r'media/(?P<path>.*)$',serve,{'document_root':settings.MEDIA_ROOT})

 


 

2.4 登录

核心代码:

1.验证码
    随机生成5个字符,0-9 a-z A-Z
​
    pip install pillow
    from PIL import Image, ImageDraw, ImageFont
        image = Image.new()
​
    在内存中生成图片直接返回
    from io import BytesIO
        f = BytesIO()
​
2.request.session['valid_str'] = valid_str
    存在session中,为了之后登录,验证是否通过
​
3.验证码点击刷新:
    $('#valid_img').click(function () {
        $(this)[0].src += '?'
    });
​
4.认证组件
    valid_str = request.session.get('valid_str')
    if valid_str.upper() == valid_code.upper():
        user = auth.authenticate(username = user, password = pwd)
        if user:
            auth.login(request, user)

 

 
 

2.5 网站首页

核心代码:

1.bootstrap搭建页面
​
2.导航条
    登录:   username / 注销
    未登录: 登录 / 注册
​
3.for循环
     {% for article in article_list %}
     {% endfor %}

 


网页底部分页器:

2.6 个人站点

1.文章列表,分类列表,标签列表,日期归档列表
    文章列表:     /blog/egon/
    分类列表:     /blog/egon/cate/python
    标签列表:     /blog/egon/tag/生活
    日期归档列表: /blog/egon/archive/2018-062.模板继承
    {% extends 'base.html' %}
​
    {% block content %}
    {% endblock content%}}
​
3.自定义标签
    /blog/templatetags/my_tag.py
​
    @register.inclusion_tag('menu.html')
    def get_menu(username):
        ...
        return {} # 去渲染 menu.html
4.分组查询 .annotate() / extra()应用
    多表分组
        tag_list = Tag.objects.filter(blog=blog).annotate(
            count = Count('article')).values_list('title', 'count')
​
    单表分组 / DATE_FORMAT() /  extra()
        date_list = Article.objects.filter(user=user).extra(
            select={"create_ym": "DATE_FORMAT(create_time,'%%Y-%%m')"}).values('create_ym').annotate(
            c = Count('nid')).values_list('create_ym', 'c')
​
5. 时间、区域配置
     TIME_ZONE = 'Asia/Shanghai'
     USE_TZ = False

 



2.7 文章详情

核心代码:

1.模板继承
    article = Article.objects.filter(pk=article_id).first()
    {% extends 'base.html' %}
    {% block content %}
         ...
        {{ article.articledetail.content|safe }}
    {% endblock content %}

 



 2.8 后台管理

核心代码:

1.支持文章编辑
2.支持富文本编辑器(支持渲染已有文章)
3.支持删除文章
4.防止Xss攻击(基于BS4)
 # 防止xss攻击,过滤script标签
        soup = BeautifulSoup(content, "html.parser")
        for tag in soup.find_all():
​
            if tag.name == "script":
                tag.decompose()

 

 

2.9 批量建立测试数据

核心代码:

1.定制建立测试数据
2.初始化得到100条用户信息,以及为100个客户分别添加文章、博客的详细信息

 

 
def supreme(request):
    """
    这是数据初始化视图,将会生成99条测试数据,用户名为管理员输入的字符后,加1_99.
    密码为管理员填入的密码
    :param request:
    :return:
    """
    tag_title = ['BASIC', 'C',
                 'C++', 'PASCAL', 'FORTRAN', 'LISP', 'Prolog', 'CLIPS', 'OpenCyc', 'Fazzy', 'Python', 'PHP', 'Ruby',
                 'Lua']
    category_titles = ['非技术区',
                       '软件测试',
                       '代码与软件发布',
                       '计算机图形学',
                       '游戏开发',
                       '程序人生',
                       '求职面试',
                       '读书区',
                       '转载区',
                       'Windows',
                       '翻译区',
                       '开源研究',
                       'Flex']
    userlist = []
    bolglist = []
    article_list = []
    tag_list = []
    cat_list = []
    article2tag_list = []
​
    if request.is_ajax():
​
        form = UserForm(request.POST)
        response = {'user': None, 'msg': None}
        if form.is_valid():
​
            response['user'] = form.cleaned_data.get('user')
            # 生成一条用户记录
            user = form.cleaned_data.get('user')
​
            pwd = form.cleaned_data.get('pwd')
            email = form.cleaned_data.get('email')
            avatar_obj = request.FILES.get('avatar')
            extra = {}
            if avatar_obj:
                extra['avatar'] = avatar_obj
                with transaction.atomic():
                    for i in range(1, 100):
                        Blog.objects.create(title='blog%s' % i)
​
                        UserInfo.objects.create_user(username=user + str(i), password=pwd, email=email, **extra)
​
                        UserInfo.objects.filter(username=user + str(i)).update(blog_id=i)
​
                        for title in tag_title:
                            tag_list.append(Tag(title=title, blog_id=i))
​
                        for cat in category_titles:
                            cat_list.append(Category(title=cat, blog_id=i))
                        import random
                        for c in range(1, 7):
                            with open('/home/hyc/PycharmProjects/cnblog/static/superme/%s.txt' % c, 'r',
                                      encoding='utf-8') as f:
                                content = f.read()
​
                            soup = BeautifulSoup(content, "html.parser")
                            for tag in soup.find_all():
                                if tag.name == "script":
                                    tag.decompose()
                            desc = soup.text[0:150] + "..."
                            c_id = 13 * (i - 1) + random.randrange(1, 9)
​
                            article_list.append(
                                Article(desc=desc, title='article%s' % c, content=content, user_id=i, category_id=c_id))
​
                    Category.objects.bulk_create(cat_list)
                    Tag.objects.bulk_create(tag_list)
                    Article.objects.bulk_create(article_list)
​
        else:
            response['msg'] = form.errors
        return JsonResponse(response)
​
    my_from = UserForm
​
    return render(request, 'supreme.html', {'from': my_from})
View Code


3.源码链接: 

https://github.com/hyyc554/BBS_blog

原文地址:https://www.cnblogs.com/huang-yc/p/9630644.html