项目实战: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 {
    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;
}

article_detail.css;

.article_info .title {
    margin-bottom: 20px;
}

#div_digg {
    float: right;
    margin-bottom: 10px;
    margin-right: 30px;
    font-size: 12px;
    width: 125px;
    text-align: center;
    margin-top: 10px;
}

.diggit {
    float: left;
    width: 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;
    width: 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;
    width: 125px;
    text-align: center;
    margin-top: 10px;
}

.diggit {
    float: left;
    width: 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;
    width: 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;
    width: 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")+"\n";
        $("#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("\n");
                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") + "\n";
            $("#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("\n");
                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("\n");
                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") + "\n";
            $("#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("\n");
                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") + "\n";
            $("#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攻击

posted @ 2018-08-22 17:39  天晴天朗  阅读(564)  评论(0编辑  收藏  举报