• 基于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

  • 相关阅读:
    深入理解 Netty编码流程及WriteAndFlush()的实现
    深入理解 Netty-解码器架构与常用解码器
    暑假集训Day 5 P3963 [TJOI2013] 奖学金
    暑假集训日记Day xx
    P3226 [HNOI2012]集合选数 状压dp(思维题)
    线段树(毒瘤)总结
    P3622 [APIO2007]动物园
    暑假集训Day 4 P4163 [SCOI2007]排列 (状压dp)
    暑假集训Day2 互不侵犯(状压dp)
    暑假集训Day2 状压dp 特殊方格棋盘
  • 原文地址:https://www.cnblogs.com/huang-yc/p/9630644.html
Copyright © 2020-2023  润新知