zoukankan      html  css  js  c++  java
  • 项目实战:BBS+Blog项目开发

    01-博客系统之功能需求

    02-博客系统之表结构设计1

    03-博客系统之表结构设计2

    04-博客系统之表结构设计3

    05-博客系统之表结构设计4

    06-博客系统之表机构设计5

    07-博客系统之创建系统与迁移表

    08-博客系统之登录页面设计

    09-博客系统之验证码图片的生成1

    10-博客系统之验证码图片的生成2

    11-博客系统之验证码图片的噪点燥线

    12-博客系统之验证码刷新

    13-博客系统之保存验证码字符串

    14-博客系统之登录验证

    15-博客系统之登录验证代码优化

    16-博客系统之滑动验证码作业

    17-博客系统之基于form组件的注册页面设计1

    18-博客系统之注册页面的默认头像

    19-博客系统之注册页面的头像预览功能

    20-博客系统之基于Ajax的form data数据

    21-博客系统之基于Ajax提交from data的数据的优化

    22-博客系统之基于Ajax在注册页面显示错误消息1

    23-博客系统之基于Ajax在注册页面显示错误消息2

    24-博客系统之form组件的局部钩子与全局钩子的应用

    25-博客系统之FileFiled字段

    26-博客系统之media配置1

    27-博客系统之media配置2

    28-博客系统之生成用户对象的代码优化

    29-博客系统之系统首页的导航区域

    30-博客系统之系统首页的主体布局

    31-博客系统之admin的简单实用

    32-博客系统之基于admin录入文章数据

    33-博客系统之系统首页的文章列表的渲染1

    34-博客系统之系统首页的文章列表的渲染2

    35-博客系统之个人站点的页面文章的查询

    36-博客系统之个人站点页面的标签与分类查询

    37-博客系统之个人站点页面的日期查询1

    38-博客系统之个人站点页面的日期查询2

    39-博客系统之个人站点页面的日期查询3

    40-博客系统之个人站点页面的日期查询4

    41-博客系统之个人站点页面的渲染布局1

    42-博客系统之个人站点页面的渲染布局2

    43-博客系统之个人站点页面的渲染布局3

    44-博客系统之个人站点的跳转过滤功能的实现1

    45-博客系统之个人站点页面的跳转过滤功能实现2

    46-博客系统之个人站点页面的跳转过滤功能的实现3

    47-博客系统之文章详情页的设计

    48-博客系统之文章详情页的构建

    49-博客系统之文章详情页的inclution_tag

    50-博客系统之文章详情页渲染标签的标签字符串转义1

    51-博客系统之文章详情页渲染的标签字符串转义2

    52-博客系统之文章点赞样式的构建

    53-博客系统之文章点赞事件的绑定

    54-博客系统之文章点赞的保存

    55-博客系统之文章点赞数的数据同步

    56-博客系统之文章点赞的提示重复操作

    57-博客系统之文章点赞数的Ajax更新

    58-博客系统之文章点赞代码的优化

    59-博客系统之评论功能的实现流程

    60-博客系统之评论样式

    61-博客系统之提交根评论

    62-博客系统之render显示根评论

    63-博客系统之Ajax显示根评论

    64-博客系统之回复按钮事件

    65-博客系统之提交子评论

    66-博客系统之render显示

    67-博客系统之Ajax显示子评论的思路

    68-博客系统之评论树简介

    69-博客系统之评论树的请求数据

    70-博客系统之展开评论树

    71-博客系统之展开评论树2

    72-博客系统之评论树的思考1

    73-博客系统之评论树的思考2

    74-博客系统之评论事务操作

    75-博客系统之评论的邮件发送new

    76-博客系统之后台管理页面的编辑功能

    77-博客系统之后台管理的编辑器引入和参数

    78-博客系统之文本编辑器的上传功能1

    79-博客系统之文本编辑器的上传功能2

    80-博客系统之文章摘要的保存

    81-博客系统之bs4的简单应用

    82-博客系统之bs4模块防御xss攻击

    01-博客系统之功能需求

    1、项目开发流程;

    • 1、搞清楚需求(与产品经理对接);
    • 2、设计表结构;
    • 3、按照每一个功能(需求)进行开发;

    2、什么是博客?

    • 博客园;
    • CSDN;
    • 微信朋友圈;
    • 新浪博客;
    • 新浪微博;

    3、基础功能分析;

    • 1、登录-基于用户认证组件和Ajax实现登录验证(图片验证码);
    • 2、注册-基于Ajax和form组件实现;
    • 3、设计系统首页(文章列表的渲染);
    • 4、设计个人站点页面;
    • 5、文章详情页;
    • 6、实现点赞功能(基于Ajax);
    • 7、实现文章评论功能(对子评论的评论);
    • 8、后台管理页面;
    • 9、富文本编辑框和防止XSS攻击;

    4、功能测试;

    5、项目部署上线;

    02-博客系统之表结构设计1

    03-博客系统之表结构设计2

    04-博客系统之表结构设计3

    05-博客系统之表结构设计4

    06-博客系统之表机构设计5

    07-博客系统之创建系统与迁移表

    1、注释默认的sqllite数据库,修改为MySQL数据库;

    2、编写models.py;

    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

    3、settingsp.py添加AUTH_USER_MODEL=“blog.UserInfo”;

    """
    Django settings for cnblogs project.
    
    Generated by 'django-admin startproject' using Django 2.1.
    
    For more information on this file, see
    https://docs.djangoproject.com/en/2.1/topics/settings/
    
    For the full list of settings and their values, see
    https://docs.djangoproject.com/en/2.1/ref/settings/
    """
    
    import os
    
    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    
    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
    
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = '97e0#&=bl*0zl@99!_#4o*6fs=e&3-6@8rdq0clas*hojx6!5z'
    
    # SECURITY WARNING: don't run with debug turned on in production!
    DEBUG = True
    
    ALLOWED_HOSTS = []
    
    
    # Application definition
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'blog.apps.BlogConfig',
    ]
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
    
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    ROOT_URLCONF = 'cnblogs.urls'
    
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')]
            ,
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    WSGI_APPLICATION = 'cnblogs.wsgi.application'
    
    
    # Database
    # https://docs.djangoproject.com/en/2.1/ref/settings/#databases
    
    # DATABASES = {
    #     'default': {
    #         'ENGINE': 'django.db.backends.sqlite3',
    #         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    #     }
    # }
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',#数据库的引擎为MySQL;
            'NAME': 'cnblogs',#要连接的数据库实例的名称,连接前需要已经完成创建;
            'USER': 'root',#MySQL数据库的用户名;
            'PASSWORD': 'Tqtl911!@%*)',#MySQL数据库的密码;
            'HOST': '47.95.121.154',#MySQL数据库服务器的IP地址;
            'PORT': '3306'#MySQL数据库的款口号,默认3306;
        }
    }
    
    
    # Password validation
    # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
    
    AUTH_PASSWORD_VALIDATORS = [
        {
            'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
        },
    ]
    
    
    # Internationalization
    # https://docs.djangoproject.com/en/2.1/topics/i18n/
    
    LANGUAGE_CODE = 'en-us'
    
    TIME_ZONE = 'UTC'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = True
    
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/2.1/howto/static-files/
    
    STATIC_URL = '/static/'
    
    AUTH_USER_MODEL = 'blog.UserInfo'
    
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'handlers': {
            'console':{
                'level':'DEBUG',
                'class':'logging.StreamHandler',
            },
        },
        'loggers': {
            'django.db.backends': {
                'handlers': ['console'],
                'propagate': True,
                'level':'DEBUG',
            },
        }
    }

    4、进行数据库迁移操作;

    python manage.py makemigrations
    python manage.py migrate

    08-博客系统之登录页面设计

    1、登录页面设计;

    2、login.html设计之引入本地Bootstrap静态文件;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
    </head>
    <body>
    <h3>登录页面</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-offset-3">
                <form action="">
                    <div class="form-group">
                        <label for="user">用户名</label>
                        <input type="text" id="user" class="form-control">
                    </div>
                    <div class="form-group">
                        <label for="pwd">密码</label>
                        <input type="password" id="pwd" class="form-control">
                    </div >
                    <input type="button" class="btn btn-default pull-right login_btn" value="submit">
                </form>
            </div>
        </div>
    </div>
    </body>
    </html>

    3、views.py;

    from django.shortcuts import render
    
    # Create your views here.
    
    def login(request):
        return render(request,"login.html")

    4、settings.py之配置STATICFILE_DIRS目录;

    STATICFILES_DIRS = [
        os.path.join(BASE_DIR,"static")
    ]

    09-博客系统之验证码图片的生成1

    1、PIL图像处理模块初识;

      PIL:Python Imaging Library,已经是Python平台事实上的图像处理标准库了,PIL功能非常强大,且API非常简单易用。

      由于PIL仅支持到Python 2.7,加上年久失修,于是一群志愿者在PIL的基础上创建了兼容的版本,名字叫Pillow,支持最新Python 3.x,又加入了许多新特性,因此,我们可以直接安装使用Pillow。

     pip install pillow

    详情请见:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014320027235877860c87af5544f25a8deeb55141d60c5000

    2、打开图片的两种方式;

    from django.shortcuts import render,HttpResponse,redirect
    
    # Create your views here.
    
    def login(request):
        return render(request,"login.html")
    
    
    import random
    def get_validCode_img(request):
        def get_random_color():
            return (random.randint(0,255),random.randint(0,255),random.randint(0,255))
        #方式1-with open方法;
        # with open("lufei.jpg","rb") as f:
        #     data = f.read()
    
        #方式2;pip install pillow;
        from PIL import Image
        img = Image.new("RGB",(270,40),color=get_random_color())
        with open("validCode.png","wb") as f:
            img.save(f,"png")
        with open("validCode.png","rb") as f:
            data = f.read()
        return HttpResponse(data)

    3、login.html;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
    </head>
    <body>
    <h3>登录页面</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-offset-3">
                <form action="">
                    {% csrf_token %}
                    <div class="form-group">
                        <label for="user">用户名</label>
                        <input type="text" id="user" class="form-control">
                    </div>
                    <div class="form-group">
                        <label for="pwd">密码</label>
                        <input type="password" id="pwd" class="form-control">
                    </div >
                    <div class="form-group">
                        <label for="pwd">验证码</label>
                        <div class="row">
                            <div class="col-md-6">
                                <input type="text" class="valid_code form-control">
                            </div>
                            <div class="col-md-6"></div>
                            <img  width="260" height="45" src="/get_validCode_img/" alt="">
                        </div>
                    </div>
                    <input type="button" class="btn btn-default pull-right login_btn" value="submit">
                </form>
            </div>
        </div>
    </div>
    </body>
    </html>

    10-博客系统之验证码图片的生成2

    1、生成随机字符串的两种方法;

    2、引入string、ImageDraw、ImageFont模块;

    from django.shortcuts import render,HttpResponse,redirect
    
    # Create your views here.
    
    def login(request):
        return render(request,"login.html")
    
    
    import random
    def get_validCode_img(request):
        def get_random_color():
            return (random.randint(0,255),random.randint(0,255),random.randint(0,255))
        #方式1-with open方法;
        # with open("lufei.jpg","rb") as f:
        #     data = f.read()
    
        #方式2;pip install pillow;
        # from PIL import Image
        # img = Image.new("RGB",(270,40),color=get_random_color())
        # with open("validCode.png","wb") as f:
        #     img.save(f,"png")
        # with open("validCode.png","rb") as f:
        #     data = f.read()
    
        #方式3:将数据放置于内存中,加快处理速度;
        # from PIL import Image
        # from io import BytesIO
        #
        # img = Image.new("RGB",(270,40),color=get_random_color())
        # f = BytesIO()
        # img.save(f,"png")
        # data = f.getvalue()
    
        #方式4:向图像区域他添加噪点,和字符串;
        from PIL import Image,ImageDraw,ImageFont
        from io import BytesIO
        import random
        char = str(random.randint(0,9))
    
        img = Image.new("RGB",(270,40),color=get_random_color())
        draw = ImageDraw.Draw(img)
        kumo_font = ImageFont.truetype("static/font/kumo.ttf",size=28)
    
        #生成随机字符串;
        #方法1:
        # for i in range(0,5):
        #     import string
        #     random_char = '  '.join(random.sample(string.ascii_lowercase  + string.ascii_uppercase,1))  # d4}5c+/m|97e@"16]s
        #     draw.text((i*50+20,5),random_char,get_random_color(),font=kumo_font)
    
        #方法2:
        for i in range(5):
            random_num = str(random.randint(0,9))
            random_lowercase = chr(random.randint(95,122))
            random_uppercase = chr(random.randint(65,90))
            random_char = random.choice([random_num,random_lowercase,random_uppercase])
            draw.text((i*50+20,5),random_char,get_random_color(),font=kumo_font)
    
        #进行画图
        #draw.line()
        #draw.point()
        f = BytesIO()
        img.save(f,"png")
        data = f.getvalue()
    
        return HttpResponse(data)

    11-博客系统之验证码图片的噪点燥线

    1、添加噪点、燥线;

        #给图片添加上噪点;
        width = 270
        height = 40
        for i in range(5):
            x1 = random.randint(0,width)
            x2 = random.randint(0,width)
            y1 = random.randint(0,height)
            y2 = random.randint(0,height)
            draw.line((x1,y1,x2,y2),fill=get_random_color())
    
        for i in range(10):
            draw.point([random.randint(0,width),random.randint(0,height)],fill=get_random_color())
            x = random.randint(0,width)
            y = random.randint(0,height)
            draw.arc((x,y,x+4,y+4),0,90,fill=get_random_color())

    2、展示效果;

    12-博客系统之验证码刷新

    1、先引入jQuery文件;

    2、添加鼠标点击事件click();

    3、给图片添加一个id属性,然后通过js代码添加事件;

    <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
    <script>
        //刷新图片验证码;
        $("#valid_code_img").click(function () {
            $(this)[0].src+="?"
        })
    </script>
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
    </head>
    <body>
    <h3>登录页面</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-offset-3">
                <form action="">
                    {% csrf_token %}
                    <div class="form-group">
                        <label for="user">用户名</label>
                        <input type="text" id="user" class="form-control">
                    </div>
                    <div class="form-group">
                        <label for="pwd">密码</label>
                        <input type="password" id="pwd" class="form-control">
                    </div >
                    <div class="form-group">
                        <label for="pwd">验证码</label>
                        <div class="row">
                            <div class="col-md-6">
                                <input type="text" class="valid_code form-control">
                            </div>
                            <div class="col-md-6">
                            <img  width="260" height="45" id="valid_code_img" src="/get_validCode_img/" alt="">
                  </div> </div> </div> <input type="button" class="btn btn-default pull-right login_btn" value="submit"> </form> </div> </div> </div> <script src="/static/blog/js/jquery-3.3.1.min.js"></script> <script> //刷新图片验证码; $("#valid_code_img").click(function () { $(this)[0].src+="?" }) </script> </body> </html>

    13-博客系统之保存验证码字符串

    1、保存验证字符串之Ajax引入;

    2、views.py;

    from django.shortcuts import render,HttpResponse,redirect
    
    # Create your views here.
    
    from django.http import JsonResponse
    
    def login(request):
        if request.method == "POST":
            response = {"user":None,"msg":None}
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
            valid_code  = request.POST.get("valid_code")
    
            valid_code_str = request.session.get("valid_code_str")
            #print("valid_code_str",valid_code_str,type(valid_code_str))
            #print("valid_code",valid_code,type(valid_code))
            if valid_code.upper() == valid_code_str.upper():
                pass
            else:
                response["msg"] = "valid code error!"
            return JsonResponse(response)
        return render(request,"login.html")
    
    
    import random
    def get_validCode_img(request):
        def get_random_color():
            return (random.randint(0,255),random.randint(0,255),random.randint(0,255))
        #方式1-with open方法;
        # with open("lufei.jpg","rb") as f:
        #     data = f.read()
    
        #方式2;pip install pillow;
        # from PIL import Image
        # img = Image.new("RGB",(270,40),color=get_random_color())
        # with open("validCode.png","wb") as f:
        #     img.save(f,"png")
        # with open("validCode.png","rb") as f:
        #     data = f.read()
    
        #方式3:将数据放置于内存中,加快处理速度;
        # from PIL import Image
        # from io import BytesIO
        #
        # img = Image.new("RGB",(270,40),color=get_random_color())
        # f = BytesIO()
        # img.save(f,"png")
        # data = f.getvalue()
    
        #方式4-向图像区域他添加噪点,和字符串;
        from PIL import Image,ImageDraw,ImageFont
        from io import BytesIO
        import random
        char = str(random.randint(0,9))
    
        img = Image.new("RGB",(270,40),color=get_random_color())
        draw = ImageDraw.Draw(img)
        kumo_font = ImageFont.truetype("static/font/BASKVILL.TTF",size=28)
    
        #保存随机字符串;
        valid_code_str = ""
        #生成随机字符串;
        #方法1:
        for i in range(0,5):
            import string
            random_char = '  '.join(random.sample(string.ascii_lowercase  + string.ascii_uppercase,1))  # d4}5c+/m|97e@"16]s
            draw.text((i*50+20,5),random_char,get_random_color(),font=kumo_font)
            #保存验证码字符串;
            valid_code_str+= random_char
    
        #方法2:
        # for i in range(500):
        #     random_num = str(random.randint(0,9))
        #     random_lowercase = chr(random.randint(95,122))
        #     random_uppercase = chr(random.randint(65,90))
        #     random_char = random.choice([random_num,random_lowercase,random_uppercase])
        #     draw.text((i*50+20,5),random_char,get_random_color(),font=kumo_font)
    
        #进行画图;
        #draw.line()
        #draw.point()
    
    
        #给图片添加上噪点;
        width = 270
        height = 40
        for i in range(5):
            x1 = random.randint(0,width)
            x2 = random.randint(0,width)
            y1 = random.randint(0,height)
            y2 = random.randint(0,height)
            draw.line((x1,y1,x2,y2),fill=get_random_color())
    
        for i in range(10):
            draw.point([random.randint(0,width),random.randint(0,height)],fill=get_random_color())
            x = random.randint(0,width)
            y = random.randint(0,height)
            draw.arc((x,y,x+4,y+4),0,90,fill=get_random_color())
        print("valid_code_str",valid_code_str)
    
        request.session["valid_code_str"] = valid_code_str
        '''
        1、生成随机字符串;
        2、COOKIE{"sessionid":fdsfdsfds}
        3、django-session表生成记录;
        '''
        f = BytesIO()
        img.save(f, "png")
        data = f.getvalue()
        return HttpResponse(data)

    login.html;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
    </head>
    <body>
    <h3>登录页面</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-offset-3">
                <form >
                    {% csrf_token %}
                    <div class="form-group">
                        <label for="user">用户名</label>
                        <input type="text" id="user" class="form-control">
                    </div>
                    <div class="form-group">
                        <label for="pwd">密码</label>
                        <input type="password" id="pwd" class="form-control">
                    </div >
                    <div class="form-group">
                        <label for="pwd">验证码</label>
                        <div class="row">
                            <div class="col-md-6">
                                <input type="text" class="form-control" id="valid_code">
                            </div>
                            <div class="col-md-6">
                                <img  width="260" height="45" id="valid_code_img" src="/get_validCode_img/" alt="">
                            </div>
                        </div>
                    </div>
                    <input type="button" class="btn btn-default pull-right login_btn" value="submit">
                </form>
            </div>
        </div>
    </div>
    <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
    <script>
        //刷新图片验证码;
        $("#valid_code_img").click(function () {
            $(this)[0].src+="?"
        });
    
        //登陆过验证;
        $(".login_btn").click(function () {
            $.ajax({
                url:"",
                type:"post",
                data:{
                    user:$("#user").val(),
                    pwd:$("#pwd").val(),
                    valid_code:$("#valid_code").val(),
                    csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(),
    
                },
                success:function (data) {
                    console.log(data)
    
                }
            })
        })
    
    
    </script>
    
    
    </body>
    </html>

    14-博客系统之登录验证

    1、修改消息提示语;

    15-博客系统之登录验证代码优化

    1、新增utils目录,将功能迁移至此;

    views.py;

    from django.shortcuts import render,HttpResponse,redirect
    
    # Create your views here.
    
    from django.http import JsonResponse
    from django.contrib import auth
    
    def login(request):
        if request.method == "POST":
            response = {"user":None,"msg":None}
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
            valid_code  = request.POST.get("valid_code")
    
            valid_code_str = request.session.get("valid_code_str")
            #print("valid_code_str",valid_code_str,type(valid_code_str))
            #print("valid_code",valid_code,type(valid_code))
            if valid_code.upper() == valid_code_str.upper():
                user = auth.authenticate(username = user,password = pwd)
                if user:
                    response["user"] = user.username
                else:
                    response["msg"] = "用户名或者密码错误!"
            else:
                response["msg"] = "验证码错误!"
            return JsonResponse(response)
        return render(request,"login.html")
    
    
    def get_valid_code_img(request):
        """
        基于PIL模块动态生成响应状态码图片;
        :param request:
        :return:
        """
        from blog.utils.validCode import get_valid_code_img
        data = get_valid_code_img(request)
        return HttpResponse(data)
    
    
    def index(request):
        return render(request,"index.html")
    
    """
    小结:
    1、一次请求伴随着多次请求;
    2、PIL模块的掌握;
    3、session存储;
    4、验证码刷新,基于js鼠标的click()事件进行;
    """

    validCode.py;

    #!/usr/bin/env python3
    # -*- coding:utf-8 -*-
    # __Author__:TQTL911
    # Version:python3.6.6
    # Time:2018/8/23 21:30
    
    import random
    
    def get_random_color():
        return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
    
    
    def get_valid_code_img(request):
        # 方式1-with open方法;
        # with open("lufei.jpg","rb") as f:
        #     data = f.read()
    
        # 方式2;pip install pillow;
        # from PIL import Image
        # img = Image.new("RGB",(270,40),color=get_random_color())
        # with open("validCode.png","wb") as f:
        #     img.save(f,"png")
        # with open("validCode.png","rb") as f:
        #     data = f.read()
    
        # 方式3:将数据放置于内存中,加快处理速度;
        # from PIL import Image
        # from io import BytesIO
        #
        # img = Image.new("RGB",(270,40),color=get_random_color())
        # f = BytesIO()
        # img.save(f,"png")
        # data = f.getvalue()
    
        # 方式4-向图像区域他添加噪点,和字符串;
        from PIL import Image, ImageDraw, ImageFont
        from io import BytesIO
        import random
        char = str(random.randint(0, 9))
    
        img = Image.new("RGB", (270, 40), color=get_random_color())
        draw = ImageDraw.Draw(img)
        kumo_font = ImageFont.truetype("static/font/BASKVILL.TTF", size=28)
    
        # 保存随机字符串;
        valid_code_str = ""
        # 生成随机字符串;
        # 方法1:
        for i in range(0, 5):
            import string
            random_char = '  '.join(
                random.sample(string.ascii_lowercase + string.ascii_uppercase, 1))  # d4}5c+/m|97e@"16]s
            draw.text((i * 50 + 20, 5), random_char, get_random_color(), font=kumo_font)
            # 保存验证码字符串;
            valid_code_str += random_char
    
        # 方法2:
        # for i in range(500):
        #     random_num = str(random.randint(0,9))
        #     random_lowercase = chr(random.randint(95,122))
        #     random_uppercase = chr(random.randint(65,90))
        #     random_char = random.choice([random_num,random_lowercase,random_uppercase])
        #     draw.text((i*50+20,5),random_char,get_random_color(),font=kumo_font)
    
        # 进行画图;
        # draw.line()
        # draw.point()
    
        # 给图片添加上噪点;
        width = 270
        height = 40
        for i in range(5):
            x1 = random.randint(0, width)
            x2 = random.randint(0, width)
            y1 = random.randint(0, height)
            y2 = random.randint(0, height)
            draw.line((x1, y1, x2, y2), fill=get_random_color())
    
        for i in range(10):
            draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color())
            x = random.randint(0, width)
            y = random.randint(0, height)
            draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color())
        print("valid_code_str", valid_code_str)
    
        request.session["valid_code_str"] = valid_code_str
        '''
        1、生成随机字符串;
        2、COOKIE{"sessionid":fdsfdsfds};
        3、django-session表生成记录;
        '''
        f = BytesIO()
        img.save(f, "png")
        data = f.getvalue()
        return data

    2、新增图片验证码1秒刷新功能,基于js的setTimeout()方法实现;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
    </head>
    <body>
    <h3>登录页面</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-offset-3">
                <form >
                    {% csrf_token %}
                    <div class="form-group">
                        <label for="user">用户名</label>
                        <input type="text" id="user" class="form-control">
                    </div>
                    <div class="form-group">
                        <label for="pwd">密码</label>
                        <input type="password" id="pwd" class="form-control">
                    </div >
                    <div class="form-group">
                        <label for="pwd">验证码</label>
                        <div class="row">
                            <div class="col-md-6">
                                <input type="text" class="form-control" id="valid_code">
                            </div>
                            <div class="col-md-6">
                                <img  width="260" height="45" id="valid_code_img" src="/get_validCode_img/" alt="">
                            </div>
                        </div>
                    </div>
                    <input type="button" class="btn btn-default login_btn" value="submit"><span class="error"></span>
                </form>
            </div>
        </div>
    </div>
    <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
    <script>
        //刷新图片验证码;
        $("#valid_code_img").click(function () {
            $(this)[0].src+="?"
        });
    
        //登陆过验证;
        $(".login_btn").click(function () {
            $.ajax({
                url:"",
                type:"post",
                data:{
                    user:$("#user").val(),
                    pwd:$("#pwd").val(),
                    valid_code:$("#valid_code").val(),
                    csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(),
    
                },
                success:function (data) {
                    console.log(data);
                    if (data.user){
                        location.href = "/index/"
                    }else {
                        $(".error").text(data.msg).css({"color":"red","margin-left":"10px"});
                        //清空错误消息提示,1000毫秒;
                        setTimeout(function () {
                            $(".error").text("")
                        },1000)
                    }
                }
            })
        });
    
    
    </script>
    
    
    </body>
    </html>

    16-博客系统之滑动验证码作业

    极验官网:http://www.geetest.com/

    17-博客系统之基于form组件的注册页面设计1

    1、注册页面的路由配置urls.py;

    """cnblogs URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/2.1/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  path('', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.urls import include, path
        2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
    """
    from django.contrib import admin
    from django.urls import path
    
    from blog import views
    urlpatterns = {
        path('admin/', admin.site.urls),
        path('login/', views.login),
        path('index/', views.index),
        path('get_validCode_img/', views.get_valid_code_img),
        path('register/', views.register),
    }

    2、注册页面的views.py视图函数编写;

    from django.shortcuts import render,HttpResponse,redirect
    
    # Create your views here.
    
    from django.http import JsonResponse
    from django.contrib import auth
    
    def login(request):
        """
        登录;
        :param request:
        :return:
        """
        if request.method == "POST":
            response = {"user":None,"msg":None}
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
            valid_code  = request.POST.get("valid_code")
    
            valid_code_str = request.session.get("valid_code_str")
            if valid_code.upper() == valid_code_str.upper():
                user = auth.authenticate(username = user,password = pwd)
                if user:
                    response["user"] = user.username
                else:
                    response["msg"] = "用户名或者密码错误!"
            else:
                response["msg"] = "验证码错误!"
            return JsonResponse(response)
        return render(request,"login.html")
    
    
    def get_valid_code_img(request):
        """
        基于PIL模块动态生成响应状态码图片;
        :param request:
        :return:
        """
        from blog.utils.validCode import get_valid_code_img
        data = get_valid_code_img(request)
        return HttpResponse(data)
    
    
    def index(request):
        """
        首页;
        :param request:
        :return:
        """
        return render(request,"index.html")
    
    
    from django import forms
    from django.forms import widgets
    
    class UserForm(forms.Form):
        user = forms.CharField(max_length=32,label="用户名",widget=widgets.TextInput(attrs={"class":"form-control"}))
        pwd = forms.CharField(max_length=32,label="密码",widget=widgets.PasswordInput(attrs={"class":"form-control"}))
        r_pwd = forms.CharField(max_length=32,label="确认密码",widget=widgets.PasswordInput(attrs={"class":"form-control"}))
        email = forms.EmailField(max_length=32,label="注册邮箱",widget=widgets.EmailInput(attrs={"class":"form-control"}))
    
    def register(request):
        """
        注册;
        :param request:
        :return:
        """
        form = UserForm()
        return render(request,"register.html",{"form":form})

    3、注册页面的模板templates下的register.html编写(基于forms组件循环生成并添加约束信息);

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册页面</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
    </head>
    <body>
    <h3>注册页面</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-offset-3">
                <form >
                    {% csrf_token %}
                    <!--进行渲染form对象-->
                    {% for field in form %}
                        <div class="form-group">
                            <label for="user">{{ field.label }}</label>
                            {{ field }}
                        </div>
                    {% endfor %}
                    <div class="form-group">
                        <label for="avatar">头像</label>
                        <input type="file">
                    </div>
    
                    <input type="button" class="btn btn-default login_btn" value="提交">
                </form>
            </div>
        </div>
    </div>
    <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
    
    </body>
    </html>

    18-博客系统之注册页面的默认头像

    1、点击头像相当于点击input标签的功能实现;

    • 1)将label标签包含img标签;
    • 2)将label标签的for值等同于input标签的id值;
    • 3)为input标签设置display:none的隐藏属性;

    2、register.html;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册页面</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
        <style>
            #avatar{
                display: none;
            }
            #avatar_img{
                margin-left: 20px;
            }
        </style>
    </head>
    <body>
    <h3>注册页面</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-offset-3">
                <form >
                    {% csrf_token %}
                    <!--进行渲染form对象-->
                    {% for field in form %}
                        <div class="form-group">
                            <label for="{{ field.auto_id }}">{{ field.label }}</label>
                            {{ field }}
                        </div>
                    {% endfor %}
                    <div class="form-group">
                        <label for="avatar">
                            头像
                            <img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png" alt="">
                        </label>
                        <input type="file" id="avatar" >
                    </div>
                    <input type="button" class="btn btn-default login_btn" value="提交">
                </form>
            </div>
        </div>
    </div>
    <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
    
    </body>
    </html>

    19-博客系统之注册页面的头像预览功能

    1、头像预览功能实现的js代码;

    2、获取上传后文件的方法;

    • $("#avatar")[0].files[0],得到如下内容:File(11414) {name: "keep.jpg", lastModified: 1531732278496, lastModifiedDate: Mon Jul 16 2018 17:11:18 GMT+0800 (中国标准时间), webkitRelativePath: "", size: 11414, …}
    <script>
        $("#avatar").change(function () {
            //1、获取用户选中的文件对象;
            var file_obj = $(this)[0].files[0];
            //2、获取文件对象的路径;
            var reader =  new FileReader();
            reader.readAsDataURL(file_obj);
           //3、修改img的src属性值,src= 文件对象的路径;
            reader.onload = function(){
                $("#avatar_img").attr("src",reader.result)
            };
    
        })
    </script>

    20-博客系统之基于Ajax的form data数据

    1、通过Ajax方法实现向后端传送数据;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册页面</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
        <style>
            #avatar{
                display: none;
            }
            #avatar_img{
                margin-left: 20px;
            }
        </style>
    </head>
    <body>
    <h3>注册页面</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-offset-3">
                <form >
                    {% csrf_token %}
                    <!--进行渲染form对象-->
                    {% for field in form %}
                        <div class="form-group">
                            <label for="{{ field.auto_id }}">{{ field.label }}</label>
                            {{ field }}
                        </div>
                    {% endfor %}
                    <div class="form-group">
                        <label for="avatar">
                            头像
                            <img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png" alt="">
                        </label>
                        <input type="file" id="avatar" >
                    </div>
                    <input type="button" class="btn btn-default reg_btn" value="提交">
                </form>
            </div>
        </div>
    </div>
    <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
    
    <!--编写js代码-->
    <script>
        $("#avatar").change(function () {
            //1、获取用户选中的文件对象;
            var file_obj = $(this)[0].files[0];
            //2、获取文件对象的路径;
            var reader =  new FileReader();
            reader.readAsDataURL(file_obj);
           //3、修改img的src属性值,src= 文件对象的路径;
            reader.onload = function(){
                $("#avatar_img").attr("src",reader.result)
            };
    
        });
        //基于Ajax提交事件;
        $(".reg_btn").click(function () {
            var formdata = new FormData();
            formdata.append("user",$("#id_user").val());
            formdata.append("pwd",$("#id_pwd").val());
            formdata.append("r_pwd",$("#id_r_pwd").val());
            formdata.append("email",$("#id_email").val());
            formdata.append("avatar",$("#avatar")[0].files[0]);
            formdata.append("csrfmiddlewaretoken",$("[name = 'csrfmiddlewaretoken']").val());
    
            $.ajax({
                url:"",
                type:"post",
                contentType:false,
                processData:false,
                data:formdata,
                success:function (data) {
                    console.log(data)
    
                }
            })
        })
    </script>
    
    </body>
    </html>

    2、views.py进行逻辑判断;

    from django.shortcuts import render,HttpResponse,redirect
    
    # Create your views here.
    
    from django.http import JsonResponse
    from django.contrib import auth
    
    def login(request):
        """
        登录;
        :param request:
        :return:
        """
        if request.method == "POST":
            response = {"user":None,"msg":None}
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
            valid_code  = request.POST.get("valid_code")
    
            valid_code_str = request.session.get("valid_code_str")
            if valid_code.upper() == valid_code_str.upper():
                user = auth.authenticate(username = user,password = pwd)
                if user:
                    response["user"] = user.username
                else:
                    response["msg"] = "用户名或者密码错误!"
            else:
                response["msg"] = "验证码错误!"
            return JsonResponse(response)
        return render(request,"login.html")
    
    
    def get_valid_code_img(request):
        """
        基于PIL模块动态生成响应状态码图片;
        :param request:
        :return:
        """
        from blog.utils.validCode import get_valid_code_img
        data = get_valid_code_img(request)
        return HttpResponse(data)
    
    
    def index(request):
        """
        首页;
        :param request:
        :return:
        """
        return render(request,"index.html")
    
    
    from django import forms
    from django.forms import widgets
    from django.http import JsonResponse
    
    class UserForm(forms.Form):
        user = forms.CharField(max_length=32,label="用户名",widget=widgets.TextInput(attrs={"class":"form-control"}))
        pwd = forms.CharField(max_length=32,label="密码",widget=widgets.PasswordInput(attrs={"class":"form-control"}))
        r_pwd = forms.CharField(max_length=32,label="确认密码",widget=widgets.PasswordInput(attrs={"class":"form-control"}))
        email = forms.EmailField(max_length=32,label="注册邮箱",widget=widgets.EmailInput(attrs={"class":"form-control"}))
    
    def register(request):
        """
        注册;
        :param request:
        :return:
        """
        #if request.method == "POST":
        if request.is_ajax():
            print(request.POST)
            form = UserForm(request.POST)
    
    
            response = {"user":None,"msg":None}
            if form.is_valid():
                response["user"] = form.cleaned_data.get("user")
            else:
                print(form.cleaned_data)
                print(form.errors)
                response["msg"] = form.errors
            return JsonResponse(response)
        form = UserForm()
        return render(request,"register.html",{"form":form})

    21-博客系统之基于Ajax提交from data的数据的优化

    1、使用serializeArray()方法进行优化;

    注意犯的错误,Js中的代码多行注释的方法为:/*被注释的内容,而不是<!--被注释的内容-->*/

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册页面</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
        <style>
            #avatar{
                display: none;
            }
            #avatar_img{
                margin-left: 20px;
            }
        </style>
    </head>
    <body>
    <h3>注册页面</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-offset-3">
                <form id="form">
                    {% csrf_token %}
                    <!--进行渲染form对象-->
                    {% for field in form %}
                        <div class="form-group">
                            <label for="{{ field.auto_id }}">{{ field.label }}</label>
                            {{ field }}
                        </div>
                    {% endfor %}
                    <div class="form-group">
                        <label for="avatar">
                            头像
                            <img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png" alt="">
                        </label>
                        <input type="file" id="avatar" >
                    </div>
                    <input type="button" class="btn btn-default reg_btn" value="提交">
                </form>
            </div>
        </div>
    </div>
    <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
    
    <!--编写js代码-->
    <script>
        $("#avatar").change(function () {
            //1、获取用户选中的文件对象;
            var file_obj = $(this)[0].files[0];
            //2、获取文件对象的路径;
            var reader =  new FileReader();
            reader.readAsDataURL(file_obj);
            //3、修改img的src属性值,src= 文件对象的路径;
            reader.onload = function(){
                $("#avatar_img").attr("src",reader.result)
            };
    
        });
        //基于Ajax提交事件;
        $(".reg_btn").click(function () {
    
            var formdata = new FormData();
            console.log($("#form").serializeArray());
            var request_data = $("#form").serializeArray();
            $.each(request_data,function (index,data) {
                formdata.append(data.name,data.value)
            });
            formdata.append("avatar",$("#avatar")[0].files[0]);
    
            /* var formdata = new FormData();
             formdata.append("user",$("#id_user").val());
             formdata.append("pwd",$("#id_pwd").val());
             formdata.append("r_pwd",$("#id_r_pwd").val());
             formdata.append("email",$("#id_email").val());
             formdata.append("avatar",$("#avatar")[0].files[0]);
             formdata.append("csrfmiddlewaretoken",$("[name = 'csrfmiddlewaretoken']").val());*/
    
            $.ajax({
                url:"",
                type:"post",
                contentType:false,
                processData:false,
                data:formdata,
                success:function (data) {
                    console.log(data)
    
                }
            })
        })
    </script>
    
    </body>
    </html>

    22-博客系统之基于Ajax在注册页面显示错误消息1

    1、基于Ajax实现页面错误消息;

    register.html;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册页面</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
        <style>
            #avatar{
                display: none;
            }
            #avatar_img{
                margin-left: 20px;
            }
            .error{
                color: red;
            }
        </style>
    </head>
    <body>
    <h3>注册页面</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-offset-3">
                <form id="form">
                    {% csrf_token %}
                    <!--进行渲染form对象-->
                    {% for field in form %}
                        <div class="form-group">
                            <label for="{{ field.auto_id }}">{{ field.label }}</label>
                            {{ field }}<span class="error pull-right"></span>
                        </div>
                    {% endfor %}
                    <div class="form-group">
                        <label for="avatar">
                            头像
                            <img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png" alt="">
                        </label>
                        <input type="file" id="avatar" name="avatar">
                    </div>
                    <input type="button" class="btn btn-default reg_btn" value="提交">
                </form>
            </div>
        </div>
    </div>
    <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
    
    <!--编写js代码-->
    <script>
        $("#avatar").change(function () {
            //1、获取用户选中的文件对象;
            var file_obj = $(this)[0].files[0];
            //2、获取文件对象的路径;
            var reader =  new FileReader();
            reader.readAsDataURL(file_obj);
            //3、修改img的src属性值,src= 文件对象的路径;
            reader.onload = function(){
                $("#avatar_img").attr("src",reader.result)
            };
    
        });
        //基于Ajax提交事件;
        $(".reg_btn").click(function () {
    
            var formdata = new FormData();
            console.log($("#form").serializeArray());
            var request_data = $("#form").serializeArray();
            $.each(request_data,function (index,data) {
                formdata.append(data.name,data.value)
            });
            formdata.append("avatar",$("#avatar")[0].files[0]);
    
           /* var formdata = new FormData();
             formdata.append("user",$("#id_user").val());
             formdata.append("pwd",$("#id_pwd").val());
             formdata.append("r_pwd",$("#id_r_pwd").val());
             formdata.append("email",$("#id_email").val());
             formdata.append("avatar",$("#avatar")[0].files[0]);
             formdata.append("csrfmiddlewaretoken",$("[name = 'csrfmiddlewaretoken']").val());*/
    
            $.ajax({
                url:"",
                type:"post",
                contentType:false,
                processData:false,
                data:formdata,
                success:function (data) {
                    console.log(data);
                    if(data.user){
                        //注册成功;
                    }else {
                        //console.log(data.msg)
                        $.each(data.msg,function (field,error_list) {
                            console.log(field,error_list);
                            $("#id_"+field).next().html(error_list[0])
                        })
                    }
                }
            });
        });
    </script>
    
    </body>
    </html>

    23-博客系统之基于Ajax在注册页面显示错误消息2

    1、addClass以及removeClass以及Bootstrap中has-error的使用;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册页面</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
        <style>
            #avatar{
                display: none;
            }
            #avatar_img{
                margin-left: 20px;
            }
            .error{
                color: red;
            }
        </style>
    </head>
    <body>
    <h3>注册页面</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-offset-3">
                <form id="form">
                    {% csrf_token %}
                    <!--进行渲染form对象-->
                    {% for field in form %}
                        <div class="form-group">
                            <label for="{{ field.auto_id }}">{{ field.label }}</label>
                            {{ field }}<span class="error pull-right"></span>
                        </div>
                    {% endfor %}
                    <div class="form-group">
                        <label for="avatar">
                            头像
                            <img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png" alt="">
                        </label>
                        <input type="file" id="avatar" name="avatar">
                    </div>
                    <input type="button" class="btn btn-default reg_btn" value="提交">
                </form>
            </div>
        </div>
    </div>
    <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
    
    <!--编写js代码-->
    <script>
        $("#avatar").change(function () {
            //1、获取用户选中的文件对象;
            var file_obj = $(this)[0].files[0];
            //2、获取文件对象的路径;
            var reader =  new FileReader();
            reader.readAsDataURL(file_obj);
            //3、修改img的src属性值,src= 文件对象的路径;
            reader.onload = function(){
                $("#avatar_img").attr("src",reader.result)
            };
    
        });
        //基于Ajax提交事件;
        $(".reg_btn").click(function () {
    
            var formdata = new FormData();
            console.log($("#form").serializeArray());
            var request_data = $("#form").serializeArray();
            $.each(request_data,function (index,data) {
                formdata.append(data.name,data.value)
            });
            formdata.append("avatar",$("#avatar")[0].files[0]);
    
            /* var formdata = new FormData();
              formdata.append("user",$("#id_user").val());
              formdata.append("pwd",$("#id_pwd").val());
              formdata.append("r_pwd",$("#id_r_pwd").val());
              formdata.append("email",$("#id_email").val());
              formdata.append("avatar",$("#avatar")[0].files[0]);
              formdata.append("csrfmiddlewaretoken",$("[name = 'csrfmiddlewaretoken']").val());*/
    
            $.ajax({
                url:"",
                type:"post",
                contentType:false,
                processData:false,
                data:formdata,
                success:function (data) {
                    console.log(data);
                    if(data.user){
                        //注册成功;
                    }else {//注册失败
                        //console.log(data.msg)
                        //清空错误提示消息;
                        $("span.error").html();
                        $(".form-group").removeClass("has-error");
                        //展示此次提提交的信息;
                        $.each(data.msg,function (field,error_list) {
                            console.log(field,error_list);
                            $("#id_"+field).next().html(error_list[0]);
                            $("#id_"+field).parent().addClass("has-error");
                        })
                    }
                }
            });
        });
    </script>
    
    </body>
    </html>

    24-博客系统之form组件的局部钩子与全局钩子的应用

    1、form组件中的钩子和全局钩子应用;

    #!/usr/bin/env python3
    # -*- coding:utf-8 -*-
    # __Author__:TQTL911
    # Version:python3.6.6
    # Time:2018/8/24 14:41
    from django import forms
    from django.forms import widgets
    from django.http import JsonResponse
    from blog.models import UserInfo
    from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
    
    class UserForm(forms.Form):
        user = forms.CharField(max_length=32,error_messages={"required":"该字段不能为空!"},label="用户名",widget=widgets.TextInput(attrs={"class":"form-control"}))
        pwd = forms.CharField(max_length=32,label="密码",widget=widgets.PasswordInput(attrs={"class":"form-control"}))
        r_pwd = forms.CharField(max_length=32,label="确认密码",widget=widgets.PasswordInput(attrs={"class":"form-control"}))
        email = forms.EmailField(max_length=32,label="注册邮箱",widget=widgets.EmailInput(attrs={"class":"form-control"}))
    
        def clean_user(self):
            user = self.cleaned_data.get("user")
            user = UserInfo.objects.filter(username=user).first()
    
            if not user:
                return user
            else:
                raise ValidationError("该用户已经注册")
        def clean(self):
            pwd = self.cleaned_data.get("pwd")
            r_pwd = self.cleaned_data.get("r_pwd")
    
            if pwd == r_pwd:
                return self.cleaned_data
            else:
                raise ValidationError("两次密码不一致!")

    2、from django.core.exceptions import NON_FIELD_ERRORS, ValidationError的引入;

    3、抛出中文异常的方法;

    4、cleaned_data的用法;

    register.html;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册页面</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
        <style>
            #avatar{
                display: none;
            }
            #avatar_img{
                margin-left: 20px;
            }
            .error{
                color: red;
            }
        </style>
    </head>
    <body>
    <h3>注册页面</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-offset-3">
                <form id="form">
                    {% csrf_token %}
                    <!--进行渲染form对象-->
                    {% for field in form %}
                        <div class="form-group">
                            <label for="{{ field.auto_id }}">{{ field.label }}</label>
                            {{ field }}<span class="error pull-right"></span>
                        </div>
                    {% endfor %}
                    <div class="form-group">
                        <label for="avatar">
                            头像
                            <img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png" alt="">
                        </label>
                        <input type="file" id="avatar" name="avatar">
                    </div>
                    <input type="button" class="btn btn-default reg_btn" value="提交">
                </form>
            </div>
        </div>
    </div>
    <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
    
    <!--编写js代码-->
    <script>
        $("#avatar").change(function () {
            //1、获取用户选中的文件对象;
            var file_obj = $(this)[0].files[0];
            //2、获取文件对象的路径;
            var reader =  new FileReader();
            reader.readAsDataURL(file_obj);
            //3、修改img的src属性值,src= 文件对象的路径;
            reader.onload = function(){
                $("#avatar_img").attr("src",reader.result)
            };
    
        });
        //基于Ajax提交事件;
        $(".reg_btn").click(function () {
    
            var formdata = new FormData();
            console.log($("#form").serializeArray());
            var request_data = $("#form").serializeArray();
            $.each(request_data,function (index,data) {
                formdata.append(data.name,data.value)
            });
            formdata.append("avatar",$("#avatar")[0].files[0]);
    
            /* var formdata = new FormData();
              formdata.append("user",$("#id_user").val());
              formdata.append("pwd",$("#id_pwd").val());
              formdata.append("r_pwd",$("#id_r_pwd").val());
              formdata.append("email",$("#id_email").val());
              formdata.append("avatar",$("#avatar")[0].files[0]);
              formdata.append("csrfmiddlewaretoken",$("[name = 'csrfmiddlewaretoken']").val());*/
    
            $.ajax({
                url:"",
                type:"post",
                contentType:false,
                processData:false,
                data:formdata,
                success:function (data) {
                    console.log(data);
                    if(data.user){
                        //注册成功;
                    }else {//注册失败
                        //console.log(data.msg)
                        //清空错误提示消息;
                        $("span.error").html();
                        $(".form-group").removeClass("has-error");
                        //展示此次提提交的信息;
                        $.each(data.msg,function (field,error_list) {
                            console.log(field,error_list);
                            if(field=="__all__"){
                                $("#id_r_pwd").next().html(error_list[0]).parent().addClass("has-error")
                            }
                            $("#id_"+field).next().html(error_list[0]);
                            $("#id_"+field).parent().addClass("has-error");
                        })
                    }
                }
            });
        });
    </script>
    
    </body>
    </html>

    25-博客系统之FileFiled字段

    1、先导入from blog.models import UserInfo;

    2、is_valid()校验通过后,向数据库库中写入数据,注意此时使用的是,UserInfo.objects.create_user()方法进行写入数据;

    3、进行注册功能的验证;

    4、进行登录验证;

    26-博客系统之media配置1

    1、settings.py中,STATICFILES_DIRS、MEDIA_ROOT配置;

    2、上传文件,default的逻辑判断;

    from django.shortcuts import render,HttpResponse,redirect
    
    # Create your views here.
    
    from django.http import JsonResponse
    from django.contrib import auth
    
    def login(request):
        """
        登录;
        :param request:
        :return:
        """
        if request.method == "POST":
            response = {"user":None,"msg":None}
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
            valid_code  = request.POST.get("valid_code")
    
            valid_code_str = request.session.get("valid_code_str")
            if valid_code.upper() == valid_code_str.upper():
                user = auth.authenticate(username = user,password = pwd)
                if user:
                    response["user"] = user.username
                else:
                    response["msg"] = "用户名或者密码错误!"
            else:
                response["msg"] = "验证码错误!"
            return JsonResponse(response)
        return render(request,"login.html")
    
    
    def get_valid_code_img(request):
        """
        基于PIL模块动态生成响应状态码图片;
        :param request:
        :return:
        """
        from blog.utils.validCode import get_valid_code_img
        data = get_valid_code_img(request)
        return HttpResponse(data)
    
    
    def index(request):
        """
        首页;
        :param request:
        :return:
        """
        return render(request,"index.html")
    
    
    from blog.Myforms import UserForm
    from blog.models import UserInfo
    def register(request):
        """
        注册;
        :param request:
        :return:
        """
        #if request.method == "POST":
        if request.is_ajax():
            print(request.POST)
            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")
                if avatar_obj:
                    user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
                else:
                    user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
    
            else:
                print(form.cleaned_data)
                print(form.errors)
                response["msg"] = form.errors
            return JsonResponse(response)
        form = UserForm()
        return render(request,"register.html",{"form":form})

    27-博客系统之media配置2

    1、settings.py中MEDIAZ_URL的配置;

    2、urls.py中MEDIA_ROOT的引用而不是MEDIA_URL;

    28-博客系统之生成用户对象的代码优化

    1、减少冗余代码,提高可读性;

    from django.shortcuts import render, HttpResponse, redirect
    
    # Create your views here.
    
    from django.http import JsonResponse
    from django.contrib import auth
    from blog.Myforms import UserForm
    from blog.models import UserInfo
    
    
    def login(request):
        """
        登录;
        :param request:
        :return:
        """
        if request.method == "POST":
            response = {"user": None, "msg": None}
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
            valid_code = request.POST.get("valid_code")
    
            valid_code_str = request.session.get("valid_code_str")
            if valid_code.upper() == valid_code_str.upper():
                user = auth.authenticate(username=user, password=pwd)
                if user:
                    response["user"] = user.username
                else:
                    response["msg"] = "用户名或者密码错误!"
            else:
                response["msg"] = "验证码错误!"
            return JsonResponse(response)
        return render(request, "login.html")
    
    
    def get_valid_code_img(request):
        """
        基于PIL模块动态生成响应状态码图片;
        :param request:
        :return:
        """
        from blog.utils.validCode import get_valid_code_img
        data = get_valid_code_img(request)
        return HttpResponse(data)
    
    
    def index(request):
        """
        首页;
        :param request:
        :return:
        """
        return render(request, "index.html")
    
    
    def register(request):
        """
        注册;
        :param request:
        :return:
        """
        # if request.method == "POST":
        if request.is_ajax():
            # print(request.POST)
            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")
                '''
                if avatar_obj:
                    user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
                else:
                    user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
                '''
                extra = {}
                if avatar_obj:
                    extra["avatar"] = avatar_obj
                UserInfo.objects.create_user(username=user, password=pwd, email=email, )
    
    
            else:
                # print(form.cleaned_data)
                # print(form.errors)
                response["msg"] = form.errors
            return JsonResponse(response)
        form = UserForm()
        return render(request, "register.html", {"form": form})

    2、import使用方式,一般位于py文件的顶部且有顺序,先xx后xx;

    Pycharm中,使用Ctrl+Shift+L组合键,进行Reformat Code操作,提升代码排版规范;

    3、遵循PEP8代码编写规范,提高代码质量;

    Python语言规范;https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_language_rules/

    29-博客系统之系统首页的导航区域

    1、Bootstrap之导航条的使用;

    2、{{ request.user.username }}的使用;

    3、auth.logout(request) # 等同于request.session.flush();

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>博客系统首页</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
        <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
        <script src="/static/blog/bs/js/bootstrap.min.js"></script>
        <style>
            #user_icon {
                font-size: 18px;
                margin-right: 10px;
                vertical-align: -3px;
            }
        </style>
    </head>
    <body>
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                        data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#">博客园</a>
            </div>
    
            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="#">随笔 <span class="sr-only">(current)</span></a></li>
                    <li><a href="#">新闻</a></li>
                    <li><a href="#">博文</a></li>
                </ul>
    
                <ul class="nav navbar-nav navbar-right">
                    {% if request.user.is_authenticated %}
                        <li><a href="#"><span id="user_icon" class="glyphicon glyphicon-user"></span>{{ request.user.username }}</a></li>
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                               aria-expanded="false">Dropdown <span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li><a href="#">修改密码</a></li>
                                <li><a href="#">修改头像</a></li>
                                <li><a href="/logout/">注销</a></li>
                                <li role="separator" class="divider"></li>
                                <li><a href="#">Separated link</a></li>
                            </ul>
                        </li>
    
                    {% else %}
                        <li><a href="/login/">登录</a></li>
                        <li><a href="/register/">注册</a></li>
                    {% endif %}
    
                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
    
    </body>
    </html>

    views.py;

    from django.shortcuts import render, HttpResponse, redirect
    
    # Create your views here.
    
    from django.http import JsonResponse
    from django.contrib import auth
    from blog.Myforms import UserForm
    from blog.models import UserInfo
    
    
    def login(request):
        """
        登录;
        :param request:
        :return:
        """
        if request.method == "POST":
            response = {"user": None, "msg": None}
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
            valid_code = request.POST.get("valid_code")
    
            valid_code_str = request.session.get("valid_code_str")
            if valid_code.upper() == valid_code_str.upper():
                user = auth.authenticate(username=user, password=pwd)
                if user:
                    auth.login(request, user)
                    response["user"] = user.username
                else:
                    response["msg"] = "用户名或者密码错误!"
            else:
                response["msg"] = "验证码错误!"
            return JsonResponse(response)
        return render(request, "login.html")
    
    
    def index(request):
        """
        首页;
        :param request:
        :return:
        """
        return render(request, "index.html")
    
    
    def logout(request):
        auth.logout(request)  # 等同于request.session.flush()
        return redirect("/login/")
    
    
    def get_valid_code_img(request):
        """
        基于PIL模块动态生成响应状态码图片;
        :param request:
        :return:
        """
        from blog.utils.validCode import get_valid_code_img
        data = get_valid_code_img(request)
        return HttpResponse(data)
    
    
    def register(request):
        """
        注册;
        :param request:
        :return:
        """
        # if request.method == "POST":
        if request.is_ajax():
            # print(request.POST)
            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")
                '''
                if avatar_obj:
                    user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
                else:
                    user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
                '''
                extra = {}
    
                if avatar_obj:
                    extra["avatar"] = avatar_obj
                UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
            else:
                # print(form.cleaned_data)
                # print(form.errors)
                response["msg"] = form.errors
            return JsonResponse(response)
        form = UserForm()
        return render(request, "register.html", {"form": form})

    urls.py;

    """cnblogs URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/2.1/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  path('', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.urls import include, path
        2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
    """
    from django.contrib import admin
    from django.urls import path, re_path
    from blog import views
    from django.views.static import serve
    from cnblogs import settings
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('login/', views.login),
        path('index/', views.index),
        path('logout/', views.logout),
        re_path('^$', views.index),
        path('get_validCode_img/', views.get_valid_code_img),
        path('register/', views.register),
        # media配置;
        re_path(r"media/(?P<path>.*)$", serve, {"document_root": settings.MEDIA_ROOT}),
    ]

    30-博客系统之系统首页的主体布局

    1、基于Bootstrap的格栅系统,定制页面布局;

    <!--引入BootStrap格栅系统-->
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-3">
                <div class="panel panel-warning">
                    <div class="panel-heading">Panel heading without title</div>
                    <div class="panel-body">
                        Panel content
                    </div>
                </div>
                <div class="panel panel-info">
                    <div class="panel-heading">Panel heading without title</div>
                    <div class="panel-body">
                        Panel content
                    </div>
                </div>
                <div class="panel panel-danger">
                    <div class="panel-heading">Panel heading without title</div>
                    <div class="panel-body">
                        Panel content
                    </div>
                </div>
            </div>
            <div class="col-md-6">222</div>
            <div class="col-md-3">
                <div class="panel panel-default">
                    <div class="panel-heading">Panel heading without title</div>
                    <div class="panel-body">
                        Panel content
                    </div>
                </div>
                <div class="panel panel-primary">
                    <div class="panel-heading">Panel heading without title</div>
                    <div class="panel-body">
                        Panel content
                    </div>
                </div>
                <div class="panel panel-default">
                    <div class="panel-heading">Panel heading without title</div>
                    <div class="panel-body">
                        Panel content
                    </div>
                </div>
            </div>
        </div>
    </div>

    31-博客系统之admin的简单实用

    1、Django下的admin初识;

    Django内部的一个组件:后台数据管理组件(Web界面);

    创建超级用户的方法:python manage.py createsuperuser 针对用户认证,在blog_userinfo表中生成记录,is_super字段值为1;

    2、settings.py中的admin说明;

    3、基于应用名称的blog中的admin.py进行表的注册;

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

    32-博客系统之基于admin录入文章数据

    1、通过Web前端页面进行文章数据的录入;

    33-博客系统之系统首页的文章列表的渲染1

    1、文章列表的渲染展示;

    <div class="col-md-6">
                <div class="article_list">
                    {% for articel in article_list %}
                        <div class="articel-item">
                            <h5><a href="">{{ articel.title }}</a></h5>
                            <div class="article-desc">
                            <span class="media-left">
                                <a href=""><img width="56" height="56" src="media/{{ articel.user.avatar }}" alt=""></a>
                            </span>
                                <span class="media-right">
                                {{ articel.desc }}
                            </span>
                            </div>
                            <hr>
                        </div>
                    {% endfor %}
                </div>
            </div>

    34-博客系统之系统首页的文章列表的渲染2

    1、首页文章列表渲染;

    index.html;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>博客系统首页</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
        <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
        <script src="/static/blog/bs/js/bootstrap.min.js"></script>
        <style>
            #user_icon {
                font-size: 18px;
                margin-right: 10px;
                vertical-align: -3px;
            }
            .pub_info{
                margin-top: 10px;
            }
            .pub_info .glyphicon-comment{
                vertical-align: -1px;
            }
        </style>
    </head>
    <body>
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                        data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#">博客园</a>
            </div>
    
            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="#">随笔 <span class="sr-only">(current)</span></a></li>
                    <li><a href="#">新闻</a></li>
                    <li><a href="#">博文</a></li>
                </ul>
    
                <ul class="nav navbar-nav navbar-right">
                    {% if request.user.is_authenticated %}
                        <li><a href="#"><span id="user_icon"
                                              class="glyphicon glyphicon-user"></span>{{ request.user.username }}</a></li>
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                               aria-expanded="false">Dropdown <span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li><a href="#">修改密码</a></li>
                                <li><a href="#">修改头像</a></li>
                                <li><a href="/logout/">注销</a></li>
                                <li role="separator" class="divider"></li>
                                <li><a href="#">Separated link</a></li>
                            </ul>
                        </li>
    
                    {% else %}
                        <li><a href="/login/">登录</a></li>
                        <li><a href="/register/">注册</a></li>
                    {% endif %}
    
                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
    
    <!--引入BootStrap格栅系统-->
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-3">
                <div class="panel panel-warning">
                    <div class="panel-heading">Panel heading without title</div>
                    <div class="panel-body">
                        Panel content
                    </div>
                </div>
                <div class="panel panel-info">
                    <div class="panel-heading">Panel heading without title</div>
                    <div class="panel-body">
                        Panel content
                    </div>
                </div>
                <div class="panel panel-danger">
                    <div class="panel-heading">Panel heading without title</div>
                    <div class="panel-body">
                        Panel content
                    </div>
                </div>
            </div>
            <div class="col-md-6">
                <div class="article_list">
                    {% for articel in article_list %}
                        <div class="articel-item">
                            <h5><a href="">{{ articel.title }}</a></h5>
                            <div class="article-desc">
                            <span class="media-left">
                                <a href=""><img width="56" height="56" src="media/{{ articel.user.avatar }}" alt=""></a>
                            </span>
                                <span class="media-right">
                                {{ articel.desc }}
                            </span>
                            </div>
                            <hr>
                        </div>
                        <div class="small pub_info">
                        <span><a href="">{{ articel.user.username }}</a></span>&nbsp;&nbsp;&nbsp;
                        <span>发布于&nbsp;&nbsp;{{ articel.create_time|date:"Y-m-d H:i" }}</span>
                        <span class="glyphicon glyphicon-comment"></span>评论({{ articel.comment_count }})
                        <span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ articel.up_count }})
                        </div>
                    {% endfor %}
                </div>
            </div>
            <div class="col-md-3">
                <div class="panel panel-default">
                    <div class="panel-heading">Panel heading without title</div>
                    <div class="panel-body">
                        Panel content
                    </div>
                </div>
                <div class="panel panel-primary">
                    <div class="panel-heading">Panel heading without title</div>
                    <div class="panel-body">
                        Panel content
                    </div>
                </div>
                <div class="panel panel-default">
                    <div class="panel-heading">Panel heading without title</div>
                    <div class="panel-body">
                        Panel content
                    </div>
                </div>
            </div>
        </div>
    </div>
    </body>
    </html>

    35-博客系统之个人站点的页面文章的查询

    1、urls.py新增个人站点home_site访问路径;

    """cnblogs URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/2.1/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  path('', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.urls import include, path
        2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
    """
    from django.contrib import admin
    from django.urls import path, re_path
    from blog import views
    from django.views.static import serve
    from cnblogs import settings
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('login/', views.login),
        path('index/', views.index),
        path('logout/', views.logout),
        re_path('^$', views.index),
        path('get_validCode_img/', views.get_valid_code_img),
        path('register/', views.register),
        # media配置;
        re_path(r'media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),
        # 个人站点URL;
        re_path(r'^(?P<username>w+)$', views.home_site),
    ]

    2、views.py;

    from django.shortcuts import render, HttpResponse, redirect
    
    # Create your views here.
    
    from django.http import JsonResponse
    from django.contrib import auth
    from blog.Myforms import UserForm
    from blog.models import UserInfo
    from blog import models
    
    
    def login(request):
        """
        登录;
        :param request:
        :return:
        """
        if request.method == "POST":
            response = {"user": None, "msg": None}
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
            valid_code = request.POST.get("valid_code")
    
            valid_code_str = request.session.get("valid_code_str")
            if valid_code.upper() == valid_code_str.upper():
                user = auth.authenticate(username=user, password=pwd)
                if user:
                    auth.login(request, user)
                    response["user"] = user.username
                else:
                    response["msg"] = "用户名或者密码错误!"
            else:
                response["msg"] = "验证码错误!"
            return JsonResponse(response)
        return render(request, "login.html")
    
    
    def index(request):
        """
        首页;
        :param request:
        :return:
        """
        article_list = models.Article.objects.all()
    
        return render(request, "index.html", {"article_list": article_list})
    
    
    def logout(request):
        auth.logout(request)  # 等同于request.session.flush()
        return redirect("/login/")
    
    
    def get_valid_code_img(request):
        """
        基于PIL模块动态生成响应状态码图片;
        :param request:
        :return:
        """
        from blog.utils.validCode import get_valid_code_img
        data = get_valid_code_img(request)
        return HttpResponse(data)
    
    
    def register(request):
        """
        注册;
        :param request:
        :return:
        """
        # if request.method == "POST":
        if request.is_ajax():
            # print(request.POST)
            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")
                '''
                if avatar_obj:
                    user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
                else:
                    user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
                '''
                extra = {}
    
                if avatar_obj:
                    extra["avatar"] = avatar_obj
                UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
            else:
                # print(form.cleaned_data)
                # print(form.errors)
                response["msg"] = form.errors
            return JsonResponse(response)
        form = UserForm()
        return render(request, "register.html", {"form": form})
    
    
    def home_site(request, username):
        """
        个人站点视图函数;
        :param request:
        :return:
        """
        print("username", username)
        user = UserInfo.objects.filter(username=username).first()
        # 判断用户是否存在;
        if not user:
            return render(request, "not_found.html")
        # 查询当前站点对象:
        blog = user.blog
        # 当前用户或者当前站点对应的所有文章;
        # 基于对象查询;
        # article_list = user.article_set.all()
        # 基于双下划线的查询;
        article_list = models.Article.objects.filter(user=user)
        return render(request, "home_site.html")

    36-博客系统之个人站点页面的标签与分类查询

    1、标签以及分类的查询方法;

    def home_site(request, username):
        """
        个人站点视图函数;
        :param request:
        :return:
        """
        print("username", username)
        user = UserInfo.objects.filter(username=username).first()
        # 判断用户是否存在;
        if not user:
            return render(request, "not_found.html")
        # 查询当前站点对象:
        blog = user.blog
        # 当前用户或者当前站点对应的所有文章;
        # 基于对象查询;
        # article_list = user.article_set.all()
        # 基于双下划线的查询;
        article_list = models.Article.objects.filter(user=user)
    
        # 每一个后表的模型.objedts.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")
    
        # 1、查询每一个分类名称以及对应的文章数;
        ret = models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title", "c")
        print(ret)
        # 2、查询当前站点的每一个分类名称以及对应的文章数;
        cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
            "title", "c")
        print(cate_list)
    
        # 3、查询当前站点的每一个标签名称对应的文章数;
        tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
        print(tag_list)
    
        return render(request, "home_site.html")

    37-博客系统之个人站点页面的日期查询1

    1、SQL查询语句,针对于显示的格式化方法:data_format;

    38-博客系统之个人站点页面的日期查询2

    1、日期查询之extra()函数;

    from django.shortcuts import render, HttpResponse, redirect
    
    # Create your views here.
    
    from django.http import JsonResponse
    from django.contrib import auth
    from blog.Myforms import UserForm
    from blog.models import UserInfo
    from blog import models
    from django.db.models import Count
    
    
    def login(request):
        """
        登录;
        :param request:
        :return:
        """
        if request.method == "POST":
            response = {"user": None, "msg": None}
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
            valid_code = request.POST.get("valid_code")
    
            valid_code_str = request.session.get("valid_code_str")
            if valid_code.upper() == valid_code_str.upper():
                user = auth.authenticate(username=user, password=pwd)
                if user:
                    auth.login(request, user)
                    response["user"] = user.username
                else:
                    response["msg"] = "用户名或者密码错误!"
            else:
                response["msg"] = "验证码错误!"
            return JsonResponse(response)
        return render(request, "login.html")
    
    
    def index(request):
        """
        首页;
        :param request:
        :return:
        """
        article_list = models.Article.objects.all()
    
        return render(request, "index.html", {"article_list": article_list})
    
    
    def logout(request):
        auth.logout(request)  # 等同于request.session.flush()
        return redirect("/login/")
    
    
    def get_valid_code_img(request):
        """
        基于PIL模块动态生成响应状态码图片;
        :param request:
        :return:
        """
        from blog.utils.validCode import get_valid_code_img
        data = get_valid_code_img(request)
        return HttpResponse(data)
    
    
    def register(request):
        """
        注册;
        :param request:
        :return:
        """
        # if request.method == "POST":
        if request.is_ajax():
            # print(request.POST)
            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")
                '''
                if avatar_obj:
                    user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
                else:
                    user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
                '''
                extra = {}
    
                if avatar_obj:
                    extra["avatar"] = avatar_obj
                UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
            else:
                # print(form.cleaned_data)
                # print(form.errors)
                response["msg"] = form.errors
            return JsonResponse(response)
        form = UserForm()
        return render(request, "register.html", {"form": form})
    
    
    def home_site(request, username):
        """
        个人站点视图函数;
        :param request:
        :return:
        """
        print("username", username)
        user = UserInfo.objects.filter(username=username).first()
        # 判断用户是否存在;
        if not user:
            return render(request, "not_found.html")
        # 查询当前站点对象:
        blog = user.blog
        # 当前用户或者当前站点对应的所有文章;
        # 基于对象查询;
        # article_list = user.article_set.all()
        # 基于双下划线的查询;
        article_list = models.Article.objects.filter(user=user)
    
        # 每一个后表的模型.objedts.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")
    
        # 1、查询每一个分类名称以及对应的文章数;
        ret1 = models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title", "c")
        print(ret1)
        # 2、查询当前站点的每一个分类名称以及对应的文章数;
        cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
            "title", "c")
        print(cate_list)
    
        # 3、查询当前站点的每一个标签名称对应的文章数;
        tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
        print(tag_list)
        #4、查询当前站点每一个年月的名称以及对应的文章数之extra函数;
        ret2 = models.Article.objects.extra(select={"is_recent":"create_time > '2018-08-26'"}).values("title","is_recent")
        print(ret2)
        ret3 = models.Article.objects.extra(select={"y_m_d_date":"date_format(create_time,'%%Y-%%m-%%d')"}).values("title","y_m_d_date")
        print("这里是ret3",ret3)
        return render(request, "home_site.html")

    39-博客系统之个人站点页面的日期查询3

    1、以年和月的格式显示;

    date_list = models.Article.objects.filter(user=user).extra(select={"y_m_date":"date_format(create_time,'%%Y-%%m')"}).values("y_m_date").annotate(c = Count("nid")).values("y_m_date","c")
        print("这里是date_list",date_list)

    40-博客系统之个人站点页面的日期查询4

    1、使用Django内置的TruncMonth代理extra()方法以及date_format;

    from django.shortcuts import render, HttpResponse, redirect
    
    # Create your views here.
    
    from django.http import JsonResponse
    from django.contrib import auth
    from blog.Myforms import UserForm
    from blog.models import UserInfo
    from blog import models
    from django.db.models import Count
    
    
    def login(request):
        """
        登录;
        :param request:
        :return:
        """
        if request.method == "POST":
            response = {"user": None, "msg": None}
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
            valid_code = request.POST.get("valid_code")
    
            valid_code_str = request.session.get("valid_code_str")
            if valid_code.upper() == valid_code_str.upper():
                user = auth.authenticate(username=user, password=pwd)
                if user:
                    auth.login(request, user)
                    response["user"] = user.username
                else:
                    response["msg"] = "用户名或者密码错误!"
            else:
                response["msg"] = "验证码错误!"
            return JsonResponse(response)
        return render(request, "login.html")
    
    
    def index(request):
        """
        首页;
        :param request:
        :return:
        """
        article_list = models.Article.objects.all()
    
        return render(request, "index.html", {"article_list": article_list})
    
    
    def logout(request):
        auth.logout(request)  # 等同于request.session.flush()
        return redirect("/login/")
    
    
    def get_valid_code_img(request):
        """
        基于PIL模块动态生成响应状态码图片;
        :param request:
        :return:
        """
        from blog.utils.validCode import get_valid_code_img
        data = get_valid_code_img(request)
        return HttpResponse(data)
    
    
    def register(request):
        """
        注册;
        :param request:
        :return:
        """
        # if request.method == "POST":
        if request.is_ajax():
            # print(request.POST)
            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")
                '''
                if avatar_obj:
                    user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
                else:
                    user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
                '''
                extra = {}
    
                if avatar_obj:
                    extra["avatar"] = avatar_obj
                UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
            else:
                # print(form.cleaned_data)
                # print(form.errors)
                response["msg"] = form.errors
            return JsonResponse(response)
        form = UserForm()
        return render(request, "register.html", {"form": form})
    
    
    def home_site(request, username):
        """
        个人站点视图函数;
        :param request:
        :return:
        """
        print("username", username)
        user = UserInfo.objects.filter(username=username).first()
        # 判断用户是否存在;
        if not user:
            return render(request, "not_found.html")
        # 查询当前站点对象:
        blog = user.blog
        # 当前用户或者当前站点对应的所有文章;
        # 基于对象查询;
        # article_list = user.article_set.all()
        # 基于双下划线的查询;
        article_list = models.Article.objects.filter(user=user)
    
        # 每一个后表的模型.objedts.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")
    
        # 1、查询每一个分类名称以及对应的文章数;
        ret1 = models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title", "c")
        print(ret1)
        # 2、查询当前站点的每一个分类名称以及对应的文章数;
        cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
            "title", "c")
        print(cate_list)
    
        # 3、查询当前站点的每一个标签名称对应的文章数;
        """
        方式1:
        tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
        print(tag_list)
        #4、查询当前站点每一个年月的名称以及对应的文章数之extra函数;
        ret2 = models.Article.objects.extra(select={"is_recent":"create_time > '2018-08-26'"}).values("title","is_recent")
        print(ret2)
        date_list = models.Article.objects.filter(user=user).extra(select={"y_m_date":"date_format(create_time,'%%Y-%%m')"}).values("y_m_date").annotate(c = Count("nid")).values("y_m_date","c")
        print("这里是date_list",date_list)
        """
    
        """
        方式2:
        """
        from django.db.models.functions import TruncMonth
        ret4 = models.Article.objects.filter(user=user).annotate(month = TruncMonth("Create_time")).values("month").annotate(c= Count("nid")).values_list("month","c")
        print("ret----->",ret4)
    
        return render(request, "home_site.html")

    41-博客系统之个人站点页面的渲染布局1

    1、顶部导航栏设置,右浮动设置;

    2、基于博客的title向页面渲染数据;

    home_site.html;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>home_site</title>
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
            }
    
            .header {
                width: 100%;
                height: 60px;
                background-color: #369;
            }
    
            .header .title {
                font-size: 18px;
                font-weight: 100;
                line-height: 60px;
                color: white;
                margin-left: 15px;
                margin-top: -10px;
    
            }
            .backend{
                float: right;
                color: white;
                text-decoration: none;
                font-size: 14px;
                margin-right: 10px;
                margin-top: 10px;
            }
        </style>
    </head>
    <body>
    <div class="header">
        <div class="content">
            <p class="title">
                <span>{{ blog.title }}</span>
                <a href="" class="backend">管理</a>
            </p>
        </div>
    </div>
    </body>
    </html>

    42-博客系统之个人站点页面的渲染布局2

    1、站点页面的格栅系统布局渲染;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>home_site</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
        <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
        <script src="/static/blog/bs/js/bootstrap.min.js"></script>
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
            }
    
            .header {
                width: 100%;
                height: 60px;
                background-color: #369;
            }
    
            .header .title {
                font-size: 18px;
                font-weight: 100;
                line-height: 60px;
                color: white;
                margin-left: 15px;
                margin-top: -10px;
    
            }
    
            .backend {
                float: right;
                color: white;
                text-decoration: none;
                font-size: 14px;
                margin-right: 10px;
                margin-top: 10px;
            }
    
            .pub_info {
                margin-top: 10px;
                color: darkgray;
            }
    
        </style>
    </head>
    <body>
    <div class="header">
        <div class="content">
            <p class="title">
                <span>{{ blog.title }}</span>
                <a href="" class="backend">管理</a>
            </p>
        </div>
    </div>
    <div class="container">
        <div class="row">
            <div class="col-md-3">
    
            </div>
            <div class="col-md-9">
                <div class="article_list">
                    {% for article in article_list %}
                        <div class="article-item clearfix">
                            <h5><a href="">{{ article.title }}</a></h5>
                            <div class="article-desc">
                                {{ article.desc }}
                            </div>
                        </div>
                        <div class="small pub_info pull-right">
                            <span>发布于&nbsp;&nbsp;{{ article.create_time|date:"Y-m-d H:i" }}</span>
                            <span class="glyphicon glyphicon-comment"></span>评论({{ article.comment_count }})
                            <span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ article.up_count }})
                        </div>
                        <hr>
                    {% endfor %}
                </div>
            </div>
        </div>
    </div>
    </body>
    </html>

    43-博客系统之个人站点页面的渲染布局3

    1、在Bootstrap的栅格系统col-md-3左侧栏设置“我的标签”、“随笔分类”、“随笔归档”等分类tag;

    <div class="col-md-3">
                <div class="panel panel-warning">
                    <div class="panel-heading">我的标签</div>
                    <div class="panel-body">
                        {% for tag in tag_list %}
                            <p>{{ tag.0 }}({{ tag.1 }})</p>
                        {% endfor %}
    
                    </div>
                </div>
                <div class="panel panel-danger">
                    <div class="panel-heading">随笔分类</div>
                    <div class="panel-body">
                        {% for cate in cate_list %}
                            <p>{{ cate.0 }}({{ cate.1 }})</p>
                        {% endfor %}
    
                    </div>
                </div>
                <div class="panel panel-success">
                    <div class="panel-heading">随笔归档</div>
                    <div class="panel-body">
                        {% for date in date_list %}
                            <p>{{ date.0 }}({{ date.1 }})</p>
                        {% endfor %}
    
                    </div>
                </div>
    
            </div>

    2、通过数据库进行渲染的到数据并循环进行前端展示;

    views.py;

    def home_site(request, username, **kwargs):
        """
        个人站点视图函数
        :param request:
        :return:
        """
    
        print("kwargs", kwargs)  # 区分访问是的站点页面还是站点下的跳转页面
        print("username", username)
        user = UserInfo.objects.filter(username=username).first()
        # 判断用户是否存在!
        if not user:
            return render(request, "not_found.html")
    
        # 查询当前站点对象
    
        blog = user.blog
    
        # 1 当前用户或者当前站点对应所有文章
        # 基于对象查询
        # article_list=user.article_set.all()
        # 基于 __
    
        article_list = models.Article.objects.filter(user=user)
    
        if kwargs:
            condition = kwargs.get("condition")
            param = kwargs.get("param")  # 2012-12
    
            if condition == "category":
                article_list = article_list.filter(category__title=param)
            elif condition == "tag":
                article_list = article_list.filter(tags__title=param)
            else:
                year, month = param.split("/")
                article_list = article_list.filter(create_time__year=year, create_time__month=month)
    
        # 每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")
    
        # 查询每一个分类名称以及对应的文章数
    
        # ret=models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title","c")
        # print(ret)
    
        # 查询当前站点的每一个分类名称以及对应的文章数
    
        cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
            "title", "c")
        print(cate_list)
    
        # 查询当前站点的每一个标签名称以及对应的文章数
    
        tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
        print(tag_list)
    
        # 查询当前站点每一个年月的名称以及对应的文章数
    
        # ret=models.Article.objects.extra(select={"is_recent":"create_time > '2018-09-05'"}).values("title","is_recent")
        # print(ret)
    
        # 方式1:
        date_list = models.Article.objects.filter(user=user).extra(
            select={"y_m_date": "date_format(create_time,'%%Y/%%m')"}).values("y_m_date").annotate(
            c=Count("nid")).values_list("y_m_date", "c")
        print(date_list)
    
    
    # 方式2:
    
    # from django.db.models.functions import TruncMonth
    #
    # ret=models.Article.objects.filter(user=user).annotate(month=TruncMonth("create_time")).values("month").annotate(c=Count("nid")).values_list("month","c")
    # print("ret----->",ret)
    
        return render(request, "home_site.html",
                  {"username": username, "blog": blog, "article_list": article_list, "tag_list": tag_list,
                   "cate_list": cate_list,"date_list":date_list})

    3、展示效果如图所示;

    44-博客系统之个人站点的跳转过滤功能的实现1

    1、urls.py之路由设计,添加有名分组;

    """cnblogs URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/2.1/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  path('', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.urls import include, path
        2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
    """
    from django.contrib import admin
    from django.urls import path, re_path
    from blog import views
    from django.views.static import serve
    from cnblogs import settings
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('login/', views.login),
        path('index/', views.index),
        path('logout/', views.logout),
        re_path('^$', views.index),
        path('get_validCode_img/', views.get_valid_code_img),
        path('register/', views.register),
        # media配置;
        re_path(r'media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),
        # 个人站点URL;
        re_path(r'^(?P<username>w+)/(?P<condition>tag|category|archive)/(?P<param>.*)/$', views.home_site),
        #re_path(r'^(?P<username>w+)/category/.*/$', views.home_site),
        #re_path(r'^(?P<username>w+)/archive/.*/$', views.home_site),
        re_path(r'^(?P<username>w+)$', views.home_site),
    ]

    45-博客系统之个人站点页面的跳转过滤功能实现2

    1、urls.py中,针对于相同视图函数匹配不同的路径,需要传入不同的参数的解决办法;

    """cnblogs URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/2.1/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  path('', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.urls import include, path
        2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
    """
    from django.contrib import admin
    from django.urls import path, re_path
    from blog import views
    from django.views.static import serve
    from cnblogs import settings
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('login/', views.login),
        path('index/', views.index),
        path('logout/', views.logout),
        re_path('^$', views.index),
        path('get_validCode_img/', views.get_valid_code_img),
        path('register/', views.register),
        # media配置;
        re_path(r'media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),
        # 个人站点下的跳转URL;# 接受的参数,home_site(request,username,condition,param)
        re_path(r'^(?P<username>w+)/(?P<condition>tag|category|archive)/(?P<param>.*)/$', views.home_site),
    
        # re_path(r'^(?P<username>w+)/category/.*/$', views.home_site),
        # re_path(r'^(?P<username>w+)/archive/.*/$', views.home_site),
        # 个人站点URL;#接收的参数home_site(request,username="cxz")
        re_path(r'^(?P<username>w+)/$', views.home_site),
    ]

    2、MySQL数据库针对于create_time有约束的限制,需调整settings.py中的USE_TZ = True改为False;

    3、针对于年和月的匹配规则,使用split()方法进行分割处理;

    def home_site(request, username, **kwargs):
        """
        个人站点视图函数
        :param request:
        :return:
        """
    
        print("kwargs", kwargs)  # 区分访问是的站点页面还是站点下的跳转页面;
        print("username", username)
        user = UserInfo.objects.filter(username=username).first()
        # 判断用户是否存在!
        if not user:
            return render(request, "not_found.html")
    
        # 查询当前站点对象
    
        blog = user.blog
    
        # 1 当前用户或者当前站点对应所有文章
        # 基于对象查询
        # article_list=user.article_set.all()
        # 基于 __
    
        article_list = models.Article.objects.filter(user=user)
    
        if kwargs:
            condition = kwargs.get("condition")
            param = kwargs.get("param")  # 2012-12
    
            if condition == "category":
                article_list = article_list.filter(category__title=param)
            elif condition == "tag":
                article_list = article_list.filter(tags__title=param)
            else:
                year, month = param.split("/")
                article_list = article_list.filter(create_time__year=year, create_time__month=month)
    
        # 每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")
    
        # 查询每一个分类名称以及对应的文章数
    
        # ret=models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title","c")
        # print(ret)
    
        # 查询当前站点的每一个分类名称以及对应的文章数
    
        cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
            "title", "c")
        print(cate_list)
    
        # 查询当前站点的每一个标签名称以及对应的文章数
    
        tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
        print(tag_list)
    
        # 查询当前站点每一个年月的名称以及对应的文章数
    
        # ret=models.Article.objects.extra(select={"is_recent":"create_time > '2018-09-05'"}).values("title","is_recent")
        # print(ret)
    
        # 方式1:
        date_list = models.Article.objects.filter(user=user).extra(
            select={"y_m_date": "date_format(create_time,'%%Y-%%m')"}).values("y_m_date").annotate(
            c=Count("nid")).values_list("y_m_date", "c")
        print(date_list)
    
    
    # 方式2:
    
    # from django.db.models.functions import TruncMonth
    #
    # ret=models.Article.objects.filter(user=user).annotate(month=TruncMonth("create_time")).values("month").annotate(c=Count("nid")).values_list("month","c")
    # print("ret----->",ret)
    
        return render(request, "home_site.html",
                  {"username": username, "blog": blog, "article_list": article_list, "tag_list": tag_list,
                   "cate_list": cate_list,"date_list":date_list})

    46-博客系统之个人站点页面的跳转过滤功能的实现3

    1、基于“我的标签”、“随笔分类”、“随笔归档”进行跳转;

    home_site.html;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>home_site</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
        <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
        <script src="/static/blog/bs/js/bootstrap.min.js"></script>
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
            }
    
            .header {
                width: 100%;
                height: 60px;
                background-color: #369;
            }
    
            .header .title {
                font-size: 18px;
                font-weight: 100;
                line-height: 60px;
                color: white;
                margin-left: 15px;
                margin-top: -10px;
    
            }
    
            .backend {
                float: right;
                color: white;
                text-decoration: none;
                font-size: 14px;
                margin-right: 10px;
                margin-top: 10px;
            }
    
            .pub_info {
                margin-top: 10px;
                color: darkgray;
            }
    
        </style>
    </head>
    <body>
    <div class="header">
        <div class="content">
            <p class="title">
                <span>{{ blog.title }}</span>
                <a href="" class="backend">管理</a>
            </p>
        </div>
    </div>
    <div class="container">
        <div class="row">
            <div class="col-md-3">
                <div class="panel panel-warning">
                    <div class="panel-heading">我的标签</div>
                    <div class="panel-body">
                        {% for tag in tag_list %}
                            <p><a href="/{{ username }}/tag/{{ tag.0 }}"> {{ tag.0 }}({{ tag.1 }})</a></p>
                        {% endfor %}
    
                    </div>
                </div>
                <div class="panel panel-danger">
                    <div class="panel-heading">随笔分类</div>
                    <div class="panel-body">
                        {% for cate in cate_list %}
                             <p><a href="/{{ username }}/category/{{ cate.0 }}"> {{ cate.0 }}({{ tag.1 }})</a></p>
                        {% endfor %}
    
                    </div>
                </div>
                <div class="panel panel-success">
                    <div class="panel-heading">随笔归档</div>
                    <div class="panel-body">
                        {% for date in date_list %}
                            <p><a href="/{{ username }}/archive/{{ date.0 }}"> {{ date.0 }}({{ tag.1 }})</a></p>
                        {% endfor %}
    
                    </div>
                </div>
    
            </div>
            <div class="col-md-9">
                <div class="article_list">
                    {% for article in article_list %}
                        <div class="article-item clearfix">
                            <h5><a href="">{{ article.title }}</a></h5>
                            <div class="article-desc">
                                {{ article.desc }}
                            </div>
                        </div>
                        <div class="small pub_info pull-right">
                            <span>发布于&nbsp;&nbsp;{{ article.create_time|date:"Y-m-d H:i" }}</span>
                            <span class="glyphicon glyphicon-comment"></span>评论({{ article.comment_count }})
                            <span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ article.up_count }})
                        </div>
                        <hr>
                    {% endfor %}
                </div>
            </div>
        </div>
    </div>
    </body>
    </html>

    views.py;

    from django.shortcuts import render, HttpResponse, redirect
    
    # Create your views here.
    
    from django.http import JsonResponse
    from django.contrib import auth
    from blog.Myforms import UserForm
    from blog.models import UserInfo
    from blog import models
    from django.db.models import Count
    
    
    def login(request):
        """
        登录;
        :param request:
        :return:
        """
        if request.method == "POST":
            response = {"user": None, "msg": None}
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
            valid_code = request.POST.get("valid_code")
    
            valid_code_str = request.session.get("valid_code_str")
            if valid_code.upper() == valid_code_str.upper():
                user = auth.authenticate(username=user, password=pwd)
                if user:
                    auth.login(request, user)
                    response["user"] = user.username
                else:
                    response["msg"] = "用户名或者密码错误!"
            else:
                response["msg"] = "验证码错误!"
            return JsonResponse(response)
        return render(request, "login.html")
    
    
    def index(request):
        """
        首页;
        :param request:
        :return:
        """
        article_list = models.Article.objects.all()
    
        return render(request, "index.html", {"article_list": article_list})
    
    
    def logout(request):
        auth.logout(request)  # 等同于request.session.flush()
        return redirect("/login/")
    
    
    def get_valid_code_img(request):
        """
        基于PIL模块动态生成响应状态码图片;
        :param request:
        :return:
        """
        from blog.utils.validCode import get_valid_code_img
        data = get_valid_code_img(request)
        return HttpResponse(data)
    
    
    def register(request):
        """
        注册;
        :param request:
        :return:
        """
        # if request.method == "POST":
        if request.is_ajax():
            # print(request.POST)
            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")
                '''
                if avatar_obj:
                    user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
                else:
                    user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
                '''
                extra = {}
    
                if avatar_obj:
                    extra["avatar"] = avatar_obj
                UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
            else:
                # print(form.cleaned_data)
                # print(form.errors)
                response["msg"] = form.errors
            return JsonResponse(response)
        form = UserForm()
        return render(request, "register.html", {"form": form})
    
    
    def home_site(request, username, **kwargs):
        """
        个人站点视图函数
        :param request:
        :return:
        """
    
        print("kwargs", kwargs)  # 区分访问是的站点页面还是站点下的跳转页面;
        print("username", username)
        user = UserInfo.objects.filter(username=username).first()
        # 判断用户是否存在!
        if not user:
            return render(request, "not_found.html")
    
        # 查询当前站点对象
    
        blog = user.blog
    
        # 1 当前用户或者当前站点对应所有文章
        # 基于对象查询
        # article_list=user.article_set.all()
        # 基于 __
    
        article_list = models.Article.objects.filter(user=user)
    
        if kwargs:
            condition = kwargs.get("condition")
            param = kwargs.get("param")  # 2012-12
    
            if condition == "category":
                article_list = article_list.filter(category__title=param)
            elif condition == "tag":
                article_list = article_list.filter(tags__title=param)
            else:
                year, month = param.split("-")
                article_list = article_list.filter(create_time__year=year, create_time__month=month)
    
        # 每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")
    
        # 查询每一个分类名称以及对应的文章数
    
        # ret=models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title","c")
        # print(ret)
    
        # 查询当前站点的每一个分类名称以及对应的文章数
    
        cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
            "title", "c")
        print(cate_list)
    
        # 查询当前站点的每一个标签名称以及对应的文章数
    
        tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
        print(tag_list)
    
        # 查询当前站点每一个年月的名称以及对应的文章数
    
        # ret=models.Article.objects.extra(select={"is_recent":"create_time > '2018-09-05'"}).values("title","is_recent")
        # print(ret)
    
        # 方式1:
        date_list = models.Article.objects.filter(user=user).extra(
            select={"y_m_date": "date_format(create_time,'%%Y-%%m')"}).values("y_m_date").annotate(
            c=Count("nid")).values_list("y_m_date", "c")
        print(date_list)
    
    
    # 方式2:
    
    # from django.db.models.functions import TruncMonth
    #
    # ret=models.Article.objects.filter(user=user).annotate(month=TruncMonth("create_time")).values("month").annotate(c=Count("nid")).values_list("month","c")
    # print("ret----->",ret)
    
        return render(request, "home_site.html",
                  {"username": username, "blog": blog, "article_list": article_list, "tag_list": tag_list,
                   "cate_list": cate_list,"date_list":date_list})
    
    # def get_classification_data(username):
    #     user = UserInfo.objects.filter(username=username).first()
    #     blog = user.blog
    #     cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
    #         "title", "c")
    #     tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
    #     date_list = models.Article.objects.filter(user=user).extra(
    #         select={"y_m_date": "date_format(create_time,'%%y/%%m')"}
    #     ).values("y_m_date").annotate(c=Count("nid")).values_list("y_m_date", "c")
    #     return {"blog": blog, "cate_list": cate_list, "date_list": date_list, "tag_list": tag_list}

    47-博客系统之文章详情页的设计

    1、路由urls.py配置;

    re_path(r'^(?P<username>w+)/articles/(?P<article_id>d+)$', views.article_detail),

    48-博客系统之文章详情页的构建

    1、进行程序解耦合操作,分离base.html,基于{% extends 'base.html' %}进行调用;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>home_site</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
        <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
        <script src="/static/blog/bs/js/bootstrap.min.js"></script>
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
            }
    
            .header {
                width: 100%;
                height: 60px;
                background-color: #369;
            }
    
            .header .title {
                font-size: 18px;
                font-weight: 100;
                line-height: 60px;
                color: white;
                margin-left: 15px;
                margin-top: -10px;
    
            }
    
            .backend {
                float: right;
                color: white;
                text-decoration: none;
                font-size: 14px;
                margin-right: 10px;
                margin-top: 10px;
            }
    
            .pub_info {
                margin-top: 10px;
                color: darkgray;
            }
    
        </style>
    </head>
    <body>
    <div class="header">
        <div class="content">
            <p class="title">
                <span>{{ blog.title }}</span>
                <a href="" class="backend">管理</a>
            </p>
        </div>
    </div>
    <div class="container">
        <div class="row">
            <div class="col-md-3">
                <div class="panel panel-warning">
                    <div class="panel-heading">我的标签</div>
                    <div class="panel-body">
                        {% for tag in tag_list %}
                            <p><a href="/{{ username }}/tag/{{ tag.0 }}"> {{ tag.0 }}({{ tag.1 }})</a></p>
                        {% endfor %}
    
                    </div>
                </div>
                <div class="panel panel-danger">
                    <div class="panel-heading">随笔分类</div>
                    <div class="panel-body">
                        {% for cate in cate_list %}
                            <p><a href="/{{ username }}/category/{{ cate.0 }}"> {{ cate.0 }}({{ tag.1 }})</a></p>
                        {% endfor %}
    
                    </div>
                </div>
                <div class="panel panel-success">
                    <div class="panel-heading">随笔归档</div>
                    <div class="panel-body">
                        {% for date in date_list %}
                            <p><a href="/{{ username }}/archive/{{ date.0 }}"> {{ date.0 }}({{ tag.1 }})</a></p>
                        {% endfor %}
    
                    </div>
                </div>
    
            </div>
            <div class="col-md-9">
                {% block content %}
    
                {% endblock %}
            </div>
        </div>
    </div>
    </body>
    </html>

    2、生成article_detail.html;

    {% extends 'base.html' %}

    3、重用的代码优化,编写独立函数进行调用;

    from django.shortcuts import render, HttpResponse, redirect
    
    # Create your views here.
    
    from django.http import JsonResponse
    from django.contrib import auth
    from blog.Myforms import UserForm
    from blog.models import UserInfo
    from blog import models
    from django.db.models import Count
    
    
    def login(request):
        """
        登录;
        :param request:
        :return:
        """
        if request.method == "POST":
            response = {"user": None, "msg": None}
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
            valid_code = request.POST.get("valid_code")
    
            valid_code_str = request.session.get("valid_code_str")
            if valid_code.upper() == valid_code_str.upper():
                user = auth.authenticate(username=user, password=pwd)
                if user:
                    auth.login(request, user)
                    response["user"] = user.username
                else:
                    response["msg"] = "用户名或者密码错误!"
            else:
                response["msg"] = "验证码错误!"
            return JsonResponse(response)
        return render(request, "login.html")
    
    
    def index(request):
        """
        首页;
        :param request:
        :return:
        """
        article_list = models.Article.objects.all()
    
        return render(request, "index.html", {"article_list": article_list})
    
    
    def logout(request):
        auth.logout(request)  # 等同于request.session.flush()
        return redirect("/login/")
    
    
    def get_valid_code_img(request):
        """
        基于PIL模块动态生成响应状态码图片;
        :param request:
        :return:
        """
        from blog.utils.validCode import get_valid_code_img
        data = get_valid_code_img(request)
        return HttpResponse(data)
    
    
    def register(request):
        """
        注册;
        :param request:
        :return:
        """
        # if request.method == "POST":
        if request.is_ajax():
            # print(request.POST)
            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")
                '''
                if avatar_obj:
                    user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar = avatar_obj )
                else:
                    user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
                '''
                extra = {}
    
                if avatar_obj:
                    extra["avatar"] = avatar_obj
                UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
            else:
                # print(form.cleaned_data)
                # print(form.errors)
                response["msg"] = form.errors
            return JsonResponse(response)
        form = UserForm()
        return render(request, "register.html", {"form": form})
    
    
    def get_query_data(username):
        user = UserInfo.objects.filter(username=username).first()
        # 判断用户是否存在!
        if not user:
            return render(request, "not_found.html")
    
        # 查询当前站点对象
    
        blog = user.blog
        # 查询当前站点的每一个分类名称以及对应的文章数
    
        cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
            "title", "c")
        tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
        date_list = models.Article.objects.filter(user=user).extra(
            select={"y_m_date": "date_format(create_time,'%%Y-%%m')"}).values("y_m_date").annotate(
            c=Count("nid")).values_list("y_m_date", "c")
        return {"blog": blog, "cate_list": cate_list, "date_list": date_list, "tag_list": tag_list}
    
    
    def article_detail(request, username, article_id):
        context = get_query_data(username)
        return render(request, "article_detail.html", context)
    
    
    def home_site(request, username, **kwargs):
        """
        个人站点视图函数
        :param request:
        :return:
        """
    
        print("kwargs", kwargs)  # 区分访问是的站点页面还是站点下的跳转页面;
        print("username", username)
        user = UserInfo.objects.filter(username=username).first()
        # 判断用户是否存在!
        if not user:
            return render(request, "not_found.html")
    
        # 查询当前站点对象
    
        blog = user.blog
    
        # 1 当前用户或者当前站点对应所有文章
        # 基于对象查询
        # article_list=user.article_set.all()
        # 基于 __
    
        article_list = models.Article.objects.filter(user=user)
    
        if kwargs:
            condition = kwargs.get("condition")
            param = kwargs.get("param")  # 2012-12
    
            if condition == "category":
                article_list = article_list.filter(category__title=param)
            elif condition == "tag":
                article_list = article_list.filter(tags__title=param)
            else:
                year, month = param.split("-")
                article_list = article_list.filter(create_time__year=year, create_time__month=month)
    
        # 每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")
    
        # 查询每一个分类名称以及对应的文章数
    
        # ret=models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title","c")
        # print(ret)
    
        # 查询当前站点的每一个分类名称以及对应的文章数
    
        cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
            "title", "c")
        print(cate_list)
    
        # 查询当前站点的每一个标签名称以及对应的文章数
    
        tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
        print(tag_list)
    
        # 查询当前站点每一个年月的名称以及对应的文章数
    
        # ret=models.Article.objects.extra(select={"is_recent":"create_time > '2018-09-05'"}).values("title","is_recent")
        # print(ret)
    
        # 方式1:
        date_list = models.Article.objects.filter(user=user).extra(
            select={"y_m_date": "date_format(create_time,'%%Y-%%m')"}).values("y_m_date").annotate(
            c=Count("nid")).values_list("y_m_date", "c")
        print(date_list)
    
        # 方式2:
    
        # from django.db.models.functions import TruncMonth
        #
        # ret=models.Article.objects.filter(user=user).annotate(month=TruncMonth("create_time")).values("month").annotate(c=Count("nid")).values_list("month","c")
        # print("ret----->",ret)
    
        return render(request, "home_site.html",
                      {"username": username, "blog": blog, "article_list": article_list, "tag_list": tag_list,
                       "cate_list": cate_list, "date_list": date_list})

    49-博客系统之文章详情页的inclution_tag

    1、配置base.html;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>home_site</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
        <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
        <script src="/static/blog/bs/js/bootstrap.min.js"></script>
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
            }
    
            .header {
                width: 100%;
                height: 60px;
                background-color: #369;
            }
    
            .header .title {
                font-size: 18px;
                font-weight: 100;
                line-height: 60px;
                color: white;
                margin-left: 15px;
                margin-top: -10px;
    
            }
    
            .backend {
                float: right;
                color: white;
                text-decoration: none;
                font-size: 14px;
                margin-right: 10px;
                margin-top: 10px;
            }
    
            .pub_info {
                margin-top: 10px;
                color: darkgray;
            }
    
        </style>
    </head>
    <body>
    <div class="header">
        <div class="content">
            <p class="title">
                <span>{{ blog.title }}</span>
                <a href="" class="backend">管理</a>
            </p>
        </div>
    </div>
    <div class="container">
        <div class="row">
            <div class="col-md-3 menu">
                {% load my_tags %}
                {% get_classification_style username %}
    
            </div>
            <div class="col-md-9">
                {% block content %}
    
                {% endblock %}
            </div>
        </div>
    </div>
    </body>
    </html>

    2、引入自定义标签templatetags;

    3、使用register.inclusion_tag("classification.html")

    # @register.simple_tag
    @register.inclusion_tag("classification.html")
    def get_classification_style(username):
        user = models.UserInfo.objects.filter(username=username).first()
        # 查询当前站点对象
        blog = user.blog
        # 查询当前站点的每一个分类名称以及对应的文章数
        cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list(
            "title", "c")
        tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c")
        date_list = models.Article.objects.filter(user=user).extra(
            select={"y_m_date": "date_format(create_time,'%%Y-%%m')"}).values("y_m_date").annotate(
            c=Count("nid")).values_list("y_m_date", "c")
        return {"blog": blog, "cate_list": cate_list, "date_list": date_list, "tag_list": tag_list}

    4、总体来讲,降低程序耦合度,提高可拓展性;

    <div>
        <div class="panel panel-warning">
            <div class="panel-heading">我的标签</div>
            <div class="panel-body">
                {% for tag in tag_list %}
                    <p><a href="/{{ username }}/tag/{{ tag.0 }}"> {{ tag.0 }}({{ tag.1 }})</a></p>
                {% endfor %}
    
            </div>
        </div>
        <div class="panel panel-danger">
            <div class="panel-heading">随笔分类</div>
            <div class="panel-body">
                {% for cate in cate_list %}
                    <p><a href="/{{ username }}/category/{{ cate.0 }}"> {{ cate.0 }}({{ tag.1 }})</a></p>
                {% endfor %}
    
            </div>
        </div>
        <div class="panel panel-success">
            <div class="panel-heading">随笔归档</div>
            <div class="panel-body">
                {% for date in date_list %}
                    <p><a href="/{{ username }}/archive/{{ date.0 }}"> {{ date.0 }}({{ tag.1 }})</a></p>
                {% endfor %}
    
            </div>
        </div>
    </div>

    50-博客系统之文章详情页渲染标签的标签字符串转义1

    1、Django内部将标签字符串做了转义处理;

    2、article_detail.html;

    {% extends 'base.html' %}
    
    {% block content %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
        {{ article_obj.content }}
    
        </div>
    {% endblock %}

    51-博客系统之文章详情页渲染的标签字符串转义2

    1、设置safe属性,防止xss攻击;

    2、article_detail.html;

    {% extends 'base.html' %}
    
    {% block content %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
        {{ article_obj.content|safe }}
    
        </div>
    {% endblock %}

    52-博客系统之文章点赞样式的构建

    1、点赞的样式仿照博客园进行构建,并且拆分独立css样式文件,使用link方法进行引入;

    article_detail.html;

    {% extends 'base.html' %}
    
    {% block content %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
    
        </div>
        <div id="div_digg">
            <div class="diggit">
                <span class="diggnum" id="digg_count">1</span>
            </div>
            <div class="buryit">
                <span class="burynum" id="bury_count">0</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color:red;"></div>
        </div>
    {% endblock %}

    home_site.css;

    * {
        margin: 0;
        padding: 0;
    }
    
    .header {
         100%;
        height: 60px;
        background-color: #369;
    }
    
    .header .title {
        font-size: 18px;
        font-weight: 100;
        line-height: 60px;
        color: white;
        margin-left: 15px;
        margin-top: -10px;
    
    }
    
    .backend {
        float: right;
        color: white;
        text-decoration: none;
        font-size: 14px;
        margin-right: 10px;
        margin-top: 10px;
    }
    
    .pub_info {
        margin-top: 10px;
        color: darkgray;
    }

    article_detail.css;

    .article_info .title {
        margin-bottom: 20px;
    }
    
    #div_digg {
        float: right;
        margin-bottom: 10px;
        margin-right: 30px;
        font-size: 12px;
         125px;
        text-align: center;
        margin-top: 10px;
    }
    
    .diggit {
        float: left;
         46px;
        height: 52px;
        background: url(/static/blog/img/upup.gif) no-repeat;
        text-align: center;
        cursor: pointer;
        margin-top: 2px;
        padding-top: 5px;
    }
    
    .buryit {
        float: right;
        margin-left: 20px;
         46px;
        height: 52px;
        background: url(/static/blog/img/downdown.gif) no-repeat;
        text-align: center;
        cursor: pointer;
        margin-top: 2px;
        padding-top: 5px;
    }
    .clear{
        clear: both;
    }

    53-博客系统之文章点赞事件的绑定

    1、base.html引入本地静态jQuery文件;

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>home_site</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
        <script src="/static/blog/js/jquery-3.3.1.min.js"></script>
        <script src="/static/blog/bs/js/bootstrap.min.js"></script>
        <link rel="stylesheet" href="/static/blog/css/home_site.css">
        <link rel="stylesheet" href="/static/blog/css/article_detail.css">
    </head>

    2、article_detail.html添加js事件;

    {% extends 'base.html' %}
    
    {% block content %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
    
        </div>
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">1</span>
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">0</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color:red;"></div>
        </div>
        <script>
        $("#div_digg .action").click(function () {
            var is_up = $(this).hasClass("diggit")
            alert(is_up)
        })
        </script>
    {% endblock %}

    54-博客系统之文章点赞的保存

    1、绑定Ajax事件;

    {% extends 'base.html' %}
    
    {% block content %}
        {% csrf_token %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
    
        </div>
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color:red;"></div>
        </div>
        <script>
            $("#div_digg .action").click(function () {
                var is_up = $(this).hasClass("diggit");
                alert(is_up)
    
                //传Ajax事件;
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "is_up": is_up,
                        "article_id": "{{ article_obj.pk }}",
    
                    },
                    success: function (data) {
                        console.log(data)
                    }
                })
            });
        </script>
    {% endblock %}

    2、基于digg视图函数进行数据的添加;

    def digg(request):
        """
        点赞视图函数;
        :param request:
        :return:
        """
    
        print(request.POST)
        article_id = request.POST.get("article_id")
        is_up = json.loads(request.POST.get("is_up"))
        # 点赞人即当前登录人;
    
        user_id = request.user.pk
        ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
        return HttpResponse("ok")

    3、新增urls.py下的digg视图;

    """cnblogs URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/2.1/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  path('', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.urls import include, path
        2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
    """
    from django.contrib import admin
    from django.urls import path, re_path
    from blog import views
    from django.views.static import serve
    from cnblogs import settings
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('login/', views.login),
        path('index/', views.index),
        path('logout/', views.logout),
        re_path('^$', views.index),
        path('get_validCode_img/', views.get_valid_code_img),
        path('register/', views.register),
        path('digg/', views.digg),
        # media配置;
        re_path(r'media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),
        re_path(r'^(?P<username>w+)/articles/(?P<article_id>d+)$', views.article_detail),
        # 个人站点下的跳转URL;# 接受的参数,home_site(request,username,condition,param)
        re_path(r'^(?P<username>w+)/(?P<condition>tag|category|archive)/(?P<param>.*)/$', views.home_site),
    
        # re_path(r'^(?P<username>w+)/category/.*/$', views.home_site),
        # re_path(r'^(?P<username>w+)/archive/.*/$', views.home_site),
        # 个人站点URL;#接收的参数home_site(request,username="cxz")
        re_path(r'^(?P<username>w+)/$', views.home_site),
    ]

    55-博客系统之文章点赞数的数据同步

    1、通过update方法像数据库中新增数据;

    def digg(request):
        """
        点赞视图函数;
        :param request:
        :return:
        """
    
        print(request.POST)
        article_id = request.POST.get("article_id")
        is_up = json.loads(request.POST.get("is_up"))
        # 点赞人即当前登录人;
    
        user_id = request.user.pk
        ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
        if is_up:
            models.Article.objects.filter(pk=article_id).update(up_count=F("up_count") + 1)
        return HttpResponse("ok")

    56-博客系统之文章点赞的提示重复操作

    1、评论与点赞只允许一次的逻辑以及提示消息;

    def digg(request):
        """
        点赞视图函数;
        :param request:
        :return:
        """
    
        print(request.POST)
        article_id = request.POST.get("article_id")
        is_up = json.loads(request.POST.get("is_up"))
        # 点赞、点踩的人即当前登录人;
    
        user_id = request.user.pk
        obj = models.ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
        response = {"state": True}
        if not obj:
            ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)
    
            queryset = models.Article.objects.filter(pk=article_id)
            if is_up:
                queryset.update(up_count=F("up_count") + 1)
            else:
                queryset.update(down_count=F("down_count") + 1)
        else:
            response["state"] = False
            response["handled"] = obj.is_up
    
        return JsonResponse(response)

    2、前端js代码的业务逻辑;

     <script>
            $("#div_digg .action").click(function () {
                var is_up = $(this).hasClass("diggit");
               // alert(is_up)
    
                //传Ajax事件;
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "is_up": is_up,
                        "article_id": "{{ article_obj.pk }}",
    
                    },
                    success: function (data) {
                        console.log(data);
                        if (data.state){
    
                        }else {
                            if (data.handled){
                                $("#digg_tips").html("您已经推荐过!")
                            }else{
                                $("#digg_tips").html("您已经反对过!")
                            }
                            setTimeout(function () {
                                $("#digg_tips").html("")
                            },1000)
                        }
                    }
                })
            });
        </script>

    57-博客系统之文章点赞数的Ajax更新

    1、点赞功能的局部刷新功能;

      <script>
            $("#div_digg .action").click(function () {
                var is_up = $(this).hasClass("diggit");
                // alert(is_up)
    
                //传Ajax事件;
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "is_up": is_up,
                        "article_id": "{{ article_obj.pk }}",
    
                    },
                    success: function (data) {
                        console.log(data);
                        if (data.state) {
                            if (is_up) {
                                var val = parseInt($("#digg_count").text());
                                $("#digg_count").text(val + 1);
                            } else {
                                var val = parseInt($("#bury_count").text());
                                $("#bury_count").text(val + 1);
                            }
                        } else {
                            if (data.handled) {
                                $("#digg_tips").html("您已经推荐过!")
                            } else {
                                $("#digg_tips").html("您已经反对过!")
                            }
                            setTimeout(function () {
                                $("#digg_tips").html("")
                            }, 1000)
                        }
                    }
                })
            });
        </script>

    58-博客系统之文章点赞代码的优化

    1、减少重复代码,提高程序精简性;

    2、三元运算以及$obj = $(this).children("span")的使用;

    {% extends 'base.html' %}
    
    {% block content %}
        {% csrf_token %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
    
        </div>
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color:red;"></div>
        </div>
        <script>
            $("#div_digg .action").click(function () {
                var is_up = $(this).hasClass("diggit");
                // alert(is_up)
    
                //传Ajax事件;
                $obj = $(this).children("span");
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "is_up": is_up,
                        "article_id": "{{ article_obj.pk }}",
    
                    },
                    success: function (data) {
                        console.log(data);
                        if (data.state) {
                            var val = parseInt($("#digg_count").text());
                            $obj.text(val + 1);
    
                        } else {
                            var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")
                            }, 1000)
                        }
                    }
                })
            });
        </script>
    {% endblock %}

    59-博客系统之评论功能的实现流程

    1、评论功能设计思路;

    60-博客系统之评论样式

    1、评论功能样式确定;

    article_detail.html;

    {% extends 'base.html' %}
    
    {% block content %}
        {% csrf_token %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
    
        </div>
        <div class="clearfix">
            <div id="div_digg">
                <div class="diggit action">
                    <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
                </div>
                <div class="buryit action">
                    <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips" style="color:red;"></div>
            </div>
        </div>
        <div class="commonts">
            <p>发表评论</p>
            <p>
                昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                          value="{{ request.user.username }}">
            </p>
            <p>评论内容:</p>
            <textarea name="" id="" cols="60" rows="10"></textarea>
            <p>
                <button class="btn btn-default common_btn">提交评论</button>
            </p>
        </div>
        <script>
            $("#div_digg .action").click(function () {
                var is_up = $(this).hasClass("diggit");
                // alert(is_up)
    
                //传Ajax事件;
                $obj = $(this).children("span");
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "is_up": is_up,
                        "article_id": "{{ article_obj.pk }}",
    
                    },
                    success: function (data) {
                        console.log(data);
                        if (data.state) {
                            var val = parseInt($("#digg_count").text());
                            $obj.text(val + 1);
    
                        } else {
                            var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")
                            }, 1000)
                        }
                    }
                })
            });
        </script>
    {% endblock %}

    2、评论功能组件设置之——发表评论、昵称(动态引用)、评论头像、评论内容、提交评论;

    61-博客系统之提交根评论

    1、提交评论&保存评论;

    2、清除评论框操作;

    {% extends 'base.html' %}
    
    {% block content %}
        {% csrf_token %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
    
        </div>
        <div class="clearfix">
            <div id="div_digg">
                <div class="diggit action">
                    <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
                </div>
                <div class="buryit action">
                    <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips" style="color:red;"></div>
            </div>
        </div>
        <div class="commonts">
            <p>发表评论</p>
            <p>
                昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                          value="{{ request.user.username }}">
            </p>
            <p>评论内容:</p>
            <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
            <p>
                <button class="btn btn-default common_btn">提交评论</button>
            </p>
        </div>
        <script>
            //点赞请求;
            $("#div_digg .action").click(function () {
                var is_up = $(this).hasClass("diggit");
                // alert(is_up)
    
                //传Ajax事件;
                $obj = $(this).children("span");
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "is_up": is_up,
                        "article_id": "{{ article_obj.pk }}",
    
                    },
                    success: function (data) {
                        console.log(data);
                        if (data.state) {
                            var val = parseInt($("#digg_count").text());
                            $obj.text(val + 1);
    
                        } else {
                            var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")
                            }, 1000)
                        }
                    }
                })
            });
            //评论请求;
            $(".common_btn").click(function () {
                var pid = "";
                var content = $("#comment_content").val();
                $.ajax({
                    url: "/comment/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "article_id": "{{ article_obj.pk }}",
                        "content": content,
                        pid:pid,
                    },
                    success: function (data) {
                        console.log(data);
                        //清空评论框;
                        $("#comment_content").val("")
                    }
                })
            })
        </script>
    {% endblock %}

    comment视图函数;

    def comment(request):
        print(request.POST)
        article_id = request.POST.get("article_id")
        pid = request.POST.get("pid")
        content = request.POST.get("content")
        user_id = request.user.pk
        comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,
                                                    parent_comment_id=pid)
        return HttpResponse("comment")

    62-博客系统之render显示根评论

    评论列表的render显示;

    {% extends 'base.html' %}
    
    {% block content %}
        {% csrf_token %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
    
        </div>
        <div class="clearfix">
            <div id="div_digg">
                <div class="diggit action">
                    <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
                </div>
                <div class="buryit action">
                    <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips" style="color:red;"></div>
            </div>
        </div>
        <div class="commonts">
            <p>评论列表</p>
            <ul class="list-group comment_list">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <div>
                            <a href="">#{{ forloop.counter }}楼</a> &nbsp;&nbsp;
                            <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span> &nbsp;&nbsp;
                            <a href=""><span>{{ comment.user.username }}</span></a> &nbsp;&nbsp;
                            <a href="" class="pull-right">回复</a>
                        </div>
                        <div class="comment_con">
                            <p>{{ comment.content }}</p>
                        </div>
                    </li>
    
                {% endfor %}
    
            </ul>
            <p>发表评论</p>
            <p>
                昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                          value="{{ request.user.username }}">
            </p>
            <p>评论内容:</p>
            <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
            <p>
                <button class="btn btn-default common_btn">提交评论</button>
            </p>
        </div>
        <script>
            //点赞请求;
            $("#div_digg .action").click(function () {
                var is_up = $(this).hasClass("diggit");
                // alert(is_up)
    
                //传Ajax事件;
                $obj = $(this).children("span");
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "is_up": is_up,
                        "article_id": "{{ article_obj.pk }}",
    
                    },
                    success: function (data) {
                        console.log(data);
                        if (data.state) {
                            var val = parseInt($("#digg_count").text());
                            $obj.text(val + 1);
    
                        } else {
                            var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")
                            }, 1000)
                        }
                    }
                })
            });
            //评论请求;
            $(".common_btn").click(function () {
                var pid = "";
                var content = $("#comment_content").val();
                $.ajax({
                    url: "/comment/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "article_id": "{{ article_obj.pk }}",
                        "content": content,
                        pid: pid,
                    },
                    success: function (data) {
                        console.log(data);
                        //清空评论框;
                        $("#comment_content").val("")
                    }
                })
            })
        </script>
    {% endblock %}

    article_detail.css;

    .article_info .title {
        margin-bottom: 20px;
    }
    
    #div_digg {
        float: right;
        margin-bottom: 10px;
        margin-right: 30px;
        font-size: 12px;
         125px;
        text-align: center;
        margin-top: 10px;
    }
    
    .diggit {
        float: left;
         46px;
        height: 52px;
        background: url(/static/blog/img/upup.gif) no-repeat;
        text-align: center;
        cursor: pointer;
        margin-top: 2px;
        padding-top: 5px;
    }
    
    .buryit {
        float: right;
        margin-left: 20px;
         46px;
        height: 52px;
        background: url(/static/blog/img/downdown.gif) no-repeat;
        text-align: center;
        cursor: pointer;
        margin-top: 2px;
        padding-top: 5px;
    }
    
    .clear {
        clear: both;
    }
    
    input.author {
        background-image: url(/static/blog/img/icon_form.gif);
        background-repeat: no-repeat;
        border: 1px solid #ccc;
        padding: 4px 4px 4px 30px;
         300px;
        font-size: 13px;
        background-position: 3px -3px;
    }
    .comment_con{
        margin-top: 10px;
    }

    63-博客系统之Ajax显示根评论

    1、基于Ajax技术展示的评论效果;

    2、article_detail.html;

    {% extends 'base.html' %}
    
    {% block content %}
        {% csrf_token %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
    
        </div>
        <div class="clearfix">
            <div id="div_digg">
                <div class="diggit action">
                    <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
                </div>
                <div class="buryit action">
                    <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips" style="color:red;"></div>
            </div>
        </div>
        <div class="commonts">
            <p>评论列表</p>
            <ul class="list-group comment_list">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <div>
                            <a href="">#{{ forloop.counter }}楼</a> &nbsp;&nbsp;
                            <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span> &nbsp;&nbsp;
                            <a href=""><span>{{ comment.user.username }}</span></a> &nbsp;&nbsp;
                            <a href="" class="pull-right">回复</a>
                        </div>
                        <div class="comment_con">
                            <p>{{ comment.content }}</p>
                        </div>
                    </li>
    
                {% endfor %}
    
            </ul>
            <p>发表评论</p>
            <p>
                昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                          value="{{ request.user.username }}">
            </p>
            <p>评论内容:</p>
            <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
            <p>
                <button class="btn btn-default common_btn">提交评论</button>
            </p>
        </div>
        <script>
            //点赞请求;
            $("#div_digg .action").click(function () {
                var is_up = $(this).hasClass("diggit");
                // alert(is_up)
    
                //传Ajax事件;
                $obj = $(this).children("span");
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "is_up": is_up,
                        "article_id": "{{ article_obj.pk }}",
    
                    },
                    success: function (data) {
                        console.log(data);
                        if (data.state) {
                            var val = parseInt($("#digg_count").text());
                            $obj.text(val + 1);
    
                        } else {
                            var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")
                            }, 1000)
                        }
                    }
                })
            });
            //评论请求;
            $(".common_btn").click(function () {
                var pid = "";
                var content = $("#comment_content").val();
                $.ajax({
                    url: "/comment/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "article_id": "{{ article_obj.pk }}",
                        "content": content,
                        pid: pid,
                    },
                    success: function (data) {
                        console.log(data);
                        var create_time = data.create_time;
                        var username = data.username;
                        var content = data.content;
                        var s = `
                        <li class="list-group-item">
                        <div>
                            <span>${create_time}</span> &nbsp;&nbsp;
                            <a href=""><span>${username}</span></a>
                        </div>
                        <div class="comment_con">
                            <p>${content}</p>
                        </div>
                    </li>`;
                        $("ul.comment_list").append(s);
    
                        //清空评论框;
                        $("#comment_content").val("")
                    }
                })
            })
        </script>
    {% endblock %}

    3、comment视图函数;

    def comment(request):
        print(request.POST)
        article_id = request.POST.get("article_id")
        pid = request.POST.get("pid")
        content = request.POST.get("content")
        user_id = request.user.pk
        comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,
                                                    parent_comment_id=pid)
        response = {}
        response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X")
        response["username"] = request.user.username
        response["content"] = content
        return JsonResponse(response)

     

    64-博客系统之回复按钮事件

    1、点击“回复”按钮,光标自动跳转至回复栏;

    2、回复的对象@形式出现;

    {% extends 'base.html' %}
    
    {% block content %}
        {% csrf_token %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
    
        </div>
        <div class="clearfix">
            <div id="div_digg">
                <div class="diggit action">
                    <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
                </div>
                <div class="buryit action">
                    <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips" style="color:red;"></div>
            </div>
        </div>
        <div class="commonts">
            <p>评论列表</p>
            <ul class="list-group comment_list">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <div>
                            <a href="">#{{ forloop.counter }}楼</a> &nbsp;&nbsp;
                            <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span> &nbsp;&nbsp;
                            <a href=""><span>{{ comment.user.username }}</span></a> &nbsp;&nbsp;
                            <a  class="pull-right reply_btn" username = "{{ comment.user.username }}" >回复</a>
                        </div>
                        <div class="comment_con">
                            <p>{{ comment.content }}</p>
                        </div>
                    </li>
    
                {% endfor %}
    
            </ul>
            <p>发表评论</p>
            <p>
                昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                          value="{{ request.user.username }}">
            </p>
            <p>评论内容:</p>
            <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
            <p>
                <button class="btn btn-default common_btn">提交评论</button>
            </p>
        </div>
        <script>
            //点赞请求;
            $("#div_digg .action").click(function () {
                var is_up = $(this).hasClass("diggit");
                // alert(is_up)
    
                //传Ajax事件;
                $obj = $(this).children("span");
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "is_up": is_up,
                        "article_id": "{{ article_obj.pk }}",
    
                    },
                    success: function (data) {
                        console.log(data);
                        if (data.state) {
                            var val = parseInt($("#digg_count").text());
                            $obj.text(val + 1);
    
                        } else {
                            var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")
                            }, 1000)
                        }
                    }
                })
            });
            //评论请求;
            $(".common_btn").click(function () {
                var pid = "";
                var content = $("#comment_content").val();
                $.ajax({
                    url: "/comment/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "article_id": "{{ article_obj.pk }}",
                        "content": content,
                        pid: pid,
                    },
                    success: function (data) {
                        console.log(data);
                        var create_time = data.create_time;
                        var username = data.username;
                        var content = data.content;
                        var s = `
                        <li class="list-group-item">
                        <div>
                            <span>${create_time}</span> &nbsp;&nbsp;
                            <a href=""><span>${username}</span></a>
                        </div>
                        <div class="comment_con">
                            <p>${content}</p>
                        </div>
                    </li>`;
                        $("ul.comment_list").append(s);
    
                        //清空评论框;
                        $("#comment_content").val("")
                    }
                })
            });
    
        //回复按钮事件;
        $(".reply_btn").click(function () {
            $("#comment_content").focus();
            var val = "@"+$(this).attr("username")+"
    ";
            $("#comment_content").val(val)
        })
        </script>
    {% endblock %}

    65-博客系统之提交子评论

    1、字评论提交之pid号判断处理;

    2、清空评论框的pid值;

    {% extends 'base.html' %}
    
    {% block content %}
        {% csrf_token %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
    
        </div>
        <div class="clearfix">
            <div id="div_digg">
                <div class="diggit action">
                    <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
                </div>
                <div class="buryit action">
                    <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips" style="color:red;"></div>
            </div>
        </div>
        <div class="commonts">
            <p>评论列表</p>
            <ul class="list-group comment_list">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <div>
                            <a href="">#{{ forloop.counter }}楼</a> &nbsp;&nbsp;
                            <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span> &nbsp;&nbsp;
                            <a href=""><span>{{ comment.user.username }}</span></a> &nbsp;&nbsp;
                            <a class="pull-right reply_btn" username="{{ comment.user.username }} "
                               comment_pk="{{ comment.pk }}">回复</a>
                        </div>
                        <div class="comment_con">
                            <p>{{ comment.content }}</p>
                        </div>
                    </li>
    
                {% endfor %}
    
            </ul>
            <p>发表评论</p>
            <p>
                昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                          value="{{ request.user.username }}">
            </p>
            <p>评论内容:</p>
            <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
            <p>
                <button class="btn btn-default common_btn">提交评论</button>
            </p>
        </div>
        <script>
            //点赞请求;
            $("#div_digg .action").click(function () {
                var is_up = $(this).hasClass("diggit");
                // alert(is_up)
    
                //传Ajax事件;
                $obj = $(this).children("span");
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "is_up": is_up,
                        "article_id": "{{ article_obj.pk }}",
    
                    },
                    success: function (data) {
                        console.log(data);
                        if (data.state) {
                            var val = parseInt($("#digg_count").text());
                            $obj.text(val + 1);
    
                        } else {
                            var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")
                            }, 1000)
                        }
                    }
                })
            });
            //评论请求;
            var pid = "";
            $(".common_btn").click(function () {
    
                var content = $("#comment_content").val();
                if (pid) {
                    var index = content.indexOf("
    ");
                    content = content.slice(index + 1)
                }
                $.ajax({
                    url: "/comment/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "article_id": "{{ article_obj.pk }}",
                        "content": content,
                        pid: pid,
                    },
                    success: function (data) {
                        console.log(data);
                        var create_time = data.create_time;
                        var username = data.username;
                        var content = data.content;
                        var s = `
                        <li class="list-group-item">
                        <div>
                            <span>${create_time}</span> &nbsp;&nbsp;
                            <a href=""><span>${username}</span></a>
                        </div>
                        <div class="comment_con">
                            <p>${content}</p>
                        </div>
                    </li>`;
                        $("ul.comment_list").append(s);
    
                        //清空评论框;
                        pid = "";
                        $("#comment_content").val("")
                    }
                })
            });
    
            //回复按钮事件;
            $(".reply_btn").click(function () {
                $("#comment_content").focus();
                var val = "@" + $(this).attr("username") + "
    ";
                $("#comment_content").val(val);
                pid = $(this).attr("comment_pk");
            })
        </script>
    {% endblock %}

    66-博客系统之render显示

    1、通过render方法进行子评论的显示;

    2、Bootstrap的well属性的引用;

                     {% if comment.parent_comment_id %}
                            <div class="pid_info well">
                                <p>
                                    {{ comment.parent_comment.user.username }}:{{ comment.parent_comment.content }}
                                </p>
                            </div>
    
                        {% endif %}

    67-博客系统之Ajax显示子评论的思路

    1、基于Ajax实现局部刷新功能;

            var pid = "";
            $(".common_btn").click(function () {
    
                var content = $("#comment_content").val();
                if (pid) {
                    var index = content.indexOf("
    ");
                    content = content.slice(index + 1)
                }
                $.ajax({
                    url: "/comment/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "article_id": "{{ article_obj.pk }}",
                        "content": content,
                        pid: pid,
                    },
                    success: function (data) {
                        console.log(data);
                        var create_time = data.create_time;
                        var username = data.username;
                        var content = data.content;
                        var s = `
                        <li class="list-group-item">
                        <div>
                            <span>${create_time}</span> &nbsp;&nbsp;
                            <a href=""><span>${username}</span></a>
                        </div>
                        <div class="comment_con">
                            <p>${content}</p>
                        </div>
                    </li>`;
                        $("ul.comment_list").append(s);
    
                        //清空评论框;
                        pid = "";
                        $("#comment_content").val("")
                    }
                })
            });

    68-博客系统之评论树简介

    1、递归展示数据的优缺点;

    69-博客系统之评论树的请求数据

    1、构建评论树的url以及视图函数;

        path('get_comment_tree/', views.get_comment_tree),
    def get_comment_tree(request):
        article_id = request.GET.get("article_id")
        ret = list(models.Comment.objects.filter(article_id=article_id).values("pk", "content", "parent_comment_id"))
        return JsonResponse(ret, safe=False)

    70-博客系统之展开评论树

    1、点击评论树展示根评论;

    {% extends 'base.html' %}
    
    {% block content %}
        {% csrf_token %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
    
        </div>
        <div class="clearfix">
            <div id="div_digg">
                <div class="diggit action">
                    <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
                </div>
                <div class="buryit action">
                    <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips" style="color:red;"></div>
            </div>
        </div>
        <div class="commonts list-group">
            <p class="tree_btn">评论树</p>
            <div class="comment_tree">
    
    
            </div>
            <script>
                $(".tree_btn").click(function () {
    
                    $.ajax({
                        url: "/get_comment_tree",
                        type: "get",
                        data: {
                            article_id: "{{ article_obj.pk }}"
                        },
                        success: function (data) {
                            console.log(data)
                            $.each(data, function (index, comment_object) {
    
                                var pk = comment_object.pk;
                                var content = comment_object.content;
                                var parent_comment_id = comment_object.parent_comment_id;
                                if (!parent_comment_id) {
                                    var s = '<div comment_id=' + pk + '><span>' + content + '</span> </div>';
                                    $(".comment_tree").append(s)
                                }
                            })
    
                        }
                    })
                })
            </script>
            <p>评论列表</p>
            <ul class="list-group comment_list">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <div>
                            <a href="">#{{ forloop.counter }}楼</a> &nbsp;&nbsp;
                            <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span> &nbsp;&nbsp;
                            <a href=""><span>{{ comment.user.username }}</span></a> &nbsp;&nbsp;
                            <a class="pull-right reply_btn" username="{{ comment.user.username }} "
                               comment_pk="{{ comment.pk }}">回复</a>
                        </div>
                        {% if comment.parent_comment_id %}
                            <div class="pid_info well">
                                <p>
                                    {{ comment.parent_comment.user.username }}:{{ comment.parent_comment.content }}
                                </p>
                            </div>
    
                        {% endif %}
                        <div class="comment_con">
                            <p>{{ comment.content }}</p>
                        </div>
                    </li>
    
                {% endfor %}
    
            </ul>
            <p>发表评论</p>
            <p>
                昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                          value="{{ request.user.username }}">
            </p>
            <p>评论内容:</p>
            <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
            <p>
                <button class="btn btn-default common_btn">提交评论</button>
            </p>
        </div>
        <script>
            //点赞请求;
            $("#div_digg .action").click(function () {
                var is_up = $(this).hasClass("diggit");
                // alert(is_up)
    
                //传Ajax事件;
                $obj = $(this).children("span");
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "is_up": is_up,
                        "article_id": "{{ article_obj.pk }}",
    
                    },
                    success: function (data) {
                        console.log(data);
                        if (data.state) {
                            var val = parseInt($("#digg_count").text());
                            $obj.text(val + 1);
    
                        } else {
                            var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")
                            }, 1000)
                        }
                    }
                })
            });
            //评论请求;
            var pid = "";
            $(".common_btn").click(function () {
    
                var content = $("#comment_content").val();
                if (pid) {
                    var index = content.indexOf("
    ");
                    content = content.slice(index + 1)
                }
                $.ajax({
                    url: "/comment/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "article_id": "{{ article_obj.pk }}",
                        "content": content,
                        pid: pid,
                    },
                    success: function (data) {
                        console.log(data);
                        var create_time = data.create_time;
                        var username = data.username;
                        var content = data.content;
                        var s = `
                        <li class="list-group-item">
                        <div>
                            <span>${create_time}</span> &nbsp;&nbsp;
                            <a href=""><span>${username}</span></a>
                        </div>
                        <div class="comment_con">
                            <p>${content}</p>
                        </div>
                    </li>`;
                        $("ul.comment_list").append(s);
    
                        //清空评论框;
                        pid = "";
                        $("#comment_content").val("")
                    }
                })
            });
    
            //回复按钮事件;
            $(".reply_btn").click(function () {
                $("#comment_content").focus();
                var val = "@" + $(this).attr("username") + "
    ";
                $("#comment_content").val(val);
                pid = $(this).attr("comment_pk");
            })
        </script>
    {% endblock %}

    71-博客系统之展开评论树2

    article_detail.html;

    {% extends 'base.html' %}
    
    {% block content %}
        {% csrf_token %}
        <h3 class="text-center">{{ article_obj.title }}</h3>
        <div class="cont">
            {{ article_obj.content|safe }}
    
        </div>
        <div class="clearfix">
            <div id="div_digg">
                <div class="diggit action">
                    <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
                </div>
                <div class="buryit action">
                    <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips" style="color:red;"></div>
            </div>
        </div>
        <div class="commonts list-group">
            <p class="tree_btn">评论树</p>
            <div class="comment_tree">
    
    
            </div>
            <script>
                $(".tree_btn").click(function () {
    
                    $.ajax({
                        url: "/get_comment_tree",
                        type: "get",
                        data: {
                            article_id: "{{ article_obj.pk }}"
                        },
                        success: function (data) {
                            console.log(data)
                            $.each(data, function (index, comment_object) {
    
                                var pk = comment_object.pk;
                                var content = comment_object.content;
                                var parent_comment_id = comment_object.parent_comment_id;
                                var s = '<div class = "comment_item" comment_id=' + pk + '><span>' + content + '</span> </div>';
                                if (!parent_comment_id) {
    
                                    $(".comment_tree").append(s)
                                } else {
                                    $("[comment_id=" + parent_comment_id + "]").append(s);
                                }
                            })
    
                        }
                    })
                })
            </script>
            <p>评论列表</p>
            <ul class="list-group comment_list">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <div>
                            <a href="">#{{ forloop.counter }}楼</a> &nbsp;&nbsp;
                            <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span> &nbsp;&nbsp;
                            <a href=""><span>{{ comment.user.username }}</span></a> &nbsp;&nbsp;
                            <a class="pull-right reply_btn" username="{{ comment.user.username }} "
                               comment_pk="{{ comment.pk }}">回复</a>
                        </div>
                        {% if comment.parent_comment_id %}
                            <div class="pid_info well">
                                <p>
                                    {{ comment.parent_comment.user.username }}:{{ comment.parent_comment.content }}
                                </p>
                            </div>
    
                        {% endif %}
                        <div class="comment_con">
                            <p>{{ comment.content }}</p>
                        </div>
                    </li>
    
                {% endfor %}
    
            </ul>
            <p>发表评论</p>
            <p>
                昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                          value="{{ request.user.username }}">
            </p>
            <p>评论内容:</p>
            <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
            <p>
                <button class="btn btn-default common_btn">提交评论</button>
            </p>
        </div>
        <script>
            //点赞请求;
            $("#div_digg .action").click(function () {
                var is_up = $(this).hasClass("diggit");
                // alert(is_up)
    
                //传Ajax事件;
                $obj = $(this).children("span");
                $.ajax({
                    url: "/digg/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "is_up": is_up,
                        "article_id": "{{ article_obj.pk }}",
    
                    },
                    success: function (data) {
                        console.log(data);
                        if (data.state) {
                            var val = parseInt($("#digg_count").text());
                            $obj.text(val + 1);
    
                        } else {
                            var val = data.handled ? "您已经推荐过!" : "您已经反对过!";
                            $("#digg_tips").html(val);
                            setTimeout(function () {
                                $("#digg_tips").html("")
                            }, 1000)
                        }
                    }
                })
            });
            //评论请求;
            var pid = "";
            $(".common_btn").click(function () {
    
                var content = $("#comment_content").val();
                if (pid) {
                    var index = content.indexOf("
    ");
                    content = content.slice(index + 1)
                }
                $.ajax({
                    url: "/comment/",
                    type: "post",
                    data: {
                        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val(),
                        "article_id": "{{ article_obj.pk }}",
                        "content": content,
                        pid: pid,
                    },
                    success: function (data) {
                        console.log(data);
                        var create_time = data.create_time;
                        var username = data.username;
                        var content = data.content;
                        var s = `
                        <li class="list-group-item">
                        <div>
                            <span>${create_time}</span> &nbsp;&nbsp;
                            <a href=""><span>${username}</span></a>
                        </div>
                        <div class="comment_con">
                            <p>${content}</p>
                        </div>
                    </li>`;
                        $("ul.comment_list").append(s);
    
                        //清空评论框;
                        pid = "";
                        $("#comment_content").val("")
                    }
                })
            });
    
            //回复按钮事件;
            $(".reply_btn").click(function () {
                $("#comment_content").focus();
                var val = "@" + $(this).attr("username") + "
    ";
                $("#comment_content").val(val);
                pid = $(this).attr("comment_pk");
            })
        </script>
    {% endblock %}

    72-博客系统之评论树的思考1

    基于主键的order_by设置;

    def get_comment_tree(request):
        article_id = request.GET.get("article_id")
        ret = list(models.Comment.objects.filter(article_id=article_id).order_by("pk").values("pk", "content", "parent_comment_id"))
        return JsonResponse(ret, safe=False)

    73-博客系统之评论树的思考2

    1、评论树不再绑定click事件,直接通过Ajax进行展示;

     <script>
                $.ajax({
                    url: "/get_comment_tree",
                    type: "get",
                    data: {
                        article_id: "{{ article_obj.pk }}"
                    },
                    success: function (comment_list) {
                        console.log(comment_list);
                        $.each(comment_list, function (index, comment_object) {
    
                            var pk = comment_object.pk;
                            var content = comment_object.content;
                            var parent_comment_id = comment_object.parent_comment_id;
                            var s = '<div class = "comment_item" comment_id=' + pk + '><span>' + content + '</span> </div>';
                            if (!parent_comment_id) {
    
                                $(".comment_tree").append(s)
                            } else {
                                $("[comment_id=" + parent_comment_id + "]").append(s);
                            }
                        })
    
                    }
                });
            </script>

    74-博客系统之评论事务操作

    1、Django中事物的引入,with transaction.atomic()方法的使用;

    2、comment视图函数的写法;

    def comment(request):
        print(request.POST)
        article_id = request.POST.get("article_id")
        pid = request.POST.get("pid")
        content = request.POST.get("content")
        user_id = request.user.pk
        #绑定事物操作;
        with transaction.atomic():
            comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,
                                                        parent_comment_id=pid)
            models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)
        response = {}
        response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X")
        response["username"] = request.user.username
        response["content"] = content
        return JsonResponse(response)

    75-博客系统之评论的邮件发送new

    1、settings.py文件中配置邮箱信息;

    EMAIL_HOST = 'smtp.exmail.qq.com'  # 如果是 163 改成 smtp.163.com
    EMAIL_PORT = 465
    EMAIL_HOST_USER = '290799238@qq.com'  # 帐号
    EMAIL_HOST_PASSWORD = 'hmefdscufpbnxafbjib'  # 密码
    DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
    EMAIL_USE_SSL = True

    2、comment视图函数;

    def comment(request):
        print(request.POST)
        article_id = request.POST.get("article_id")
        pid = request.POST.get("pid")
        content = request.POST.get("content")
        user_id = request.user.pk
        article_obj = models.Article.objects.filter(pk=article_id).first()
        # 绑定事物操作;
        with transaction.atomic():
            comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,
                                                        parent_comment_id=pid)
            models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)
        response = {}
        response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X")
        response["username"] = request.user.username
        response["content"] = content
        # 发送邮件;
        from django.core.mail import send_mail
        from cnblogs import settings
        send_mail(
            "您的文章%s新增了一条内容" % article_obj.title,
            content,
            settings.EMAIL_HOST_USER,
            ["tqtl@tqtl.org"]
        )
        # import threading
        # t = threading.Thread(target=send_mail,args = (
        #     "您的文章%s新增了一条内容" % article_obj.title,
        #     content,
        #     settings.EMAIL_HOST_USER,
        #     ["tqtl@tqtl.org"]
        #
        # ))
        # t.start()
        return JsonResponse(response)

    76-博客系统之后台管理页面的编辑功能

    77-博客系统之后台管理的编辑器引入和参数

    1、kindeditor官网;http://kindeditor.net/demo.php

    2、kindEditor在线使用说明;http://kindeditor.net/doc.php

    78-博客系统之文本编辑器的上传功能1

    79-博客系统之文本编辑器的上传功能2

    80-博客系统之文章摘要的保存

    81-博客系统之bs4的简单应用

    82-博客系统之bs4模块防御xss攻击

  • 相关阅读:
    ValueError: Expecting property name: line 1 column 2 (char 1)
    python 函数中,os.linesep是干什么的
    django 重定向如何解决iframe页面嵌套问题
    django设置debug设为False,前端样式出现错乱或静态文件404问题。
    Jquery 前端解码base64出现中文乱码的问题解决方案
    mysql 密码重置或忘记密码相关命令
    list去重的四种方式
    注册时,第一时间判断用户名是否已经存在。
    Project facet Java version 1.8 is not supported
    Eureka的工作原理以及它与ZooKeeper的区别
  • 原文地址:https://www.cnblogs.com/tqtl911/p/9519225.html
Copyright © 2011-2022 走看看