前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >36·Python项目-博客(前后不分离)

36·Python项目-博客(前后不分离)

作者头像
DriverZeng
发布2022-11-08 16:28:18
7540
发布2022-11-08 16:28:18
举报

-曾老湿, 江湖人称曾老大。 -笔者QQ:133411023、253097001 -笔者交流群:198571640 -笔者微信:z133411023


-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。


需求分析


需求

代码语言:javascript
复制
-首页(显示文章)
-文章详情
-点赞,点踩
-文章评论
    -字评论
    -评论的展示
-登录功能(图片验证码)
-注册功能(基于form验证,ajax)
-个人站点(不同人不同样式,文章过滤)
-后台管理:
    -文章展示
-新增文章
    -富文本编辑器

数据库设计

代码语言:javascript
复制
      User
            -nid
            -name
            -password
            -email
            -phone
            -avatar   用户头像
            -create_date    用户注册时间
            -blog
        Blog
            -nid
            -title
            -site_name
            -theme
        category:
            -nid
            -title
            -blog   跟blog一对多
            
        tag:(文章关键字)
            -nid
            -title
            -blog    跟blog一对多
            
        article
            -nid
            -title
            -desc    摘要
            -create_time    auto_add_now:当该条记录创建时,自动添加当前时间
            -content   文章内容
            
            -category    一对多
            -tag         多对多
            -blog        一对多
            
        commit
            -nid
            -user     哪个用户
            -article  对哪篇文章
            -content   评论了什么内容
            -commit_time  时间
            
            -parent_id
            如何实现根评论与子评论?
                -有同学分析,要再建一张表,跟commit是一对多的关系(不好)
                
                -如何用这一个表,表示出根评论和子评论?
                    -再加一个字段,标志,给那条评论,评论的


nid user    article   content    parent_id
        
    1    1        1         111         null
    2   2        1         222         null
    3   3          1         333          1
    4   4           1         444          3
    5   3        1         反弹          4
        UpandDown
            -nid
            -user     哪个用户
            -article  对哪篇文章
            -is_up   点赞还是点踩

创建项目

1.数据库操作

代码语言:javascript
复制
# 创建数据库
mysql> create database bbs;

# 创建用户
mysql> grant all on *.* to root@'%' identified by '123';

2.Django配置使用MySQL

代码语言:javascript
复制
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'bbs',
        'HOST': '10.0.0.51',
        'PORT': 3306,
        'USER': 'root',
        'PASSWORD': '123',
    }
}

3.bbs的__init__中导入pymysql

代码语言:javascript
复制
import pymysql
pymysql.install_as_MySQLdb()

4.配置静态文件路径

代码语言:javascript
复制
STATIC_URL = '/static/'

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

5.创建静态文件目录

6.创建表

代码语言:javascript
复制
from django.db import models
from django.contrib.auth.models import AbstractUser


# Create your models here.

## UserInfo这个表 要继承AbstractUser,因为要用auth组件
class UserInfo(AbstractUser):
    nid = models.AutoField(primary_key=True)
    phone = models.CharField(max_length=32, null=True)
    ## 这个需要传一个路径
    avatar = models.FileField(upload_to='avatar/', default='/static/img/default.png')
    blog = models.OneToOneField(to='Blog', to_field='nid',null=True)


class Blog(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    sit_name = models.CharField(max_length=32)
    theme = models.CharField(max_length=64)


class Category(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)


class Tag(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)


class Article(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    desc = models.CharField(max_length=255)
    content = models.TextField()
    create_time = models.DateTimeField(auto_now_add=True)
    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)
    category = models.ForeignKey(to='Category', to_field='nid', null=True)
    tag = models.ManyToManyField(to='Tag', through='ArticleToTag', through_fields=('article', 'tag'))


## 手动创建第三张表
class ArticleToTag(models.Model):
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(to='Article', to_field='nid')
    tag = models.ForeignKey(to='Tag', to_field='nid')


class Commit(models.Model):
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey(to='UserInfo', to_field='nid')
    article = models.ForeignKey(to='Article', to_field='nid')
    content = models.CharField(max_length=255)
    create_time = models.DateTimeField(auto_now_add=True)
    ## 自己关联自己:方法一
    parent_id = models.ForeignKey(to='self', to_field='nid')
    ## 自己关联自己:方法二
    # parent_id = models.ForeignKey(to='Commit',to_field='nid')


class UpAndDown(models.Model):
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey(to='UserInfo', to_field='nid')
    article = models.ForeignKey(to='Article', to_field='nid')
    is_up = models.BooleanField()

    ## user和 article不允许一个作者,给一个文章点多个赞,做联合唯一。

    class Meta:
        ## 做这个只是为了不写脏数据,但是效率低,工作中不写也OK
        unique_together = (('user', 'article'),)

7.修改配置setting.py

代码语言:javascript
复制
AUTH_USER_MODEL = 'blog.UserInfo'

8.数据库迁移

代码语言:javascript
复制
MacBook-pro:bbs driverzeng$ python3 manage.py makemigrations
MacBook-pro:bbs driverzeng$ python3 manage.py migrate

前端登录页面


引入bootstrap

代码语言:javascript
复制
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

引入jquery

代码语言:javascript
复制
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

登录页面

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <form>
                <div class="form-group">
                    <label for="">用户名</label>
                    <input type="text" id="name">
                </div>
                <div class="form-group">
                    <label for="">密码</label>
                    <input type="password" id="pwd">
                </div>
                <div class="form-group">
                    <label for="">验证码</label>
                    <input type="password" id="valid_code">
                </div>
                <input type="button" value="登录" class="btn btn-primary">
            </form>
        </div>
    </div>
</div>
</body>
</html>

路由层

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
]

视图层

代码语言:javascript
复制
from django.shortcuts import render,HttpResponse,redirect

# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request,'login.html')
    else:
        return HttpResponse('OK')


修改样式

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1>登录</h1>
            <form>
                <div class="form-group">
                    <label for="name">用户名</label>
                    <input type="text" id="name" 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="valid_code">验证码</label>
                    <div class="row">
                        <div class="col-md-6">
                            <input type="text" id="valid_code" class="form-control">
                        </div>
                        <img width="350" height="45"
                             src="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2142985517,724352710&fm=26&gp=0.jpg"
                             alt="">
                    </div>
                </div>
                <input type="button" value="登录" class="btn btn-primary pull-right">
            </form>
        </div>
    </div>
</div>
</body>
</html>

实现验证码功能


添加验证码图片的方法

方法一:

1.添加验证码图片的路由

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
]

2.添加该路由的视图

代码语言:javascript
复制
def get_valid_code(request):
    with open('static/img/1.jpg','rb') as f:
        data = f.read()
    return HttpResponse(data)

方法二:

随机生成图片,需要使用pillow模块,是一个图像处理模块,功能很强大

代码语言:javascript
复制
MacBook-pro:bbs driverzeng$ pip3 install pillow -i https://mirrors.aliyun.com/pypi/simple/
代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image


# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        return HttpResponse('OK')


def get_valid_code(request):
    # with open('static/img/1.jpg','rb') as f:
    #     data = f.read()
    # return HttpResponse(data)
    ## 生成一张图片,mode:'RGB'模式 (320,35)图片大小,color颜色
    img = Image.new('RGB', (320, 35), color='green')

    ## 保存到本地
    with open('valid_code.png', 'wb') as f:
        # 使用save方法,第一个参数是空文件,第二个参数是格式
        img.save(f, 'png')

    ## 打开文件,再返回
    with open('valid_code.png', 'rb') as f:
        data = f.read()
    return HttpResponse(data)


随机验证码的颜色

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image
import random


# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        return HttpResponse('OK')


def get_random_color():
    return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))


def get_valid_code(request):
    # with open('static/img/1.jpg','rb') as f:
    #     data = f.read()
    # return HttpResponse(data)
    ## 生成一张图片,mode:'RGB'模式 (320,35)图片大小,color颜色
    img = Image.new('RGB', (320, 35), color=get_random_color())

    ## 保存到本地
    with open('valid_code.png', 'wb') as f:
        # 使用save方法,第一个参数是空文件,第二个参数是格式
        img.save(f, 'png')

    ## 打开文件,再返回
    with open('valid_code.png', 'rb') as f:
        data = f.read()
    return HttpResponse(data)

每次刷新都在变。


解决资源问题

每次都要保存一张图片,只要一访问,或者刷新页面都会保存,很占资源啊。

使用内存管理的模块 BytesIO

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image
import random
from io import BytesIO

# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        return HttpResponse('OK')


def get_random_color():
    return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))


def get_valid_code(request):
    img = Image.new('RGB', (320, 35), color=get_random_color())
    ## 在内存中生成一个空文件
    f = BytesIO()
    ## 把图片保存到f中
    img.save(f, 'png')
    ## 取出图片
    data = f.getvalue()
    return HttpResponse(data)

验证码添加文字

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image,ImageDraw,ImageFont
import random
from io import BytesIO

# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        return HttpResponse('OK')


def get_random_color():
    return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))


def get_valid_code(request):
    img = Image.new('RGB', (320, 35), color=get_random_color())
    # 拿到画笔,在图片上画画
    img_draw = ImageDraw.Draw(img)

    # 生成一个字体对象 ,第一个参数是字体文件路径,第二个参数,是字体大小
    font = ImageFont.truetype('static/font/ss.TTF',size=26)

    ## 第一个参数,xy的坐标,第二个参数,文字,第三个参数,颜色,第四个参数,字体。
    img_draw.text((120,0),'python',get_random_color(),font=font)

    ## 在内存中生成一个空文件
    f = BytesIO()
    ## 把图片保存到f中
    img.save(f, 'png')
    ## 取出图片
    data = f.getvalue()
    return HttpResponse(data)


验证码随机

写一个循环,循环5次,每次随机写一个(数字,大写,小写字母)

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image,ImageDraw,ImageFont
import random
from io import BytesIO

# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        return HttpResponse('OK')


def get_random_color():
    return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))


def get_valid_code(request):
    img = Image.new('RGB', (320, 35), color=get_random_color())
    # 拿到画笔,在图片上画画
    img_draw = ImageDraw.Draw(img)

    # 生成一个字体对象 ,第一个参数是字体文件路径,第二个参数,是字体大小
    font = ImageFont.truetype('static/font/ss.TTF',size=26)

    ## 写一个循环,循环5次,每次随机写一个(数字,大写,小写字母)
    for i in range(5):
        char_num = random.randint(0,9)
        # 生成97 ~ 122的数字,生成字母
        char_lower = chr(random.randint(97,122))
        char_upper = chr(random.randint(65,90))
        char_str = str(random.choice([char_num,char_lower,char_upper]))

        ## 第一个参数,xy的坐标,第二个参数,文字,第三个参数,颜色,第四个参数,字体。
        img_draw.text((i*60+20, 0), char_str, get_random_color(), font=font)

    ## 在内存中生成一个空文件
    f = BytesIO()
    ## 把图片保存到f中
    img.save(f, 'png')
    ## 取出图片
    data = f.getvalue()
    return HttpResponse(data)


画线画点

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image,ImageDraw,ImageFont
import random
from io import BytesIO

# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        return HttpResponse('OK')


def get_random_color_light():
    return (random.randint(0, 100), random.randint(0, 100), random.randint(0, 100))

def get_random_color_dark():
    return (random.randint(150, 255), random.randint(150, 255), random.randint(150, 255))


def get_valid_code(request):
    img = Image.new('RGB', (320, 35), color=get_random_color_light())
    # 拿到画笔,在图片上画画
    img_draw = ImageDraw.Draw(img)

    # 生成一个字体对象 ,第一个参数是字体文件路径,第二个参数,是字体大小
    font = ImageFont.truetype('static/font/ss.TTF',size=26)

    ## 写一个循环,循环5次,每次随机写一个(数字,大写,小写字母)
    for i in range(5):
        char_num = random.randint(0,9)
        # 生成97 ~ 122的数字,生成字母
        char_lower = chr(random.randint(97,122))
        char_upper = chr(random.randint(65,90))
        char_str = str(random.choice([char_num,char_lower,char_upper]))

        ## 第一个参数,xy的坐标,第二个参数,文字,第三个参数,颜色,第四个参数,字体。
        img_draw.text((i*60+20, 0), char_str, get_random_color_dark(), font=font)

    width = 350
    height = 45
    for i in range(10):
        x1 = random.randint(0,width)
        x2 = random.randint(0,width)
        y1 = random.randint(0,height)
        y2 = random.randint(0,height)

        #在图片上画线
        img_draw.line((x1,y1,x2,y2),fill=get_random_color_light())

    for i in range(50):
        img_draw.point([random.randint(0,width),random.randint(0,height)],fill=get_random_color_light())
        x = random.randint(0,width)
        y = random.randint(0,height)
        img_draw.arc((x,y,x+4,y+4),0,90,fill=get_random_color_light())


    ## 在内存中生成一个空文件
    f = BytesIO()
    ## 把图片保存到f中
    img.save(f, 'png')
    ## 取出图片
    data = f.getvalue()
    return HttpResponse(data)


刷新验证码

让用户点击图片,可以刷新验证码,尽量每次刷新图片越来越清晰

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1>登录</h1>
            <form>
                <div class="form-group">
                    <label for="name">用户名</label>
                    <input type="text" id="name" 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="valid_code">验证码</label>
                    <div class="row">
                        <div class="col-md-6">
                            <input type="text" id="valid_code" class="form-control">
                        </div>
                        <img width="350" height="45"
                             src="/get_valid_code/"
                             alt="" id="img_code">
                    </div>
                </div>
                <input type="button" value="登录" class="btn btn-primary pull-right">
            </form>
        </div>
    </div>
</div>
</body>
<script>
    $('#img_code').click(function () {
        // 每次点击图片都在图片路径后面加一个 问号.
        $('#img_code')[0].src+='?'
    })
</script>
</html>

登录功能


保存验证码

把验证码保存到session中

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO
from django.contrib import auth
from django.http import JsonResponse


# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    ## 判断前台发的请求是不是ajax的请求
    elif request.is_ajax():
        response = {'user': None, 'msg': None}
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        valid_code = request.POST.get('valid_code')

        if valid_code.upper() == request.session.get('valid_code').upper():
            user = auth.authenticate(request, username=name, password=pwd)
            if user:
                ## ajax请求,不能再返回render页面或者redirect页面,只能返回字符串或者json
                ## 校验通过,一定要登录
                auth.login(request, user)
                response['user'] = name
                response['msg'] = '登录成功'
            else:
                response['msg'] = '用户名密码错误'
        else:
            response['msg'] = '验证码错误'

    return JsonResponse(response)


def get_random_color_light():
    return (random.randint(0, 100), random.randint(0, 100), random.randint(0, 100))


def get_random_color_dark():
    return (random.randint(150, 255), random.randint(150, 255), random.randint(150, 255))


def get_valid_code(request):
    img = Image.new('RGB', (320, 35), color=get_random_color_light())
    # 拿到画笔,在图片上画画
    img_draw = ImageDraw.Draw(img)

    # 生成一个字体对象 ,第一个参数是字体文件路径,第二个参数,是字体大小
    font = ImageFont.truetype('static/font/ss.TTF', size=26)

    ## 保存验证码
    random_code = ''
    ## 写一个循环,循环5次,每次随机写一个(数字,大写,小写字母)
    for i in range(5):
        char_num = random.randint(0, 9)
        # 生成97 ~ 122的数字,生成字母
        char_lower = chr(random.randint(97, 122))
        char_upper = chr(random.randint(65, 90))
        char_str = str(random.choice([char_num, char_lower, char_upper]))

        ## 第一个参数,xy的坐标,第二个参数,文字,第三个参数,颜色,第四个参数,字体。
        img_draw.text((i * 60 + 20, 0), char_str, get_random_color_dark(), font=font)
        random_code += char_str

    ## 把验证码 保存到session中
    request.session['valid_code'] = random_code

    width = 350
    height = 45
    for i in range(10):
        x1 = random.randint(0, width)
        x2 = random.randint(0, width)
        y1 = random.randint(0, height)
        y2 = random.randint(0, height)

        # 在图片上画线
        img_draw.line((x1, y1, x2, y2), fill=get_random_color_light())

    for i in range(100):
        img_draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color_light())
        x = random.randint(0, width)
        y = random.randint(0, height)
        img_draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color_light())

    ## 在内存中生成一个空文件
    f = BytesIO()
    ## 把图片保存到f中
    img.save(f, 'png')
    ## 取出图片
    data = f.getvalue()
    return HttpResponse(data)
代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1>登录</h1>
            <form>
                {% csrf_token %}
                <div class="form-group">
                    <label for="name">用户名</label>
                    <input type="text" id="name" 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="valid_code">验证码</label>
                    <div class="row">
                        <div class="col-md-6">
                            <input type="text" id="valid_code" class="form-control">
                        </div>
                        <img width="350" height="45"
                             src="/get_valid_code/"
                             alt="" id="img_code">
                    </div>
                </div>
                <input type="button" value="登录" class="btn btn-primary pull-right" id="btn">
            </form>
        </div>
    </div>
</div>
</body>
<script>
    $('#img_code').click(function () {
        // 每次点击图片都在图片路径后面加一个 问号.
        $('#img_code')[0].src += '?'
    })
    $('#btn').click(function () {
        $.ajax({
            url: '/login/',
            type: 'post',
            // 一定要 记住 传CSRF
            data: {
                'name': $('#name').val(),
                'pwd': $('#pwd').val(),
                'valid_code': $('#valid_code').val(),
                // 使用属性选择器,选择
                'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val()
                //使用变量
                //'csrfmiddlewaretoken': '{{ csrf_token }}'
            },
            success:function (data) {
                console.log(data)
            }
        })
    })
</script>
</html>

创建一个用户

代码语言:javascript
复制
MacBook-pro:bbs driverzeng$ python3 manage.py createsuperuser
Username: zls
Email address: 133@qq.com
Password: zls12345
Password (again): zls12345
Superuser created successfully.


登录功能完善

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style>
        .error{
            color: red;
            margin-left: 20px;
        }
    </style>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1>登录</h1>
            <form>
                {% csrf_token %}
                <div class="form-group">
                    <label for="name">用户名</label>
                    <input type="text" id="name" 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="valid_code">验证码</label>
                    <div class="row">
                        <div class="col-md-6">
                            <input type="text" id="valid_code" class="form-control">
                        </div>
                        <img width="350" height="45"
                             src="/get_valid_code/"
                             alt="" id="img_code">
                    </div>
                </div>
                <input type="button" value="登录" class="btn btn-primary" id="btn"><span class="error"></span>
            </form>
        </div>
    </div>
</div>
</body>
<script>
    $('#img_code').click(function () {
        // 每次点击图片都在图片路径后面加一个 问号.
        $('#img_code')[0].src += '?'
    })
    $('#btn').click(function () {
        $.ajax({
            url: '/login/',
            type: 'post',
            // 一定要 记住 传CSRF
            data: {
                'name': $('#name').val(),
                'pwd': $('#pwd').val(),
                'valid_code': $('#valid_code').val(),
                // 使用属性选择器,选择
                'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val()
                //使用变量
                //'csrfmiddlewaretoken': '{{ csrf_token }}'
            },
            success:function (data) {
                console.log(data)
                if(data.user){
                    // 登录成功跳转到index页面
                    location.href = '/index/'
                }else {
                    $('.error').html(data.msg)
                }
            }
        })
    })
</script>
</html>

注册功能


myforms

使用forms组件。

创建一个forms组件的文件。

代码语言:javascript
复制
from django import forms

class RegForm(forms.Form):
    name = forms.CharField(max_length=18,min_length=2)
    pwd = forms.CharField(max_length=18,min_length=2)
    re_pwd = forms.CharField(max_length=18,min_length=2)
    emai = forms.EmailField()

路由层

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
]

视图层

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO
from django.contrib import auth
from django.http import JsonResponse
from blog import myforms


# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    ## 判断前台发的请求是不是ajax的请求
    elif request.is_ajax():
        response = {'user': None, 'msg': None}
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        valid_code = request.POST.get('valid_code')

        if valid_code.upper() == request.session.get('valid_code').upper():
            user = auth.authenticate(request, username=name, password=pwd)
            if user:
                ## ajax请求,不能再返回render页面或者redirect页面,只能返回字符串或者json
                ## 校验通过,一定要登录
                auth.login(request, user)
                response['user'] = name
                response['msg'] = '登录成功'
            else:
                response['msg'] = '用户名密码错误'
        else:
            response['msg'] = '验证码错误'

    return JsonResponse(response)


def get_random_color_light():
    return (random.randint(0, 100), random.randint(0, 100), random.randint(0, 100))


def get_random_color_dark():
    return (random.randint(150, 255), random.randint(150, 255), random.randint(150, 255))


def get_valid_code(request):
    img = Image.new('RGB', (320, 35), color=get_random_color_light())
    # 拿到画笔,在图片上画画
    img_draw = ImageDraw.Draw(img)

    # 生成一个字体对象 ,第一个参数是字体文件路径,第二个参数,是字体大小
    font = ImageFont.truetype('static/font/ss.TTF', size=26)

    ## 保存验证码
    random_code = ''
    ## 写一个循环,循环5次,每次随机写一个(数字,大写,小写字母)
    for i in range(5):
        char_num = random.randint(0, 9)
        # 生成97 ~ 122的数字,生成字母
        char_lower = chr(random.randint(97, 122))
        char_upper = chr(random.randint(65, 90))
        char_str = str(random.choice([char_num, char_lower, char_upper]))

        ## 第一个参数,xy的坐标,第二个参数,文字,第三个参数,颜色,第四个参数,字体。
        img_draw.text((i * 60 + 20, 0), char_str, get_random_color_dark(), font=font)
        random_code += char_str

    ## 把验证码 保存到session中
    request.session['valid_code'] = random_code

    width = 350
    height = 45
    for i in range(10):
        x1 = random.randint(0, width)
        x2 = random.randint(0, width)
        y1 = random.randint(0, height)
        y2 = random.randint(0, height)

        # 在图片上画线
        img_draw.line((x1, y1, x2, y2), fill=get_random_color_light())

    for i in range(100):
        img_draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color_light())
        x = random.randint(0, width)
        y = random.randint(0, height)
        img_draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color_light())

    ## 在内存中生成一个空文件
    f = BytesIO()
    ## 把图片保存到f中
    img.save(f, 'png')
    ## 取出图片
    data = f.getvalue()
    return HttpResponse(data)


def register(request):
    my_form = myforms.RegForm()
    return render(request, 'register.html', {'my_form': my_form})

模板层

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style>
        #my_file {
            /* 把上传文件控件隐藏 */
            display: none;
        }
    </style>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1>登录</h1>
            <form>
                {% csrf_token %}
                {% for foo in my_form %}
                    <div class="form-group">
                        <label for="{{ foo.auto_id }}">{{ foo.label }}</label>
                        {{ foo }}
                    </div>
                {% endfor %}
                {#                加一个上传文件的控件#}
                <div class="form-group">
                    <label for="my_file">
                        头像
                        <img src="/static/img/default.png" id="img_file" alt="" width="80" height="80"
                             style="margin-left: 20px;margin-bottom: 20px" title="点击上传头像">
                    </label>
                    <input type="file" id="my_file">
                </div>
                <input type="button" value="注册" class="btn btn-primary" id="btn"><span class="error"></span>
            </form>
        </div>
    </div>
</div>
</body>
<script>
    $("#my_file").change(function () {
        // 取出文件
        var file_obj = $("#my_file")[0].files[0]
        // 通过文件阅读器,把图片放到img标签上
        // 生成一个文件阅读器对象
        var filereader = new FileReader()
        // 把图片读到filereader对象中
        filereader.readAsDataURL(file_obj)
        // filereader.result是filereader对象的值
        filereader.onload = function () {
            $('#img_file').attr('src', filereader.result)
        }
    })
</script>
</html>

提交注册信息


模板层

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style>
        #my_file {
            /* 把上传文件控件隐藏 */
            display: none;
        }
    </style>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1>登录</h1>
            <form>
                {% csrf_token %}
                {% for foo in my_form %}
                    <div class="form-group">
                        <label for="{{ foo.auto_id }}">{{ foo.label }}</label>
                        {{ foo }}
                    </div>
                {% endfor %}
                {#                加一个上传文件的控件#}
                <div class="form-group">
                    <label for="my_file">
                        头像
                        <img src="/static/img/default.png" id="img_file" alt="" width="80" height="80"
                             style="margin-left: 20px;margin-bottom: 20px" title="点击上传头像">
                    </label>
                    <input type="file" id="my_file">
                </div>
                <input type="button" value="注册" class="btn btn-primary" id="btn"><span class="error"></span>
            </form>
        </div>
    </div>
</div>
</body>
<script>
    $("#my_file").change(function () {
        // 取出文件
        var file_obj = $("#my_file")[0].files[0]
        // 通过文件阅读器,把图片放到img标签上
        // 生成一个文件阅读器对象
        var filereader = new FileReader()
        // 把图片读到filereader对象中
        filereader.readAsDataURL(file_obj)
        // filereader.result是filereader对象的值
        filereader.onload = function () {
            $('#img_file').attr('src', filereader.result)
        }
    })
    $("#btn").click(function () {
        // 因为要上传文件,所以要生成一个formdata对象
        var formdata = new FormData()
        formdata.append('name',$('#id_name').val())
        formdata.append('pwd',$('#id_pwd').val())
        formdata.append('re_pwd',$('#id_re_pwd').val())
        formdata.append('email',$('#id_email').val())
        formdata.append('csrfmiddlewaretoken',$('[name="csrfmiddlewaretoken"]').val())
        // 把文件放到formdata中
        formdata.append('my_file',$('#my_file')[0].files[0])

        $.ajax({
            url: '/register/',
            type: 'post',
            processData:false,
            contentType:false,
            data: {},
            success: function (data) {
                console.log(data)
            }
        })
    })
</script>
</html>

forms组件

代码语言:javascript
复制
from django import forms
from django.forms import widgets


class RegForm(forms.Form):
    name = forms.CharField(max_length=18, min_length=2, label='用户名',
                           widget=widgets.TextInput(attrs={'class': 'form-control'}))
    pwd = forms.CharField(max_length=18, min_length=2, label='密码',
                          widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    re_pwd = forms.CharField(max_length=18, min_length=2, label='确认密码',
                             widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label='邮箱', widget=widgets.EmailInput(attrs={'class': 'form-control'}))

视图层

代码语言:javascript
复制
def register(request):
    if request.method == 'GET':
        my_form = myforms.RegForm()
        return render(request, 'register.html', {'my_form': my_form})
    elif request.is_ajax():
        response = {'status':100,'msg':None}
        my_form = myforms.RegForm(request.POST)
        if my_form.is_valid():
            pass
        else:
            response['status'] = 101
            response['msg'] = my_form.errors

        return JsonResponse(response)

现在我们提交的数据还不算多,但是如果数据多起来的话,模板层的代码,要命了。

优化代码

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style>
        #my_file {
            /* 把上传文件控件隐藏 */
            display: none;
        }
    </style>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1>登录</h1>
            <form id="form">
                {% csrf_token %}
                {% for foo in my_form %}
                    <div class="form-group">
                        <label for="{{ foo.auto_id }}">{{ foo.label }}</label>
                        {{ foo }}
                    </div>
                {% endfor %}
                {#                加一个上传文件的控件#}
                <div class="form-group">
                    <label for="my_file">
                        头像
                        <img src="/static/img/default.png" id="img_file" alt="" width="80" height="80"
                             style="margin-left: 20px;margin-bottom: 20px" title="点击上传头像">
                    </label>
                    <input type="file" id="my_file">
                </div>
                <input type="button" value="注册" class="btn btn-primary" id="btn"><span class="error"></span>
            </form>
        </div>
    </div>
</div>
</body>
<script>
    $("#my_file").change(function () {
        // 取出文件
        var file_obj = $("#my_file")[0].files[0]
        // 通过文件阅读器,把图片放到img标签上
        // 生成一个文件阅读器对象
        var filereader = new FileReader()
        // 把图片读到filereader对象中
        filereader.readAsDataURL(file_obj)
        // filereader.result是filereader对象的值
        filereader.onload = function () {
            $('#img_file').attr('src', filereader.result)
        }
    })
    $("#btn").click(function () {
        // 因为要上传文件,所以要生成一个formdata对象
        var formdata = new FormData()
        {#formdata.append('name',$('#id_name').val())#}
        {#formdata.append('pwd',$('#id_pwd').val())#}
        {#formdata.append('re_pwd',$('#id_re_pwd').val())#}
        {#formdata.append('email',$('#id_email').val())#}

        var arr = $('#form').serializeArray()
        // jquery的循环
        $.each(arr, function (key, value) {
            formdata.append(value.name, value.value)
        })
        formdata.append('csrfmiddlewaretoken', $('[name="csrfmiddlewaretoken"]').val())
        // 把文件放到formdata中
        formdata.append('my_file', $('#my_file')[0].files[0])

        $.ajax({
            url: '/register/',
            type: 'post',
            processData: false,
            contentType: false,
            data: formdata,
            success: function (data) {
                console.log(data)
            }
        })
    })
</script>
</html>

注册成功插入数据

myforms.py

代码语言:javascript
复制
from django import forms
from django.forms import widgets
from blog import models
from django.core.exceptions import ValidationError

class RegForm(forms.Form):
    username = forms.CharField(max_length=18, min_length=2, label='用户名',
                           widget=widgets.TextInput(attrs={'class': 'form-control'}))
    password = forms.CharField(max_length=18, min_length=2, label='密码',
                          widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    re_password = forms.CharField(max_length=18, min_length=2, label='确认密码',
                             widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label='邮箱', widget=widgets.EmailInput(attrs={'class': 'form-control'}))

    # 局部校验钩子
    def clean_username(self):
        name = self.cleaned_data.get('username')
        # 去数据库校验
        ret = models.UserInfo.objects.filter(username=name).first()
        if ret:
            raise ValidationError('用户名已存在')
        return name

    # 全局校验钩子
    def clean(self):
        pwd = self.cleaned_data.get('password')
        re_pwd = self.cleaned_data.get('re_password')
        if pwd and re_pwd:
            if pwd == re_pwd:
                return self.cleaned_data
            else:
                raise ValidationError('两次密码不一致')

views.py

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO
from django.contrib import auth
from django.http import JsonResponse
from blog import myforms
from blog import models


# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    ## 判断前台发的请求是不是ajax的请求
    elif request.is_ajax():
        response = {'user': None, 'msg': None}
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        valid_code = request.POST.get('valid_code')

        if valid_code.upper() == request.session.get('valid_code').upper():
            user = auth.authenticate(request, username=name, password=pwd)
            if user:
                ## ajax请求,不能再返回render页面或者redirect页面,只能返回字符串或者json
                ## 校验通过,一定要登录
                auth.login(request, user)
                response['user'] = name
                response['msg'] = '登录成功'
            else:
                response['msg'] = '用户名密码错误'
        else:
            response['msg'] = '验证码错误'

    return JsonResponse(response)


def get_random_color_light():
    return (random.randint(0, 100), random.randint(0, 100), random.randint(0, 100))


def get_random_color_dark():
    return (random.randint(150, 255), random.randint(150, 255), random.randint(150, 255))


def get_valid_code(request):
    img = Image.new('RGB', (320, 35), color=get_random_color_light())
    # 拿到画笔,在图片上画画
    img_draw = ImageDraw.Draw(img)

    # 生成一个字体对象 ,第一个参数是字体文件路径,第二个参数,是字体大小
    font = ImageFont.truetype('static/font/ss.TTF', size=26)

    ## 保存验证码
    random_code = ''
    ## 写一个循环,循环5次,每次随机写一个(数字,大写,小写字母)
    for i in range(5):
        char_num = random.randint(0, 9)
        # 生成97 ~ 122的数字,生成字母
        char_lower = chr(random.randint(97, 122))
        char_upper = chr(random.randint(65, 90))
        char_str = str(random.choice([char_num, char_lower, char_upper]))

        ## 第一个参数,xy的坐标,第二个参数,文字,第三个参数,颜色,第四个参数,字体。
        img_draw.text((i * 60 + 20, 0), char_str, get_random_color_dark(), font=font)
        random_code += char_str

    ## 把验证码 保存到session中
    request.session['valid_code'] = random_code

    width = 350
    height = 45
    for i in range(10):
        x1 = random.randint(0, width)
        x2 = random.randint(0, width)
        y1 = random.randint(0, height)
        y2 = random.randint(0, height)

        # 在图片上画线
        img_draw.line((x1, y1, x2, y2), fill=get_random_color_light())

    for i in range(100):
        img_draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color_light())
        x = random.randint(0, width)
        y = random.randint(0, height)
        img_draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color_light())

    ## 在内存中生成一个空文件
    f = BytesIO()
    ## 把图片保存到f中
    img.save(f, 'png')
    ## 取出图片
    data = f.getvalue()
    return HttpResponse(data)


def register(request):
    if request.method == 'GET':
        my_form = myforms.RegForm()
        return render(request, 'register.html', {'my_form': my_form})
    elif request.is_ajax():
        response = {'status': 100, 'msg': None}
        my_form = myforms.RegForm(request.POST)
        if my_form.is_valid():
            ## 定义一个字段,把通过的数据赋值给字典
            dic = my_form.cleaned_data
            ## 移除确认密码的字段
            dic.pop('re_password')
            ## 取出上传的文件对象
            my_file = request.FILES.get('my_file')
            ## 放到字典中
            ## 如果没有上传文件,数据库存默认值
            if my_file:
                dic['avatar'] = my_file
            user = models.UserInfo.objects.create_user(**dic)
            print(user.username)
        else:
            response['status'] = 101
            response['msg'] = my_form.errors

        return JsonResponse(response)

用户已存在

密码不一致

注册成功

最后说一个,在上传文件中,加一个image/*可以控制图片的显示。

代码语言:javascript
复制
<input accept="image/*" type="file" id="my_file">

渲染错误信息


模板层

判断,数据的返回。

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style>
        #my_file {
            /* 把上传文件控件隐藏 */
            display: none;
        }
        .error{
            color: red;
        }
    </style>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1>登录</h1>
            <form id="form">
                {% csrf_token %}
                {% for foo in my_form %}
                    <div class="form-group">
                        <label for="{{ foo.auto_id }}">{{ foo.label }}</label>
                        {{ foo }} <span class="error pull-right"></span>
                    </div>
                {% endfor %}
                {#                加一个上传文件的控件#}
                <div class="form-group">
                    <label for="my_file">
                        头像
                        <img src="/static/img/default.png" id="img_file" alt="" width="80" height="80"
                             style="margin-left: 20px;margin-bottom: 20px" title="点击上传头像">
                    </label>
                    <input accept="image/*" type="file" id="my_file">
                </div>
                <input type="button" value="注册" class="btn btn-primary" id="btn"><span class="error"></span>
            </form>
        </div>
    </div>
</div>
</body>
<script>
    $("#my_file").change(function () {
        // 取出文件
        var file_obj = $("#my_file")[0].files[0]
        // 通过文件阅读器,把图片放到img标签上
        // 生成一个文件阅读器对象
        var filereader = new FileReader()
        // 把图片读到filereader对象中
        filereader.readAsDataURL(file_obj)
        // filereader.result是filereader对象的值
        filereader.onload = function () {
            $('#img_file').attr('src', filereader.result)
        }
    })
    $("#btn").click(function () {
        // 因为要上传文件,所以要生成一个formdata对象
        var formdata = new FormData()
        {#formdata.append('name',$('#id_name').val())#}
        {#formdata.append('pwd',$('#id_pwd').val())#}
        {#formdata.append('re_pwd',$('#id_re_pwd').val())#}
        {#formdata.append('email',$('#id_email').val())#}

        var arr = $('#form').serializeArray()
        // jquery的循环
        $.each(arr, function (key, value) {
            formdata.append(value.name, value.value)
        })
        formdata.append('csrfmiddlewaretoken', $('[name="csrfmiddlewaretoken"]').val())
        // 把文件放到formdata中
        formdata.append('my_file', $('#my_file')[0].files[0])

        $.ajax({
            url: '/register/',
            type: 'post',
            processData: false,
            contentType: false,
            data: formdata,
            success: function (data) {
                console.log(data)
                if(data.status === 100){
                    location.href = data.url
                }else {
                    $.each(data.msg,function (key, value) {
                        console.log(key,value)
                        // 根据key 拼上id 通过id取出控件
                        //$('#id_username').next().html('sb')
                        //$('#id_'+key).next().html(value[0])
                        // 取出爸爸,然后加报错颜色
                        //$('#id_'+key).parent().addClass('has-error')
                        //  一行代码实现
                        $('#id_'+key).next().html(value[0]).parent().addClass('has-error')
                    })
                }
            }
        })
    })
</script>
</html>

视图层

代码语言:javascript
复制
def register(request):
    if request.method == 'GET':
        my_form = myforms.RegForm()
        return render(request, 'register.html', {'my_form': my_form})
    elif request.is_ajax():
        response = {'status': 100, 'msg': None}
        my_form = myforms.RegForm(request.POST)
        if my_form.is_valid():
            ## 定义一个字段,把通过的数据赋值给字典
            dic = my_form.cleaned_data
            ## 移除确认密码的字段
            dic.pop('re_password')
            ## 取出上传的文件对象
            my_file = request.FILES.get('my_file')
            ## 放到字典中
            ## 如果没有上传文件,数据库存默认值
            if my_file:
                dic['avatar'] = my_file
            user = models.UserInfo.objects.create_user(**dic)
            print(user.username)
            response['url'] = '/login/'
        else:
            response['status'] = 101
            response['msg'] = my_form.errors

        return JsonResponse(response)

myforms组件

代码语言:javascript
复制
from django import forms
from django.forms import widgets
from blog import models
from django.core.exceptions import ValidationError

class RegForm(forms.Form):
    username = forms.CharField(max_length=18, min_length=2, label='用户名',
                           widget=widgets.TextInput(attrs={'class': 'form-control'}))
    password = forms.CharField(max_length=18, min_length=2, label='密码',
                          widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    re_password = forms.CharField(max_length=18, min_length=2, label='确认密码',
                             widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label='邮箱', widget=widgets.EmailInput(attrs={'class': 'form-control'}))

    # 局部校验钩子
    def clean_username(self):
        name = self.cleaned_data.get('username')
        # 去数据库校验
        ret = models.UserInfo.objects.filter(username=name).first()
        if ret:
            raise ValidationError('用户名已存在')
        return name

    # 全局校验钩子
    def clean(self):
        pwd = self.cleaned_data.get('password')
        re_pwd = self.cleaned_data.get('re_password')
        if pwd and re_pwd:
            if pwd == re_pwd:
                return self.cleaned_data
            else:
                raise ValidationError('两次密码不一致')

但是这么写有个bug,如果把对应字段改成正确的之后,点击注册,还是这些错误

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style>
        #my_file {
            /* 把上传文件控件隐藏 */
            display: none;
        }
        .error{
            color: red;
        }
    </style>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1>登录</h1>
            <form id="form">
                {% csrf_token %}
                {% for foo in my_form %}
                    <div class="form-group">
                        <label for="{{ foo.auto_id }}">{{ foo.label }}</label>
                        {{ foo }} <span class="error pull-right"></span>
                    </div>
                {% endfor %}
                {#                加一个上传文件的控件#}
                <div class="form-group">
                    <label for="my_file">
                        头像
                        <img src="/static/img/default.png" id="img_file" alt="" width="80" height="80"
                             style="margin-left: 20px;margin-bottom: 20px" title="点击上传头像">
                    </label>
                    <input accept="image/*" type="file" id="my_file">
                </div>
                <input type="button" value="注册" class="btn btn-primary" id="btn"><span class="error"></span>
            </form>
        </div>
    </div>
</div>
</body>
<script>
    $("#my_file").change(function () {
        // 取出文件
        var file_obj = $("#my_file")[0].files[0]
        // 通过文件阅读器,把图片放到img标签上
        // 生成一个文件阅读器对象
        var filereader = new FileReader()
        // 把图片读到filereader对象中
        filereader.readAsDataURL(file_obj)
        // filereader.result是filereader对象的值
        filereader.onload = function () {
            $('#img_file').attr('src', filereader.result)
        }
    })
    $("#btn").click(function () {
        // 因为要上传文件,所以要生成一个formdata对象
        var formdata = new FormData()
        {#formdata.append('name',$('#id_name').val())#}
        {#formdata.append('pwd',$('#id_pwd').val())#}
        {#formdata.append('re_pwd',$('#id_re_pwd').val())#}
        {#formdata.append('email',$('#id_email').val())#}

        var arr = $('#form').serializeArray()
        // jquery的循环
        $.each(arr, function (key, value) {
            formdata.append(value.name, value.value)
        })
        formdata.append('csrfmiddlewaretoken', $('[name="csrfmiddlewaretoken"]').val())
        // 把文件放到formdata中
        formdata.append('my_file', $('#my_file')[0].files[0])

        $.ajax({
            url: '/register/',
            type: 'post',
            processData: false,
            contentType: false,
            data: formdata,
            success: function (data) {
                console.log(data)
                if(data.status === 100){
                    location.href = data.url
                }else {
                    $('.form-group').removeClass('has-error')
                        $('.error').html('')
                    $.each(data.msg,function (key, value) {
                        console.log(key,value)
                        // 根据key 拼上id 通过id取出控件
                        //$('#id_username').next().html('sb')
                        //$('#id_'+key).next().html(value[0])
                        // 取出爸爸,然后加报错颜色
                        //$('#id_'+key).parent().addClass('has-error')
                        //  一行代码实现
                        $('#id_'+key).next().html(value[0]).parent().addClass('has-error')
                    })
                    /*setTimeout(function () {
                        // 清除父级的has-error
                        // 清除错误信息
                        $('.form-group').removeClass('has-error')
                        $('.error').html('')
                    },3000)*/
                }
            }
        })
    })
</script>
</html>

处理form组件钩子报错显示

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style>
        #my_file {
            /* 把上传文件控件隐藏 */
            display: none;
        }

        .error {
            color: red;
        }
    </style>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1>登录</h1>
            <form id="form">
                {% csrf_token %}
                {% for foo in my_form %}
                    <div class="form-group">
                        <label for="{{ foo.auto_id }}">{{ foo.label }}</label>
                        {{ foo }} <span class="error pull-right"></span>
                    </div>
                {% endfor %}
                {#                加一个上传文件的控件#}
                <div class="form-group">
                    <label for="my_file">
                        头像
                        <img src="/static/img/default.png" id="img_file" alt="" width="80" height="80"
                             style="margin-left: 20px;margin-bottom: 20px" title="点击上传头像">
                    </label>
                    <input accept="image/*" type="file" id="my_file">
                </div>
                <input type="button" value="注册" class="btn btn-primary" id="btn"><span class="error"></span>
            </form>
        </div>
    </div>
</div>
</body>
<script>
    $("#my_file").change(function () {
        // 取出文件
        var file_obj = $("#my_file")[0].files[0]
        // 通过文件阅读器,把图片放到img标签上
        // 生成一个文件阅读器对象
        var filereader = new FileReader()
        // 把图片读到filereader对象中
        filereader.readAsDataURL(file_obj)
        // filereader.result是filereader对象的值
        filereader.onload = function () {
            $('#img_file').attr('src', filereader.result)
        }
    })
    $("#btn").click(function () {
        // 因为要上传文件,所以要生成一个formdata对象
        var formdata = new FormData()
        {#formdata.append('name',$('#id_name').val())#}
        {#formdata.append('pwd',$('#id_pwd').val())#}
        {#formdata.append('re_pwd',$('#id_re_pwd').val())#}
        {#formdata.append('email',$('#id_email').val())#}

        var arr = $('#form').serializeArray()
        // jquery的循环
        $.each(arr, function (key, value) {
            formdata.append(value.name, value.value)
        })
        formdata.append('csrfmiddlewaretoken', $('[name="csrfmiddlewaretoken"]').val())
        // 把文件放到formdata中
        formdata.append('my_file', $('#my_file')[0].files[0])

        $.ajax({
            url: '/register/',
            type: 'post',
            processData: false,
            contentType: false,
            data: formdata,
            success: function (data) {
                console.log(data)
                if (data.status === 100) {
                    location.href = data.url
                } else {
                    $('.form-group').removeClass('has-error')
                    $('.error').html('')
                    $.each(data.msg, function (key, value) {
                        console.log(key, value)
                        // 根据key 拼上id 通过id取出控件
                        //$('#id_username').next().html('sb')
                        //$('#id_'+key).next().html(value[0])
                        // 取出爸爸,然后加报错颜色
                        //$('#id_'+key).parent().addClass('has-error')
                        // 处理两次密码 不一致
                        if(key === '__all__'){
                            $('#id_re_password').next().html(value[0])
                        }
                        //  一行代码实现
                        $('#id_' + key).next().html(value[0]).parent().addClass('has-error')
                    })
                    /*setTimeout(function () {
                        // 清除父级的has-error
                        // 清除错误信息
                        $('.form-group').removeClass('has-error')
                        $('.error').html('')
                    },3000)*/
                }
            }
        })
    })
</script>
</html>


错误信息显示中文

myforms.py

代码语言:javascript
复制
from django import forms
from django.forms import widgets
from blog import models
from django.core.exceptions import ValidationError


class RegForm(forms.Form):
    username = forms.CharField(max_length=18, min_length=2, label='用户名',
                               widget=widgets.TextInput(attrs={'class': 'form-control'}),
                               error_messages={'max_length': '最长18位哦~', 'min_length': '最少2位哦~', 'required': '必须填写哦~'})

    password = forms.CharField(max_length=18, min_length=2, label='密码',
                               widget=widgets.PasswordInput(attrs={'class': 'form-control'}),
                               error_messages={'max_length': '最长18位哦~', 'min_length': '最少2位哦~', 'required': '必须填写哦~'})
    re_password = forms.CharField(max_length=18, min_length=2, label='确认密码',
                                  widget=widgets.PasswordInput(attrs={'class': 'form-control'}),
                                  error_messages={'max_length': '最长18位哦~', 'min_length': '最少2位哦~',
                                                  'required': '必须填写哦~'})
    email = forms.EmailField(label='邮箱', widget=widgets.EmailInput(attrs={'class': 'form-control'}),
                             error_messages={'invalid': '请填写邮件格式,傻x', 'required': '必须填写哦~'})

    # 局部校验钩子
    def clean_username(self):
        name = self.cleaned_data.get('username')
        # 去数据库校验
        ret = models.UserInfo.objects.filter(username=name).first()
        if ret:
            raise ValidationError('用户名已存在')
        return name

    # 全局校验钩子
    def clean(self):
        pwd = self.cleaned_data.get('password')
        re_pwd = self.cleaned_data.get('re_password')
        if pwd and re_pwd:
            if pwd == re_pwd:
                return self.cleaned_data
            else:
                raise ValidationError('两次密码不一致')


失去焦点

啥意思呢?当填写完用户名的时候,鼠标移动到下一个框的时候,用户名的框就失去焦点了,那么在失去焦点的时候,瞬间显示出,这个字段的校验错误信息。

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style>
        #my_file {
            /* 把上传文件控件隐藏 */
            display: none;
        }

        .error {
            color: red;
        }
    </style>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1>登录</h1>
            <form id="form">
                {% csrf_token %}
                {% for foo in my_form %}
                    <div class="form-group">
                        <label for="{{ foo.auto_id }}">{{ foo.label }}</label>
                        {{ foo }} <span class="error pull-right"></span>
                    </div>
                {% endfor %}
                {#                加一个上传文件的控件#}
                <div class="form-group">
                    <label for="my_file">
                        头像
                        <img src="/static/img/default.png" id="img_file" alt="" width="80" height="80"
                             style="margin-left: 20px;margin-bottom: 20px" title="点击上传头像">
                    </label>
                    <input accept="image/*" type="file" id="my_file">
                </div>
                <input type="button" value="注册" class="btn btn-primary" id="btn"><span class="error"></span>
            </form>
        </div>
    </div>
</div>
</body>
<script>
    $("#my_file").change(function () {
        // 取出文件
        var file_obj = $("#my_file")[0].files[0]
        // 通过文件阅读器,把图片放到img标签上
        // 生成一个文件阅读器对象
        var filereader = new FileReader()
        // 把图片读到filereader对象中
        filereader.readAsDataURL(file_obj)
        // filereader.result是filereader对象的值
        filereader.onload = function () {
            $('#img_file').attr('src', filereader.result)
        }
    })
    $("#btn").click(function () {
        // 因为要上传文件,所以要生成一个formdata对象
        var formdata = new FormData()
        {#formdata.append('name',$('#id_name').val())#}
        {#formdata.append('pwd',$('#id_pwd').val())#}
        {#formdata.append('re_pwd',$('#id_re_pwd').val())#}
        {#formdata.append('email',$('#id_email').val())#}

        var arr = $('#form').serializeArray()
        // jquery的循环
        $.each(arr, function (key, value) {
            formdata.append(value.name, value.value)
        })
        formdata.append('csrfmiddlewaretoken', $('[name="csrfmiddlewaretoken"]').val())
        // 把文件放到formdata中
        formdata.append('my_file', $('#my_file')[0].files[0])

        $.ajax({
            url: '/register/',
            type: 'post',
            processData: false,
            contentType: false,
            data: formdata,
            success: function (data) {
                console.log(data)
                if (data.status === 100) {
                    location.href = data.url
                } else {
                    $('.form-group').removeClass('has-error')
                    $('.error').html('')
                    $.each(data.msg, function (key, value) {
                        console.log(key, value)
                        // 根据key 拼上id 通过id取出控件
                        //$('#id_username').next().html('sb')
                        //$('#id_'+key).next().html(value[0])
                        // 取出爸爸,然后加报错颜色
                        //$('#id_'+key).parent().addClass('has-error')
                        // 处理两次密码 不一致
                        if(key === '__all__'){
                            $('#id_re_password').next().html(value[0])
                        }
                        //  一行代码实现
                        $('#id_' + key).next().html(value[0]).parent().addClass('has-error')
                    })
                    /*setTimeout(function () {
                        // 清除父级的has-error
                        // 清除错误信息
                        $('.form-group').removeClass('has-error')
                        $('.error').html('')
                    },3000)*/
                }
            }
        })
    })

    // name失去焦点,发ajax请求校验用户是否存在
    /*$('#id_username').change(function () {
        alert('change')
    })*/

    //只要发生变化就去校验
    $('#id_username').blur(function () {
        $.ajax({
            url: '/check_username/',
            type: 'post',
            data:{name:$('#id_username').val(),'csrfmiddlewaretoken':'{{ csrf_token }}'},
            success:function (data) {
                if(data.status === 101){
                    $('#id_username').next().html(data.msg).parent().addClass('has-error')
                }
            }
        })
    })
</script>
</html>
代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
]
代码语言:javascript
复制
def check_username(request):
    response = {'status':100,'msg':None}
    name = request.POST.get('name')
    user = models.UserInfo.objects.filter(username=name).first()
    if user:
        response['status'] = 101
        response['msg'] = '用户名已存在'
    return JsonResponse(response)

首页设计


创建index页面

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <title>博客</title>
</head>
<body>
<div class="head">
    <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="#">Brand</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="#">Link <span class="sr-only">(current)</span></a></li>
        <li><a href="#">Link</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="#">Action</a></li>
            <li><a href="#">Another action</a></li>
            <li><a href="#">Something else here</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="#">Separated link</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="#">One more separated link</a></li>
          </ul>
        </li>
      </ul>
      <form class="navbar-form navbar-left">
        <div class="form-group">
          <input type="text" class="form-control" placeholder="Search">
        </div>
        <button type="submit" class="btn btn-default">Submit</button>
      </form>
      <ul class="nav navbar-nav navbar-right">
        <li><a href="#">Link</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="#">Action</a></li>
            <li><a href="#">Another action</a></li>
            <li><a href="#">Something else here</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="#">Separated link</a></li>
          </ul>
        </li>
      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-2"></div>
        <div class="col-md-7"></div>
        <div class="col-md-3"></div>
    </div>
</div>
</body>
</html>

配置路由

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
]

配置视图

代码语言:javascript
复制
def index(request):
    return render(request,'index.html')


修改导航

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <title>博客</title>
</head>
<body>
<div class="head">
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <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>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    <li><a href="#">Link</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                           aria-expanded="false">个人中心 <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="#">修改密码</a></li>
                            <li><a href="#">修改头像</a></li>
                            <li><a href="#">修改主题</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="/logout/">注销</a></li>
                        </ul>
                    </li>
                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-2"></div>
        <div class="col-md-7"></div>
        <div class="col-md-3"></div>
    </div>
</div>
</body>
</html>


实现注销

添加路由

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
]

编辑视图

代码语言:javascript
复制
def logout(request):
    auth.logout(request)
    return redirect('/index/')

显示登录用户名

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <title>博客</title>
</head>
<body>
<div class="head">
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <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>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    {% if request.user.is_authenticated %}
                        <li><a href="#">{{ request.user.username }}</a></li>
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
                               aria-haspopup="true"
                               aria-expanded="false">个人中心<span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li><a href="#">修改密码</a></li>
                                <li><a href="#">修改头像</a></li>
                                <li><a href="#">修改主题</a></li>
                                <li role="separator" class="divider"></li>
                                <li><a href="/logout/">注销</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>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-2"></div>
        <div class="col-md-7"></div>
        <div class="col-md-3"></div>
    </div>
</div>
</body>
</html>


添加侧边栏

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <style>
        body{
            background-image: url("/static/img/bg.jpg");
        }
    </style>
    <title>博客</title>
</head>
<body>
<div class="head">
    <nav class="navbar navbar-inverse">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <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>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    {% if request.user.is_authenticated %}
                        <li><a href="#">{{ request.user.username }}</a></li>
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
                               aria-haspopup="true"
                               aria-expanded="false">个人中心<span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li><a href="#">修改密码</a></li>
                                <li><a href="#">修改头像</a></li>
                                <li><a href="#">修改主题</a></li>
                                <li role="separator" class="divider"></li>
                                <li><a href="/logout/">注销</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>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-2">
            <div class="panel panel-danger">
                <div class="panel-heading">重金求子</div>
                <div class="panel-body">
                    <p>请联系:13800000000</p>
                    <p>年入60w</p>
                </div>
            </div>

            <div class="panel panel-warning">
                <div class="panel-heading">草榴社区</div>
                <div class="panel-body">
                    <a href="http://www.driverzeng.com">请点击跳转</a>
                </div>
            </div>
        </div>
        <div class="col-md-7"></div>
        <div class="col-md-3">
            <div class="panel panel-success">
                <div class="panel-heading">日韩系列</div>
                <div class="panel-body">
                    <a href="http://download.driverzeng.com">Tokyo Hot</a>
                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">欧美系列</div>
                <div class="panel-body">
                    <a href="http://blog.driverzeng.com">金八天国</a>
                </div>
            </div>
            <div class="panel panel-default">
                <div class="panel-heading">动漫卡通</div>
                <div class="panel-body">
                    <a href="http://pikachu.driverzeng.com">女仆</a>
                </div>
            </div>
            <div class="panel panel-info">
                <div class="panel-heading">精品图区</div>
                <div class="panel-body">
                    <a href="http://taiji.driverzeng.com">美腿丝袜</a>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>


主页面

代码语言:javascript
复制
def index(request):
    article_list =  models.Article.objects.all()
    return render(request,'index.html',{'article_list':article_list})

使用admin组件添加数据

http://127.0.0.1:8000/admin

将页面设置为中文settings.py

代码语言:javascript
复制
LANGUAGE_CODE = 'zh-hans'

把数据库的表模型,注册到admin页面上admin.py

代码语言:javascript
复制
from django.contrib import admin
from blog import models
# Register your models here.
admin.site.register(models.UserInfo)
admin.site.register(models.Blog)
admin.site.register(models.Article)
admin.site.register(models.Tag)
admin.site.register(models.Category)
admin.site.register(models.Commit)
admin.site.register(models.UpAndDown)

把表名变成中文

代码语言:javascript
复制
from django.db import models
from django.contrib.auth.models import AbstractUser


# Create your models here.

## UserInfo这个表 要继承AbstractUser,因为要用auth组件
class UserInfo(AbstractUser):
    nid = models.AutoField(primary_key=True)
    phone = models.CharField(max_length=32, null=True)
    ## 这个需要传一个路径
    avatar = models.FileField(upload_to='avatar/', default='/static/img/default.png')
    blog = models.OneToOneField(to='Blog', to_field='nid',null=True)

    class Meta:
        verbose_name = '用户表'
        verbose_name_plural = verbose_name


class Blog(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    sit_name = models.CharField(max_length=32)
    theme = models.CharField(max_length=64)

    class Meta:
        verbose_name = '博客表'
        verbose_name_plural = verbose_name


class Category(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)

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


class Tag(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)

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


class Article(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    desc = models.CharField(max_length=255)
    content = models.TextField()
    create_time = models.DateTimeField(auto_now_add=True)
    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)
    category = models.ForeignKey(to='Category', to_field='nid', null=True)
    tag = models.ManyToManyField(to='Tag', through='ArticleToTag', through_fields=('article', 'tag'))

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


## 手动创建第三张表
class ArticleToTag(models.Model):
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(to='Article', to_field='nid')
    tag = models.ForeignKey(to='Tag', to_field='nid')


class Commit(models.Model):
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey(to='UserInfo', to_field='nid')
    article = models.ForeignKey(to='Article', to_field='nid')
    content = models.CharField(max_length=255)
    create_time = models.DateTimeField(auto_now_add=True)
    ## 自己关联自己:方法一
    parent_id = models.ForeignKey(to='self', to_field='nid')
    ## 自己关联自己:方法二
    # parent_id = models.ForeignKey(to='Commit',to_field='nid')
    class Meta:
        verbose_name = '评论表'
        verbose_name_plural = verbose_name


class UpAndDown(models.Model):
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey(to='UserInfo', to_field='nid')
    article = models.ForeignKey(to='Article', to_field='nid')
    is_up = models.BooleanField()

    ## user和 article不允许一个作者,给一个文章点多个赞,做联合唯一。

    class Meta:
        ## 做这个只是为了不写脏数据,但是效率低,工作中不写也OK
        unique_together = (('user', 'article'),)
        verbose_name = '点赞表'
        verbose_name_plural = verbose_name

添加数据

作者表需要跟blog进行关联

多添加几篇文章

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <title>博客</title>
</head>
<body>
<div class="head">
    <nav class="navbar navbar-inverse">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <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>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    {% if request.user.is_authenticated %}
                        <li><a href="#">{{ request.user.username }}</a></li>
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
                               aria-haspopup="true"
                               aria-expanded="false">个人中心<span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li><a href="#">修改密码</a></li>
                                <li><a href="#">修改头像</a></li>
                                <li><a href="#">修改主题</a></li>
                                <li role="separator" class="divider"></li>
                                <li><a href="/logout/">注销</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>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-2">
            <div class="panel panel-danger">
                <div class="panel-heading">重金求子</div>
                <div class="panel-body">
                    <p>请联系:13800000000</p>
                    <p>年入60w</p>
                </div>
            </div>

            <div class="panel panel-warning">
                <div class="panel-heading">草榴社区</div>
                <div class="panel-body">
                    <a href="http://www.driverzeng.com">请点击跳转</a>
                </div>
            </div>
        </div>
        <div class="col-md-7">
            {% for article in article_list %}
                <div>
                    <h3><a href="">{{ article.title }}</a></h3>
                    <div class="media">
                        <div class="media-left">
                            <a href="#">
                                <img class="media-object" src="/static/img/default.png">
                            </a>
                        </div>
                        <div class="media-body">
                            {{ article.desc }}
                        </div>
                    </div>
                </div>
            {% endfor %}

        </div>
        <div class="col-md-3">
            <div class="panel panel-success">
                <div class="panel-heading">日韩系列</div>
                <div class="panel-body">
                    <a href="http://download.driverzeng.com">Tokyo Hot</a>
                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">欧美系列</div>
                <div class="panel-body">
                    <a href="http://blog.driverzeng.com">金八天国</a>
                </div>
            </div>
            <div class="panel panel-default">
                <div class="panel-heading">动漫卡通</div>
                <div class="panel-body">
                    <a href="http://pikachu.driverzeng.com">女仆</a>
                </div>
            </div>
            <div class="panel panel-info">
                <div class="panel-heading">精品图区</div>
                <div class="panel-body">
                    <a href="http://taiji.driverzeng.com">美腿丝袜</a>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

评论点赞处理


添底部作者信息及发布时间

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <title>博客</title>
</head>
<body>
<div class="head">
    <nav class="navbar navbar-inverse">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <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>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    {% if request.user.is_authenticated %}
                        <li><a href="#">{{ request.user.username }}</a></li>
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
                               aria-haspopup="true"
                               aria-expanded="false">个人中心<span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li><a href="#">修改密码</a></li>
                                <li><a href="#">修改头像</a></li>
                                <li><a href="#">修改主题</a></li>
                                <li role="separator" class="divider"></li>
                                <li><a href="/logout/">注销</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>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-2">
            <div class="panel panel-danger">
                <div class="panel-heading">重金求子</div>
                <div class="panel-body">
                    <p>请联系:13800000000</p>
                    <p>年入60w</p>
                </div>
            </div>

            <div class="panel panel-warning">
                <div class="panel-heading">草榴社区</div>
                <div class="panel-body">
                    <a href="http://www.driverzeng.com">请点击跳转</a>
                </div>
            </div>
        </div>
        <div class="col-md-7">
            {% for article in article_list %}
                <div>
                    <h4><a href="">{{ article.title }}</a></h4>
                    <div class="media">
                        <div class="media-left">
                            <a href="#">
                                <img class="media-object" src="/static/img/default.png">
                            </a>
                        </div>
                        <div class="media-body">
                            {{ article.desc }}
                        </div>
                    </div>
                    <div>
                        <span><a href="">{{ article.blog.userinfo.username }}</a></span>
                        <span>发布于{{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                    </div>
                </div>
            {% endfor %}

        </div>
        <div class="col-md-3">
            <div class="panel panel-success">
                <div class="panel-heading">日韩系列</div>
                <div class="panel-body">
                    <a href="http://download.driverzeng.com">Tokyo Hot</a>
                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">欧美系列</div>
                <div class="panel-body">
                    <a href="http://blog.driverzeng.com">金八天国</a>
                </div>
            </div>
            <div class="panel panel-default">
                <div class="panel-heading">动漫卡通</div>
                <div class="panel-body">
                    <a href="http://pikachu.driverzeng.com">女仆</a>
                </div>
            </div>
            <div class="panel panel-info">
                <div class="panel-heading">精品图区</div>
                <div class="panel-body">
                    <a href="http://taiji.driverzeng.com">美腿丝袜</a>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>


修改时区问题

代码语言:javascript
复制
TIME_ZONE = 'Asia/Shanghai'


添加评论和点赞

字段修改

代码语言:javascript
复制
class Article(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    desc = models.CharField(max_length=255)
    content = models.TextField()
    create_time = models.DateTimeField(auto_now_add=True)
    ## 评论数
    commit_num = models.IntegerField(default=0)
    ## 点赞数
    up_num = models.IntegerField(default=0)
    ## 点踩数
    down_num = models.IntegerField(default=0)

    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)
    category = models.ForeignKey(to='Category', to_field='nid', null=True)
    tag = models.ManyToManyField(to='Tag', through='ArticleToTag', through_fields=('article', 'tag'))

    class Meta:
        verbose_name = '文章表'
        verbose_name_plural = verbose_name
    def __str__(self):
        return self.title

数据迁移

代码语言:javascript
复制
MacBook-pro:bbs driverzeng$ python3 manage.py makemigrations
MacBook-pro:bbs driverzeng$ python3 manage.py migrate

添加几条评论

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <style>
        .bottom span{
            margin-right: 16px;
        }
    </style>
    <title>博客</title>
</head>
<body>
<div class="head">
    <nav class="navbar navbar-inverse">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <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>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    {% if request.user.is_authenticated %}
                        <li><a href="#">{{ request.user.username }}</a></li>
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
                               aria-haspopup="true"
                               aria-expanded="false">个人中心<span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li><a href="#">修改密码</a></li>
                                <li><a href="#">修改头像</a></li>
                                <li><a href="#">修改主题</a></li>
                                <li role="separator" class="divider"></li>
                                <li><a href="/logout/">注销</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>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-2">
            <div class="panel panel-danger">
                <div class="panel-heading">重金求子</div>
                <div class="panel-body">
                    <p>请联系:13800000000</p>
                    <p>年入60w</p>
                </div>
            </div>

            <div class="panel panel-warning">
                <div class="panel-heading">草榴社区</div>
                <div class="panel-body">
                    <a href="http://www.driverzeng.com">请点击跳转</a>
                </div>
            </div>
        </div>
        <div class="col-md-7">
            {% for article in article_list %}
                <div>
                    <h4><a href="">{{ article.title }}</a></h4>
                    <div class="media">
                        <div class="media-left">
                            <a href="#">
                                <img class="media-object" src="/static/img/default.png">
                            </a>
                        </div>
                        <div class="media-body">
                            {{ article.desc }}
                        </div>
                    </div>
                    <div style="margin-top: 20px;margin-bottom: 30px" class="bottom">
                        <span><a href="">{{ article.blog.userinfo.username }}</a></span>
                        <span>发布于{{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                        <span class="glyphicon glyphicon-comment"><a href="" style="margin-left: 2px">评论({{ article.commit_num }})</a></span>
                        <span class="glyphicon glyphicon-thumbs-up"><a href="" style="margin-left: 2px">点赞({{ article.up_num}})</a></span>
                    </div>
                </div>
            {% endfor %}

        </div>
        <div class="col-md-3">
            <div class="panel panel-success">
                <div class="panel-heading">日韩系列</div>
                <div class="panel-body">
                    <a href="http://download.driverzeng.com">Tokyo Hot</a>
                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">欧美系列</div>
                <div class="panel-body">
                    <a href="http://blog.driverzeng.com">金八天国</a>
                </div>
            </div>
            <div class="panel panel-default">
                <div class="panel-heading">动漫卡通</div>
                <div class="panel-body">
                    <a href="http://pikachu.driverzeng.com">女仆</a>
                </div>
            </div>
            <div class="panel panel-info">
                <div class="panel-heading">精品图区</div>
                <div class="panel-body">
                    <a href="http://taiji.driverzeng.com">美腿丝袜</a>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

显示头像


配置头像上传路径

settings.py

代码语言:javascript
复制
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')

测试注册用户上传图片

因为数据库创建字段的时候,写的upload_to='avatar/',所以会在media目录下创建一个avatar目录,然后把图像上传进去,并且,media目录也不需要咱们手动创建,会自动创建。


在页面显示图片

首先配置路由: 创建有名分组 1.url第一个参数,正则表达式 2.第二个参数,函数的内存地址 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数) 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对

settings.py

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
]

models.py

代码语言:javascript
复制
class UserInfo(AbstractUser):
    nid = models.AutoField(primary_key=True)
    phone = models.CharField(max_length=32, null=True)
    ## 这个需要传一个路径
    avatar = models.FileField(upload_to='avatar/', default='avatar/default.png')
    blog = models.OneToOneField(to='Blog', to_field='nid',null=True)

    class Meta:
        verbose_name = '用户表'
        verbose_name_plural = verbose_name

数据库迁移

代码语言:javascript
复制
MacBook-pro:bbs driverzeng$ python3 manage.py makemigrations
MacBook-pro:bbs driverzeng$ python3 manage.py migrate

模板层页面

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <style>
        .bottom span {
            margin-right: 16px;
        }
    </style>
    <title>博客</title>
</head>
<body>
<div class="head">
    <nav class="navbar navbar-inverse">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <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>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    {% if request.user.is_authenticated %}
                        <li><a href="#">{{ request.user.username }}</a></li>
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
                               aria-haspopup="true"
                               aria-expanded="false">个人中心<span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li><a href="#">修改密码</a></li>
                                <li><a href="#">修改头像</a></li>
                                <li><a href="#">修改主题</a></li>
                                <li role="separator" class="divider"></li>
                                <li><a href="/logout/">注销</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>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-2">
            <div class="panel panel-danger">
                <div class="panel-heading">重金求子</div>
                <div class="panel-body">
                    <p>请联系:13800000000</p>
                    <p>年入60w</p>
                </div>
            </div>

            <div class="panel panel-warning">
                <div class="panel-heading">草榴社区</div>
                <div class="panel-body">
                    <a href="http://www.driverzeng.com">请点击跳转</a>
                </div>
            </div>
        </div>
        <div class="col-md-7">
            {% for article in article_list %}
                <div>
                    <h4><a href="">{{ article.title }}</a></h4>
                    <div class="media">
                        <div class="media-left">
                            <a href="#">
                                <img  width="60px" height="60px" class="media-object" src="/media/{{ article.blog.userinfo.avatar }}">
                            </a>
                        </div>
                        <div class="media-body">
                            {{ article.desc }}
                        </div>
                    </div>
                    <div style="margin-top: 20px;margin-bottom: 30px" class="bottom">
                        <span><a href="">{{ article.blog.userinfo.username }}</a></span>
                        <span>发布于{{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                        <span class="glyphicon glyphicon-comment"><a href=""
                                                                     style="margin-left: 2px">评论({{ article.commit_num }})</a></span>
                        <span class="glyphicon glyphicon-thumbs-up"><a href=""
                                                                       style="margin-left: 2px">点赞({{ article.up_num }})</a></span>
                    </div>
                </div>
            {% endfor %}

        </div>
        <div class="col-md-3">
            <div class="panel panel-success">
                <div class="panel-heading">日韩系列</div>
                <div class="panel-body">
                    <a href="http://download.driverzeng.com">Tokyo Hot</a>
                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">欧美系列</div>
                <div class="panel-body">
                    <a href="http://blog.driverzeng.com">金八天国</a>
                </div>
            </div>
            <div class="panel panel-default">
                <div class="panel-heading">动漫卡通</div>
                <div class="panel-body">
                    <a href="http://pikachu.driverzeng.com">女仆</a>
                </div>
            </div>
            <div class="panel panel-info">
                <div class="panel-heading">精品图区</div>
                <div class="panel-body">
                    <a href="http://taiji.driverzeng.com">美腿丝袜</a>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

个人站点


路由设计

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
    # 放到最后,都匹配完成,没有匹配到,再匹配它
    url(r'^(?P<username>[\w]+)', views.user_blog),
]

视图设计

代码语言:javascript
复制
def user_blog(request, username):
    print(username)
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'error.html')

    blog = user.blog
    return render(request, 'user_blog.html', locals())

模板层

error.html

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>404错误</title>
</head>
<body>

<a href="http://www.cnblogs.com/"><img src="//static.cnblogs.com/images/logo_small.gif" alt="cnblogs"></a>

<p class="d">请确认您输入的网址是否正确,如果问题持续存在,请发邮件至contact@cnblogs.com与我们联系。</p>
<p><b>404.</b> 抱歉! 您访问的资源不存在!</p><p><a href="http://www.cnblogs.com/">返回网站首页</a></p>


</body>
</html>
代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ blog.userinfo.username }}-的个人博客</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <link rel="stylesheet" href="/static/css/{{ blog.theme }}">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

    </style>
</head>
<body>
<div class="head">
    <p>{{ blog.title }}</p>
</div>
<div class="container-fluid">
    <div class="row">

        <div class="col-md-3">

            <div class="panel panel-primary">
                <div class="panel-heading">我的标签</div>
                <div class="panel-body">
                   {% for foo in tag_num %}
                   <p><a href="">{{ foo.0 }}({{ foo.1 }})</a></p>
                   {% endfor %}

                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">我的分类</div>
                <div class="panel-body">
                    {% for foo in category_num %}
                        <p><a href="">{{ foo.0 }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">随笔档案</div>
                <div class="panel-body">
                    {% for foo in y_m_num %}
                    <p><a href="">{{ foo.0|date:"Y-m" }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>
        </div>
        <div class="col-md-9">
            {#通过站点查询所有文章,反向查询,按表名小写_set.all#}
            {% for article in blog.article_set.all %}
                <div>
                <h4><a href="">{{ article.title }}</a></h4>
                <div class="media">


                    <div>

                        {{ article.desc }}
                    </div>

                    <div style="margin-top: 10px " class="article_bottom small">


                        <span>posted @ {{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                        {#反向查询,一对多,按表名小写_set#}
                        {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}

                        <span>{{ article.blog.userinfo.username }}</span>
                        <span><i class="fa fa-comment" aria-hidden="true"><a
                                href="">评论({{ article.commit_num }})</a></i></span>

                        {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}
                        <span class="glyphicon glyphicon-thumbs-up"><a href="">点赞({{ article.up_num }})</a></span>
                        <span><a href="">编辑</a></span>
                    </div>
                    <hr>
                </div>
            {% endfor %}



            </div>
        </div>
    </div>
</div>
</body>
</html>


添加分类

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ blog.userinfo.username }}-的个人博客</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <link rel="stylesheet" href="/static/css/{{ blog.theme }}">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

    </style>
</head>
<body>
<div class="head">
    <p>{{ blog.title }}</p>
</div>
<div class="container-fluid">
    <div class="row">

        <div class="col-md-3">

            <div class="panel panel-primary">
                <div class="panel-heading">我的标签</div>
                <div class="panel-body">
                   {% for foo in tag_num %}
                   <p><a href="">{{ foo.0 }}({{ foo.1 }})</a></p>
                   {% endfor %}

                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">我的分类</div>
                <div class="panel-body">
                    {% for foo in category_num %}
                        <p><a href="">{{ foo.0 }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">随笔档案</div>
                <div class="panel-body">
                    {% for foo in y_m_num %}
                    <p><a href="">{{ foo.0|date:"Y-m" }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>
        </div>
        <div class="col-md-9">
            {#通过站点查询所有文章,反向查询,按表名小写_set.all#}
            {% for article in blog.article_set.all %}
                <div>
                <h4><a href="">{{ article.title }}</a></h4>
                <div class="media">


                    <div>

                        {{ article.desc }}
                    </div>

                    <div style="margin-top: 10px " class="article_bottom small">


                        <span>posted @ {{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                        {#反向查询,一对多,按表名小写_set#}
                        {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}

                        <span>{{ article.blog.userinfo.username }}</span>
                        <span><i class="fa fa-comment" aria-hidden="true"><a
                                href="">评论({{ article.commit_num }})</a></i></span>

                        {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}
                        <span class="glyphicon glyphicon-thumbs-up"><a href="">点赞({{ article.up_num }})</a></span>
                        <span><a href="">编辑</a></span>
                    </div>
                    <hr>
                </div>
            {% endfor %}



            </div>
        </div>
    </div>
</div>
</body>
</html>
代码语言:javascript
复制
def user_blog(request, username):
    print(username)
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'error.html')

    blog = user.blog
    # 查询当前站点下所有的分类,对应的文章数

    # 每个的分类,对应的文章数
    #group by谁,就以谁做基表
    #ret=models.Category.objects.all().annotate(coun=Count('article__title')).values('title','coun')
    #filter在前表示where value在前表group by
    #value在后表示取值,fileter在后,表示having
    #先过滤出当前站点下所有的分类
    #ret=models.Category.objects.all().filter(blog=blog)
    #ret=models.Category.objects.all().filter(blog=blog).values('pk').annotate(coun=Count('article__title')).values('title','coun')
    #结果跟上面一样
    #ret=models.Category.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values('title','coun')
    category_num = models.Category.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list('title', 'coun')
    print(category_num)
    return render(request, 'user_blog.html', locals())


添加标签

导入标签的第三张表

代码语言:javascript
复制
from django.contrib import admin
from blog import models
# Register your models here.
admin.site.register(models.UserInfo)
admin.site.register(models.Blog)
admin.site.register(models.Article)
admin.site.register(models.Tag)
admin.site.register(models.Category)
admin.site.register(models.Commit)
admin.site.register(models.UpAndDown)
admin.site.register(models.ArticleToTag)

视图

代码语言:javascript
复制
def user_blog(request, username):
    print(username)
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'error.html')

    blog = user.blog
    # 查询当前站点下所有的分类,对应的文章数

    # 每个的分类,对应的文章数
    #group by谁,就以谁做基表
    #ret=models.Category.objects.all().annotate(coun=Count('article__title')).values('title','coun')
    #filter在前表示where value在前表group by
    #value在后表示取值,fileter在后,表示having
    #先过滤出当前站点下所有的分类
    #ret=models.Category.objects.all().filter(blog=blog)
    #ret=models.Category.objects.all().filter(blog=blog).values('pk').annotate(coun=Count('article__title')).values('title','coun')
    #结果跟上面一样
    #ret=models.Category.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values('title','coun')
    category_num = models.Category.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list('title', 'coun')
    print(category_num)

    # 查询当前站点下每个标签对应的文章数
    tag_num = models.Tag.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list('title','coun')
    print(tag_num)
    return render(request, 'user_blog.html', locals())

模板

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ blog.userinfo.username }}-的个人博客</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <link rel="stylesheet" href="/static/css/{{ blog.theme }}">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

    </style>
</head>
<body>
<div class="head">
    <p>{{ blog.title }}</p>
</div>
<div class="container-fluid">
    <div class="row">

        <div class="col-md-3">

            <div class="panel panel-primary">
                <div class="panel-heading">我的标签</div>
                <div class="panel-body">
                   {% for foo in tag_num %}
                   <p><a href="">{{ foo.0 }}({{ foo.1 }})</a></p>
                   {% endfor %}

                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">我的分类</div>
                <div class="panel-body">
                    {% for foo in category_num %}
                        <p><a href="">{{ foo.0 }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">随笔档案</div>
                <div class="panel-body">
                    {% for foo in y_m_num %}
                    <p><a href="">{{ foo.0|date:"Y-m" }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>
        </div>
        <div class="col-md-9">
            {#通过站点查询所有文章,反向查询,按表名小写_set.all#}
            {% for article in blog.article_set.all %}
                <div>
                <h4><a href="">{{ article.title }}</a></h4>
                <div class="media">


                    <div>

                        {{ article.desc }}
                    </div>

                    <div style="margin-top: 10px " class="article_bottom small">


                        <span>posted @ {{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                        {#反向查询,一对多,按表名小写_set#}
                        {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}

                        <span>{{ article.blog.userinfo.username }}</span>
                        <span><i class="fa fa-comment" aria-hidden="true"><a
                                href="">评论({{ article.commit_num }})</a></i></span>

                        {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}
                        <span class="glyphicon glyphicon-thumbs-up"><a href="">点赞({{ article.up_num }})</a></span>
                        <span><a href="">编辑</a></span>
                    </div>
                    <hr>
                </div>
            {% endfor %}



            </div>
        </div>
    </div>
</div>
</body>
</html>

随笔档案

我们要来按日期归档。

修改一下数据库的时间


使用截断

代码语言:javascript
复制
'''
from django.db.models.functions import TruncMonth
Sales.objects
.annotate(month=TruncMonth('timestamp'))  # Truncate to month and add to select list
.values('month')  # Group By month
.annotate(c=Count('id'))  # Select the count of the grouping
.values('month', 'c')  # (might be redundant, haven't tested) select month and count
'''

## 截断函数

文章标题                时间            blog_id      month
文章1     2018-07-03 02:55:42.391000      1       2018-07
文章2     2018-07-24 03:08:19.705000      1       2018-07
文章3     2019-08-23 03:09:09.747000      1       2019-08

group by month
from django.db.models.functions import TruncMonth
y_m_num = models.Article.objects.all().filter(blog=blog).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(coun=Count('y_m')).values('y_m', 'coun')

修改时区 settings.py

代码语言:javascript
复制
## 不用UTC时间
USE_TZ = False

视图层

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO
from django.contrib import auth
from django.http import JsonResponse
from blog import myforms
from blog import models
from django.db.models import Count

def user_blog(request, username):
    print(username)
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'error.html')

    blog = user.blog
    # 查询当前站点下所有的分类,对应的文章数

    # 每个的分类,对应的文章数
    #group by谁,就以谁做基表
    #ret=models.Category.objects.all().annotate(coun=Count('article__title')).values('title','coun')
    #filter在前表示where value在前表group by
    #value在后表示取值,fileter在后,表示having
    #先过滤出当前站点下所有的分类
    #ret=models.Category.objects.all().filter(blog=blog)
    #ret=models.Category.objects.all().filter(blog=blog).values('pk').annotate(coun=Count('article__title')).values('title','coun')
    #结果跟上面一样
    #ret=models.Category.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values('title','coun')
    category_num = models.Category.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list('title', 'coun')
    print(category_num)

    # 查询当前站点下每个标签对应的文章数
    tag_num = models.Tag.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list('title','coun')
    print(tag_num)

    '''
        from django.db.models.functions import TruncMonth
        Sales.objects
        .annotate(month=TruncMonth('timestamp'))  # Truncate to month and add to select list
        .values('month')  # Group By month
        .annotate(c=Count('id'))  # Select the count of the grouping
        .values('month', 'c')  # (might be redundant, haven't tested) select month and count

    '''
    # 查询当前站点下按年月分类的文章数
    from django.db.models.functions import TruncMonth
    # y_m_num = models.Article.objects.all().filter(blog=blog).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(coun=Count('y_m')).values('y_m', 'coun')
    y_m_num = models.Article.objects.all().filter(blog=blog).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(coun=Count('y_m')).values_list('y_m', 'coun')
    print(y_m_num)
    return render(request, 'user_blog.html', locals())

模板层

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ blog.userinfo.username }}-的个人博客</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <link rel="stylesheet" href="/static/css/{{ blog.theme }}">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

    </style>
</head>
<body>
<div class="head">
    <p>{{ blog.title }}</p>
</div>
<div class="container-fluid">
    <div class="row">

        <div class="col-md-3">

            <div class="panel panel-primary">
                <div class="panel-heading">我的标签</div>
                <div class="panel-body">
                   {% for foo in tag_num %}
                   <p><a href="">{{ foo.0 }}({{ foo.1 }})</a></p>
                   {% endfor %}

                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">我的分类</div>
                <div class="panel-body">
                    {% for foo in category_num %}
                        <p><a href="">{{ foo.0 }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">随笔档案</div>
                <div class="panel-body">
                    {% for foo in y_m_num %}
                    <p><a href="">{{ foo.0|date:"Y-m" }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>
        </div>
        <div class="col-md-9">
            {#通过站点查询所有文章,反向查询,按表名小写_set.all#}
            {% for article in blog.article_set.all %}
                <div>
                <h4><a href="">{{ article.title }}</a></h4>
                <div class="media">


                    <div>

                        {{ article.desc }}
                    </div>

                    <div style="margin-top: 10px " class="article_bottom small">


                        <span>posted @ {{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                        {#反向查询,一对多,按表名小写_set#}
                        {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}

                        <span>{{ article.blog.userinfo.username }}</span>
                        <span><i class="fa fa-comment" aria-hidden="true"><a
                                href="">评论({{ article.commit_num }})</a></i></span>

                        {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}
                        <span class="glyphicon glyphicon-thumbs-up"><a href="">点赞({{ article.up_num }})</a></span>
                        <span><a href="">编辑</a></span>
                    </div>
                    <hr>
                </div>
            {% endfor %}



            </div>
        </div>
    </div>
</div>
</body>
</html>

改成中文的年月

代码语言:javascript
复制
<p><a href="">{{ foo.0|date:"Y年m月" }}({{ foo.1 }})</a></p>

底部放右侧

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ blog.userinfo.username }}-的个人博客</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <link rel="stylesheet" href="/static/css/{{ blog.theme }}">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

    </style>
</head>
<body>
<div class="head">
    <p>{{ blog.title }}</p>
</div>
<div class="container-fluid">
    <div class="row">

        <div class="col-md-3">

            <div class="panel panel-primary">
                <div class="panel-heading">我的标签</div>
                <div class="panel-body">
                    {% for foo in tag_num %}
                        <p><a href="">{{ foo.0 }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">我的分类</div>
                <div class="panel-body">
                    {% for foo in category_num %}
                        <p><a href="">{{ foo.0 }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">随笔档案</div>
                <div class="panel-body">
                    {% for foo in y_m_num %}
                        <p><a href="">{{ foo.0|date:"Y年m月" }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>
        </div>
        <div class="col-md-9">
            {#通过站点查询所有文章,反向查询,按表名小写_set.all#}
            {% for article in blog.article_set.all %}
                <div>
                <h4><a href="">{{ article.title }}</a></h4>
                <div class="media">


                    <div>

                        {{ article.desc }}
                    </div>
                    <div class="clearfix">
                        <div style="margin-top: 10px " class="article_bottom small pull-right">


                            <span>posted @ {{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                            {#反向查询,一对多,按表名小写_set#}
                            {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}

                            <span>{{ article.blog.userinfo.username }}</span>
                            <span><i class="fa fa-comment" aria-hidden="true"><a
                                    href="">评论({{ article.commit_num }})</a></i></span>

                            {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}
                            <span class="glyphicon glyphicon-thumbs-up"><a href="">点赞({{ article.up_num }})</a></span>
                            <span><a href="">编辑</a></span>
                        </div>
                    </div>
                    <hr>
                </div>
            {% endfor %}



            </div>
        </div>
    </div>
</div>
</body>
</html>

个人站点分类过滤文章


路由设计

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
    ## 个人站点过滤
    url(r'^(?P<username>[\w]+)/category/(?P<category_id>\d+)', views.user_blog),
    # 放到最后,都匹配完成,没有匹配到,再匹配它
    url(r'^(?P<username>[\w]+)', views.user_blog),
]

视图函数

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO
from django.contrib import auth
from django.http import JsonResponse
from blog import myforms
from blog import models
from django.db.models import Count

def user_blog(request, username,*args,**kwargs):
    print(username)
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'error.html')
    # 先取出category_id
    category_id =  kwargs.get('category_id',None)
    blog = user.blog
    ## 过滤这个人所有的文章
    article_list = blog.article_set.all()
    # 判断category_id,如果有值直接过滤
    if category_id:
        article_list = article_list.filter(category__pk=category_id)


    # 查询当前站点下所有的分类,对应的文章数
    # 每个的分类,对应的文章数
    #group by谁,就以谁做基表
    #ret=models.Category.objects.all().annotate(coun=Count('article__title')).values('title','coun')
    #filter在前表示where value在前表group by
    #value在后表示取值,fileter在后,表示having
    #先过滤出当前站点下所有的分类
    #ret=models.Category.objects.all().filter(blog=blog)
    #ret=models.Category.objects.all().filter(blog=blog).values('pk').annotate(coun=Count('article__title')).values('title','coun')
    #结果跟上面一样
    #ret=models.Category.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values('title','coun')
    category_num = models.Category.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list('title', 'coun')
    print(category_num)

    # 查询当前站点下每个标签对应的文章数
    tag_num = models.Tag.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list('title','coun')
    print(tag_num)

    '''
        from django.db.models.functions import TruncMonth
        Sales.objects
        .annotate(month=TruncMonth('timestamp'))  # Truncate to month and add to select list
        .values('month')  # Group By month
        .annotate(c=Count('id'))  # Select the count of the grouping
        .values('month', 'c')  # (might be redundant, haven't tested) select month and count

    '''
    # 查询当前站点下按年月分类的文章数
    from django.db.models.functions import TruncMonth
    # y_m_num = models.Article.objects.all().filter(blog=blog).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(coun=Count('y_m')).values('y_m', 'coun')
    y_m_num = models.Article.objects.all().filter(blog=blog).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(coun=Count('y_m')).values_list('y_m', 'coun')
    print(y_m_num)
    return render(request, 'user_blog.html', locals())

个人站点标签过滤文章(三合一)


路由设计

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
    ## 个人站点分类过路由
    url(r'^(?P<username>[\w]+)/category/(?P<category_id>\d+)', views.user_blog),
    ## 个人站点标签过滤路由
    url(r'^(?P<username>[\w]+)/tag/(?P<tag>\d+)', views.user_blog),
    # 放到最后,都匹配完成,没有匹配到,再匹配它
    url(r'^(?P<username>[\w]+)', views.user_blog),
]

视图设计

代码语言:javascript
复制
def user_blog(request, username,*args,**kwargs):
    print(username)
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'error.html')
    
    '''
    过滤分类
    '''
    # 先取出category_id
    category_id =  kwargs.get('category_id',None)
    blog = user.blog
    ## 过滤这个人所有的文章
    article_list = blog.article_set.all()
    # 判断category_id,如果有值直接过滤
    if category_id:
        article_list = article_list.filter(category__pk=category_id)


    '''
    过滤标签
    '''
    tag_id = kwargs.get('tag_id', None)
    blog = user.blog
    ## 过滤这个人所有的文章
    article_list = blog.article_set.all()
    # 判断category_id,如果有值直接过滤
    if tag_id:
        article_list = article_list.filter(category__pk=tag_id)

    # 查询当前站点下所有的分类,对应的文章数
    # 每个的分类,对应的文章数
    #group by谁,就以谁做基表
    #ret=models.Category.objects.all().annotate(coun=Count('article__title')).values('title','coun')
    #filter在前表示where value在前表group by
    #value在后表示取值,fileter在后,表示having
    #先过滤出当前站点下所有的分类
    #ret=models.Category.objects.all().filter(blog=blog)
    #ret=models.Category.objects.all().filter(blog=blog).values('pk').annotate(coun=Count('article__title')).values('title','coun')
    #结果跟上面一样
    #ret=models.Category.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values('title','coun')
    category_num = models.Category.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list('title', 'coun')
    print(category_num)

    # 查询当前站点下每个标签对应的文章数
    tag_num = models.Tag.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list('title','coun')
    print(tag_num)

    '''
        from django.db.models.functions import TruncMonth
        Sales.objects
        .annotate(month=TruncMonth('timestamp'))  # Truncate to month and add to select list
        .values('month')  # Group By month
        .annotate(c=Count('id'))  # Select the count of the grouping
        .values('month', 'c')  # (might be redundant, haven't tested) select month and count

    '''
    # 查询当前站点下按年月分类的文章数
    from django.db.models.functions import TruncMonth
    # y_m_num = models.Article.objects.all().filter(blog=blog).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(coun=Count('y_m')).values('y_m', 'coun')
    y_m_num = models.Article.objects.all().filter(blog=blog).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(coun=Count('y_m')).values_list('y_m', 'coun')
    print(y_m_num)
    return render(request, 'user_blog.html', locals())

会发现,路由和视图,代码基本差不多,就中间的字段不一样,所以我们来节省代码。包括随笔的过滤也是一样的,我们三合一


路由合并

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
    ## 个人站点分类过路由
    # url(r'^(?P<username>[\w]+)/category/(?P<category_id>\d+)', views.user_blog),
    ## 个人站点标签过滤路由
    # url(r'^(?P<username>[\w]+)/tag/(?P<tag>\d+)', views.user_blog),

    ## 路由三合一(分类,标签,随笔)
    # 分组分出三个,(用户名,category|tag|archive中的一个,可能是分类id  tagid或者时间)
    url(r'^(?P<username>[\w]+)/(?P<condition>category|tag|archive)/(?P<param>.*)', views.user_blog),

    # 放到最后,都匹配完成,没有匹配到,再匹配它
    url(r'^(?P<username>[\w]+)$', views.user_blog),
]

视图合并

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO
from django.contrib import auth
from django.http import JsonResponse
from blog import myforms
from blog import models
from django.db.models import Count
from django.db.models.functions import TruncMonth

def user_blog(request, username, *args, **kwargs):
    print(username)
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'error.html')
    blog = user.blog
    article_list = blog.article_set.all()
    condition = kwargs.get('condition')
    param = kwargs.get('param')
    if condition == 'tag':
        article_list = article_list.filter(tag__pk=param)
    elif condition == 'category':
        article_list = article_list.filter(category__pk=param)
    elif condition == 'archive':
        archive_list = param.split('-')
        article_list = article_list.filter(create_time__year=archive_list[0], create_time__month=archive_list[1])

    category_num = models.Category.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list(
        'title', 'coun', 'pk')
    print(category_num)
    tag_num = models.Tag.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list('title',
                                                                                                            'coun',
                                                                                                            'pk')
    print(tag_num)
    y_m_num = models.Article.objects.all().filter(blog=blog).annotate(y_m=TruncMonth('create_time')).values(
        'y_m').annotate(coun=Count('y_m')).values_list('y_m', 'coun')
    print(y_m_num)
    return render(request, 'user_blog.html', locals())

修改点击跳转

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ blog.userinfo.username }}-的个人博客</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <link rel="stylesheet" href="/static/css/{{ blog.theme }}">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

    </style>
</head>
<body>
<div class="head">
    <p>{{ blog.title }}</p>
</div>
<div class="container-fluid">
    <div class="row">

        <div class="col-md-3">

            <div class="panel panel-primary">
                <div class="panel-heading">我的标签</div>
                <div class="panel-body">
                    {% for foo in tag_num %}
                        <p><a href="/{{ username }}/tag/{{ foo.2 }}">{{ foo.0 }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">我的分类</div>
                <div class="panel-body">
                    {% for foo in category_num %}
                        <p><a href="/{{ username }}/category/{{ foo.2 }}">{{ foo.0 }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">随笔档案</div>
                <div class="panel-body">
                    {% for foo in y_m_num %}
                        <p><a href="/{{ username }}/archive/{{ foo.0|date:"Y-m" }}">{{ foo.0|date:"Y年m月" }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>
        </div>
        <div class="col-md-9">
            {#通过站点查询所有文章,反向查询,按表名小写_set.all#}
            {% for article in article_list %}
                <div>
                <h4><a href="">{{ article.title }}</a></h4>
                <div class="media">


                    <div>

                        {{ article.desc }}
                    </div>
                    <div class="clearfix">
                        <div style="margin-top: 10px " class="article_bottom small pull-right">


                            <span>posted @ {{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                            {#反向查询,一对多,按表名小写_set#}
                            {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}

                            <span>{{ article.blog.userinfo.username }}</span>
                            <span><i class="fa fa-comment" aria-hidden="true"><a
                                    href="">评论({{ article.commit_num }})</a></i></span>

                            {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}
                            <span class="glyphicon glyphicon-thumbs-up"><a href="">点赞({{ article.up_num }})</a></span>
                            <span><a href="">编辑</a></span>
                        </div>
                    </div>
                    <hr>
                </div>
            {% endfor %}



            </div>
        </div>
    </div>
</div>
</body>
</html>
代码语言:javascript
复制
def user_blog(request, username, *args, **kwargs):
    print(username)
    username = username
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'error.html')
    blog = user.blog
    article_list = blog.article_set.all()
    condition = kwargs.get('condition')
    param = kwargs.get('param')
    if condition == 'tag':
        article_list = article_list.filter(tag__pk=param)
    elif condition == 'category':
        article_list = article_list.filter(category__pk=param)
    elif condition == 'archive':
        archive_list = param.split('-')
        article_list = article_list.filter(create_time__year=archive_list[0], create_time__month=archive_list[1])

    category_num = models.Category.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list(
        'title', 'coun', 'pk')
    print(category_num)
    tag_num = models.Tag.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list('title',
                                                                                                            'coun',
                                                                                                            'pk')
    print(tag_num)

    from django.db.models.functions import TruncMonth
    y_m_num = models.Article.objects.all().filter(blog=blog).annotate(y_m=TruncMonth('create_time')).values(
        'y_m').annotate(coun=Count('y_m')).values_list('y_m', 'coun')
    print(y_m_num)
    return render(request, 'user_blog.html', locals())

文章详情页


路由设计

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
    ## 个人站点分类过路由
    # url(r'^(?P<username>[\w]+)/category/(?P<category_id>\d+)', views.user_blog),
    ## 个人站点标签过滤路由
    # url(r'^(?P<username>[\w]+)/tag/(?P<tag>\d+)', views.user_blog),

    ## 路由三合一(分类,标签,随笔)
    # 分组分出三个,(用户名,category|tag|archive中的一个,可能是分类id  tagid或者时间)
    url(r'^(?P<username>[\w]+)/(?P<condition>category|tag|archive)/(?P<param>.*)', views.user_blog),
    url(r'^(?P<username>[\w]+)/article/(?P<id>\d+)', views.article_detail),

    # 放到最后,都匹配完成,没有匹配到,再匹配它
    url(r'^(?P<username>[\w]+)/$', views.user_blog),
]

视图设计

代码语言:javascript
复制
def article_detail(request,username,id):
    article = models.Article.objects.filter(pk=id)
    
    return render(request,'article_detail.html',locals())

模板继承

base.html

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ blog.userinfo.username }}-的个人博客</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <link rel="stylesheet" href="/static/css/{{ blog.theme }}">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

    </style>
</head>
<body>
<div class="head">
    <p>{{ blog.title }}</p>
</div>
<div class="container-fluid">
    <div class="row">

        <div class="col-md-3">

            <div class="panel panel-primary">
                <div class="panel-heading">我的标签</div>
                <div class="panel-body">
                    {% for foo in tag_num %}
                        <p><a href="/{{ username }}/tag/{{ foo.2 }}">{{ foo.0 }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">我的分类</div>
                <div class="panel-body">
                    {% for foo in category_num %}
                        <p><a href="/{{ username }}/category/{{ foo.2 }}">{{ foo.0 }}({{ foo.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">随笔档案</div>
                <div class="panel-body">
                    {% for foo in y_m_num %}
                        <p>
                            <a href="/{{ username }}/archive/{{ foo.0|date:"Y-m" }}">{{ foo.0|date:"Y年m月" }}({{ foo.1 }})</a>
                        </p>
                    {% endfor %}

                </div>
            </div>
        </div>
        <div class="col-md-9">
            {% block content %}
            {% endblock %}
        </div>
    </div>
</div>
</div>
</body>
</html>

user_blog.html

代码语言:javascript
复制
{% extends 'base.html' %}
{% block content %}
    {#通过站点查询所有文章,反向查询,按表名小写_set.all#}
    {% for article in article_list %}
        <div>
            <h4><a href="">{{ article.title }}</a></h4>
            <div class="media">


                <div>

                    {{ article.desc }}
                </div>
                <div class="clearfix">
                    <div style="margin-top: 10px " class="article_bottom small pull-right">


                        <span>posted @ {{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                        {#反向查询,一对多,按表名小写_set#}
                        {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}

                        <span>{{ article.blog.userinfo.username }}</span>
                        <span><i class="fa fa-comment" aria-hidden="true"><a
                                href="">评论({{ article.commit_num }})</a></i></span>

                        {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}
                        <span class="glyphicon glyphicon-thumbs-up"><a href="">点赞({{ article.up_num }})</a></span>
                        <span><a href="">编辑</a></span>
                    </div>
                </div>
                <hr>
            </div>
        </div>
    {% endfor %}
{% endblock %}

inclution_tag

blog/templatetags/my_tag.py

代码语言:javascript
复制
from django.template import Library
from blog import models
from django.db.models import Count
from django.db.models.functions import TruncMonth

register = Library()


@register.inclusion_tag('classify.html')
def classify(username):
    user = models.UserInfo.objects.filter(username=username).first()
    blog = user.blog
    category_num = models.Category.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list(
        'title', 'coun', 'pk')
    tag_num = models.Tag.objects.all().filter(blog=blog).annotate(coun=Count('article__title')).values_list('title',
                                                                                                            'coun',
                                                                                                            'pk')
    y_m_num = models.Article.objects.all().filter(blog=blog).annotate(y_m=TruncMonth('create_time')).values(
        'y_m').annotate(coun=Count('y_m')).values_list('y_m', 'coun')

    return {'category_num': category_num, 'tag_num': tag_num, 'y_m_num': y_m_num, 'username': username}

classify.html

代码语言:javascript
复制
<div>
    <div class="panel panel-primary">
        <div class="panel-heading">我的标签</div>
        <div class="panel-body">
            {% for foo in tag_num %}
                <p><a href="/{{ username }}/tag/{{ foo.2 }}">{{ foo.0 }}({{ foo.1 }})</a></p>
            {% endfor %}

        </div>
    </div>

    <div class="panel panel-primary">
        <div class="panel-heading">我的分类</div>
        <div class="panel-body">
            {% for foo in category_num %}
                <p><a href="/{{ username }}/category/{{ foo.2 }}">{{ foo.0 }}({{ foo.1 }})</a></p>
            {% endfor %}

        </div>
    </div>

    <div class="panel panel-primary">
        <div class="panel-heading">随笔档案</div>
        <div class="panel-body">
            {% for foo in y_m_num %}
                <p>
                    <a href="/{{ username }}/archive/{{ foo.0|date:"Y-m" }}">{{ foo.0|date:"Y年m月" }}({{ foo.1 }})</a>
                </p>
            {% endfor %}

        </div>
    </div>
</div>

base.html

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ blog.userinfo.username }}-的个人博客</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <link rel="stylesheet" href="/static/css/{{ blog.theme }}">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

    </style>
</head>
<body>
<div class="head">
    <p>{{ blog.title }}</p>
</div>
<div class="container-fluid">
    <div class="row">

        <div class="col-md-3">
            {% load my_tag %}
            {% classify username %}
        </div>
        <div class="col-md-9">
            {% block content %}
            {% endblock %}
        </div>
    </div>
</div>
</div>
</body>
</html>

views.py

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO
from django.contrib import auth
from django.http import JsonResponse
from blog import myforms
from blog import models
from django.db.models import Count
from django.db.models.functions import TruncMonth


# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    ## 判断前台发的请求是不是ajax的请求
    elif request.is_ajax():
        response = {'user': None, 'msg': None}
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        valid_code = request.POST.get('valid_code')

        if valid_code.upper() == request.session.get('valid_code').upper():
            user = auth.authenticate(request, username=name, password=pwd)
            if user:
                ## ajax请求,不能再返回render页面或者redirect页面,只能返回字符串或者json
                ## 校验通过,一定要登录
                auth.login(request, user)
                response['user'] = name
                response['msg'] = '登录成功'
            else:
                response['msg'] = '用户名密码错误'
        else:
            response['msg'] = '验证码错误'

    return JsonResponse(response)


def get_random_color_light():
    return (random.randint(0, 100), random.randint(0, 100), random.randint(0, 100))


def get_random_color_dark():
    return (random.randint(150, 255), random.randint(150, 255), random.randint(150, 255))


def get_valid_code(request):
    img = Image.new('RGB', (320, 35), color=get_random_color_light())
    # 拿到画笔,在图片上画画
    img_draw = ImageDraw.Draw(img)

    # 生成一个字体对象 ,第一个参数是字体文件路径,第二个参数,是字体大小
    font = ImageFont.truetype('static/font/ss.TTF', size=26)

    ## 保存验证码
    random_code = ''
    ## 写一个循环,循环5次,每次随机写一个(数字,大写,小写字母)
    for i in range(5):
        char_num = random.randint(0, 9)
        # 生成97 ~ 122的数字,生成字母
        char_lower = chr(random.randint(97, 122))
        char_upper = chr(random.randint(65, 90))
        char_str = str(random.choice([char_num, char_lower, char_upper]))

        ## 第一个参数,xy的坐标,第二个参数,文字,第三个参数,颜色,第四个参数,字体。
        img_draw.text((i * 60 + 20, 0), char_str, get_random_color_dark(), font=font)
        random_code += char_str

    ## 把验证码 保存到session中
    request.session['valid_code'] = random_code

    width = 350
    height = 45
    for i in range(10):
        x1 = random.randint(0, width)
        x2 = random.randint(0, width)
        y1 = random.randint(0, height)
        y2 = random.randint(0, height)

        # 在图片上画线
        img_draw.line((x1, y1, x2, y2), fill=get_random_color_light())

    for i in range(100):
        img_draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color_light())
        x = random.randint(0, width)
        y = random.randint(0, height)
        img_draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color_light())

    ## 在内存中生成一个空文件
    f = BytesIO()
    ## 把图片保存到f中
    img.save(f, 'png')
    ## 取出图片
    data = f.getvalue()
    return HttpResponse(data)


def register(request):
    if request.method == 'GET':
        my_form = myforms.RegForm()
        return render(request, 'register.html', {'my_form': my_form})
    elif request.is_ajax():
        response = {'status': 100, 'msg': None}
        my_form = myforms.RegForm(request.POST)
        if my_form.is_valid():
            ## 定义一个字段,把通过的数据赋值给字典
            dic = my_form.cleaned_data
            ## 移除确认密码的字段
            dic.pop('re_password')
            ## 取出上传的文件对象
            my_file = request.FILES.get('my_file')
            ## 放到字典中
            ## 如果没有上传文件,数据库存默认值
            if my_file:
                dic['avatar'] = my_file
            user = models.UserInfo.objects.create_user(**dic)
            print(user.username)
            response['url'] = '/login/'
        else:
            response['status'] = 101
            response['msg'] = my_form.errors

        return JsonResponse(response)


def check_username(request):
    response = {'status': 100, 'msg': None}
    name = request.POST.get('name')
    user = models.UserInfo.objects.filter(username=name).first()
    if user:
        response['status'] = 101
        response['msg'] = '用户名已存在'
    return JsonResponse(response)


def index(request):
    article_list = models.Article.objects.all()
    return render(request, 'index.html', {'article_list': article_list})


def logout(request):
    auth.logout(request)
    return redirect('/index/')


def user_blog(request, username, *args, **kwargs):
    username = username
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'error.html')
    blog = user.blog
    article_list = blog.article_set.all()
    condition = kwargs.get('condition')
    param = kwargs.get('param')
    if condition == 'tag':
        article_list = article_list.filter(tag__pk=param)
    elif condition == 'category':
        article_list = article_list.filter(category__pk=param)
    elif condition == 'archive':
        archive_list = param.split('-')
        article_list = article_list.filter(create_time__year=archive_list[0], create_time__month=archive_list[1])
    return render(request, 'user_blog.html', locals())

def article_detail(request,username,id):
    username = username
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'error.html')
    blog = user.blog
    article = models.Article.objects.filter(pk=id)

    return render(request,'article_detail.html',locals())


详情页跳转

代码语言:javascript
复制
{% extends 'base.html' %}
{% block content %}
    {#通过站点查询所有文章,反向查询,按表名小写_set.all#}
    {% for article in article_list %}
        <div>
            <h4><a href="/{{ username }}/article/{{ article.pk }}">{{ article.title }}</a></h4>
            <div class="media">


                <div>

                    {{ article.desc }}
                </div>
                <div class="clearfix">
                    <div style="margin-top: 10px " class="article_bottom small pull-right">


                        <span>posted @ {{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                        {#反向查询,一对多,按表名小写_set#}
                        {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}

                        <span>{{ article.blog.userinfo.username }}</span>
                        <span><i class="fa fa-comment" aria-hidden="true"><a
                                href="">评论({{ article.commit_num }})</a></i></span>

                        {#                        <span class="glyphicon glyphicon-comment"><a href="">评论({{ article.commit_num }})</a></span>#}
                        <span class="glyphicon glyphicon-thumbs-up"><a href="">点赞({{ article.up_num }})</a></span>
                        <span><a href="">编辑</a></span>
                    </div>
                </div>
                <hr>
            </div>
        </div>
    {% endfor %}
{% endblock %}

主页跳转文章详情和个人中心

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <style>
        .bottom span {
            margin-right: 16px;
        }
    </style>
    <title>博客</title>
</head>
<body>
<div class="head">
    <nav class="navbar navbar-inverse">
        <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
                <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>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    {% if request.user.is_authenticated %}
                        <li><a href="#">{{ request.user.username }}</a></li>
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
                               aria-haspopup="true"
                               aria-expanded="false">个人中心<span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li><a href="#">修改密码</a></li>
                                <li><a href="#">修改头像</a></li>
                                <li><a href="#">修改主题</a></li>
                                <li role="separator" class="divider"></li>
                                <li><a href="/logout/">注销</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>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-2">
            <div class="panel panel-danger">
                <div class="panel-heading">重金求子</div>
                <div class="panel-body">
                    <p>请联系:13800000000</p>
                    <p>年入60w</p>
                </div>
            </div>

            <div class="panel panel-warning">
                <div class="panel-heading">草榴社区</div>
                <div class="panel-body">
                    <a href="http://www.driverzeng.com">请点击跳转</a>
                </div>
            </div>
        </div>
        <div class="col-md-7">
            {% for article in article_list %}
                <div>
                    <h4><a href="/{{ article.blog.userinfo.username }}/article/{{ article.pk }}">{{ article.title }}</a></h4>
                    <div class="media">
                        <div class="media-left">
                            <a href="#">
                                <img  width="60px" height="60px" class="media-object" src="/media/{{ article.blog.userinfo.avatar }}">
                            </a>
                        </div>
                        <div class="media-body">
                            {{ article.desc }}
                        </div>
                    </div>
                    <div style="margin-top: 20px;margin-bottom: 30px" class="bottom">
                        <span><a href="/{{ article.blog.userinfo.username }}/">{{ article.blog.userinfo.username }}</a></span>
                        <span>发布于{{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                        <span class="glyphicon glyphicon-comment"><a href=""
                                                                     style="margin-left: 2px">评论({{ article.commit_num }})</a></span>
                        <span class="glyphicon glyphicon-thumbs-up"><a href=""
                                                                       style="margin-left: 2px">点赞({{ article.up_num }})</a></span>
                    </div>
                </div>
            {% endfor %}

        </div>
        <div class="col-md-3">
            <div class="panel panel-success">
                <div class="panel-heading">日韩系列</div>
                <div class="panel-body">
                    <a href="http://download.driverzeng.com">Tokyo Hot</a>
                </div>
            </div>

            <div class="panel panel-primary">
                <div class="panel-heading">欧美系列</div>
                <div class="panel-body">
                    <a href="http://blog.driverzeng.com">金八天国</a>
                </div>
            </div>
            <div class="panel panel-default">
                <div class="panel-heading">动漫卡通</div>
                <div class="panel-body">
                    <a href="http://pikachu.driverzeng.com">女仆</a>
                </div>
            </div>
            <div class="panel panel-info">
                <div class="panel-heading">精品图区</div>
                <div class="panel-body">
                    <a href="http://taiji.driverzeng.com">美腿丝袜</a>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

构造文章详情页

views.py

代码语言:javascript
复制
def article_detail(request,username,id):
    username = username
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'error.html')
    blog = user.blog
    article = models.Article.objects.filter(pk=id).first()

    return render(request,'article_detail.html',locals())
代码语言:javascript
复制
{% extends 'base.html' %}
{% block content %}
<div>
<h4 class="text-center">{{ article.title }}</h4>
<div>
    {{ article.content }}
</div>
</div>
{% endblock %}

文章内容,好....

好踏马的丑。

文章里面存的都是html的标签。我们来修改一下。

卧槽,妖兽啦~为啥没好使?

因为需要使用模板语言的safe

代码语言:javascript
复制
{% extends 'base.html' %}
{% block content %}
<div>
<h4 class="text-center">{{ article.title }}</h4>
<div>
    {{ article.content|safe }}
</div>
</div>
{% endblock %}

文章点赞功能


模板层

base.html 引入一个css文件

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ blog.userinfo.username }}-的个人博客</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <link rel="stylesheet" href="/static/css/mycss.css">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <link rel="stylesheet" href="/static/css/{{ blog.theme }}">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

    </style>
</head>
<body>
<div class="head">
    <p>{{ blog.title }}</p>
</div>
<div class="container-fluid">
    <div class="row">

        <div class="col-md-3">
            {% load my_tag %}
            {% classify username %}
        </div>
        <div class="col-md-9">
            {% block content %}
            {% endblock %}
        </div>
    </div>
</div>
</div>
</body>
</html>

mycss.css

代码语言:javascript
复制
#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/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/img/downdown.gif) no-repeat;
    text-align: center;
    cursor: pointer;
    margin-top: 2px;
    padding-top: 5px;
}
.clear {
    clear: both;
}
.diggword {
    margin-top: 5px;
    margin-left: 0;
    font-size: 12px;
    color: gray;
}

article_detail.html

代码语言:javascript
复制
{% extends 'base.html' %}
{% block content %}
    <div>
        <h4 class="text-center">{{ article.title }}</h4>
        <div>
            {{ article.content|safe }}
        </div>
        <div id="div_digg">
            <div class="diggit">
                <span class="diggnum" id="digg_count">{{ article.up_num }}</span>
            </div>
            <div class="buryit">
                <span class="burynum" id="bury_count">{{ article.down_num }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color: red;">您已经反对过</div>
        </div>

    </div>
    </div>
{% endblock %}

点赞,向后台发起ajax请求。

代码语言:javascript
复制
{% extends 'base.html' %}
{% block content %}
    <div>
        <h4 class="text-center">{{ article.title }}</h4>
        <div>
            {{ article.content|safe }}
        </div>
        <div id="div_digg">
            <div class="diggit">
                <span class="diggnum" id="digg_count">{{ article.up_num }}</span>
            </div>
            <div class="buryit">
                <span class="burynum" id="bury_count">{{ article.down_num }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color: red;">您已经反对过</div>
        </div>

    </div>
    </div>

    <script>
        $(".diggit").click(function () {
            $.ajax({
                url: '/diggit/',
                type: 'post',
                //谁对哪篇文章,点赞
                //谁,可以不传吗?从后台取
                data: {
                    article_id: '{{ article.pk }}',
                    is_up: true,
                    'csrfmiddlewaretoken': '{{ csrf_token }}'
                },
                success: function (data) {
                    console.log(data)

                }
            })
        })
    </script>
{% endblock %}
代码语言:javascript
复制
def diggit(request):
    response={'status':100,'msg':None}


    if request.user.is_authenticated():
        # 从前端传过来的数据,都转成str类型
        article_id=request.POST.get('article_id')
        is_up=request.POST.get('is_up')
        print(is_up)
        print(type(is_up))
        # 用json转
        # # python中的
        # {'is_up':True}
        # # 转成json
        # {"is_up": "true"}
        is_up=json.loads(is_up)
        print(is_up)
        print(type(is_up))
        # 原子性操作.用事务
        with transaction.atomic():
            models.UpAndDown.objects.create(user=request.user,article_id=article_id,is_up=is_up)
            models.Article.objects.filter(pk=article_id).update(up_num=F('up_num')+1)
            response['msg']='点赞成功'
    else:
        response['msg'] = '请先登录'
        response['status'] = 101
    return JsonResponse(response)
代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
    ## 个人站点分类过路由
    # url(r'^(?P<username>[\w]+)/category/(?P<category_id>\d+)', views.user_blog),
    ## 个人站点标签过滤路由
    # url(r'^(?P<username>[\w]+)/tag/(?P<tag>\d+)', views.user_blog),
    
    # 点赞的路由
    url(r'^diggit/$', views.diggit),
    
    
    ## 路由三合一(分类,标签,随笔)
    # 分组分出三个,(用户名,category|tag|archive中的一个,可能是分类id  tagid或者时间)
    url(r'^(?P<username>[\w]+)/(?P<condition>category|tag|archive)/(?P<param>.*)', views.user_blog),
    url(r'^(?P<username>[\w]+)/article/(?P<id>\d+)', views.article_detail),

    # 放到最后,都匹配完成,没有匹配到,再匹配它
    url(r'^(?P<username>[\w]+)/$', views.user_blog),
]


合并点赞点踩以及评论功能

article_detail.html

代码语言:javascript
复制
{% extends 'base.html' %}



{% block content %}

    {#{% csrf_token %}#}

    <div>
        <p><h4 class="text-center">{{ article.title }}</h4></p>
        <div>
            {{ article.content|safe }}
        </div>
        {#点赞#}
        <div class="clearfix">
            <div id="div_digg">
                <div class="diggit action">
                    <span class="diggnum" id="digg_count">{{ article.up_num }}</span>
                </div>
                <div class="buryit action">
                    <span class="burynum" id="bury_count">{{ article.down_num }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips" style="color: red;"></div>
            </div>
        </div>

        {#评论#}
        <div>
            <ul class="list-group cotent_ul">

                {% for content in content_list %}
                    <li class="list-group-item">
                        <p><span>#{{ forloop.counter }}楼</span>
                            <span>{{ content.create_time|date:'Y-m-d H:i:s' }}</span>
                            <span><a href="/{{ content.user.username }}">{{ content.user.username }}</a></span>
                            <span class="pull-right replay" username="{{ content.user.username }}" content_id="{{ content.pk }}">回复</span>
                        </p>
                        {% if content.parent %}
                            {#content.parent 拿到的是什么? 拿到父评论的对象#}
                            <p class="well">@{{ content.parent.user.username }}</p>

                        {% endif %}
                        {{ content.content }}
                    </li>
                {% endfor %}


            </ul>
        </div>
        <div>
            <p>发表评论</p>
            <p>
                昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="曾老湿">
            </p>
            <p>评论内容:</p>
            <p>
                <textarea name="" id="content" cols="60" rows="10">


            </textarea>
            </p>
            <button class="btn btn-primary submit">提交</button>


        </div>
    </div>

    <script>
        //全局变量
        var pid = ''
        //评论相关(跟评论的方法)
        /*
        $('.submit').click(function () {
            //评论要提交什么内容
            //谁?又不需要传对哪篇文章评论了什么内容

            var content = $("#content").val()
            $("#content").val("")
            $.ajax({
                url: '/commit_content/',
                type: 'post',
                data: {
                    'article_id': '{{ article.pk }}',
                    'content': content,
                    'pid': '',
                    'csrfmiddlewaretoken': '{{ csrf_token }}'
                },
                success: function (data) {
                    console.log(data)
                    //拼  用户名:时间
                    //    评论内容    这些数据都应该从后台返回
                    var time=data.time
                    var content=data.content
                    var user_name=data.user_name
                    var ss=`
                      <li class="list-group-item">
                        <p>
                            <span>${ user_name }</span>:
                            <span>${ time }</span>
                        </p>
                         ${content}
                    </li>
                    `
                    $(".cotent_ul").append(ss)

                }
            })


        })
        */
        //字评论和根评论
        $('.submit').click(function () {
            var content = $("#content").val()
            $("#content").val("")

            if (pid) {
                //pid有值,才切掉头部
                //拿到content要切的起始位置indexOf(),取到指定值的索引值
                //取到 \n 索引位置的后一位
                var index = content.indexOf('\n') + 1
                alert('评论成功')
                //slice传一个起始位置,一个结束位置,就可以切出来
                content = content.slice(index)
            }


            $.ajax({
                url: '/commit_content/',
                type: 'post',
                data: {
                    'article_id': '{{ article.pk }}',
                    'content': content,
                    'pid': pid,
                    'csrfmiddlewaretoken': '{{ csrf_token }}'
                },
                success: function (data) {
                    console.log(data)
                    //拼  用户名:时间
                    //    评论内容    这些数据都应该从后台返回
                    var time = data.time
                    var content = data.content
                    var user_name = data.user_name
                    var ss =''
                    if (pid){
                        //需要清空一下父评论的id
                        {#pid=''#}
                        var parent_name=data.parent_name
                        ss=`
                         <li class="list-group-item">
                         <p>
                         <span>${ user_name }</span>
                            <span>${ time }</span>
                        </p>
                            <p class="well">@${parent_name }</p>
                        ${content}
                    </li>
                        `
                    }else {
                         ss= `
                      <li class="list-group-item">
                        <p>
                            <span>${ user_name }</span>:
                            <span>${ time }</span>
                        </p>
                         ${content}
                    </li>
                    `
                    }

                    $(".cotent_ul").append(ss)

                }
            })
        })
        $(".replay").click(function () {

            //拿到span标签中username属性对应的值
            var username = $(this).attr('username')
            //alert(username)
            //把拼接的字符串放到content内,并且换行
            $('#content').val('@' + username + '\n')
            //让光标聚焦到这个控件
            $('#content').focus()
            //给pid赋值(pid是父评论的id)
            pid = $(this).attr('content_id')


        })
        //点赞相关
        $(".action").click(function () {
            //判断当前点击的div控件,有没有diggit类
            var is_up = $(this).hasClass('diggit')

            var obj = $(this).children('span')
            //alert(is_up)
            $.ajax({
                url: '/diggit/',
                type: 'post',
                //谁对哪篇文章,点赞
                //谁,可以不传吗?从后台取
                data: {article_id: '{{ article.pk }}', is_up: is_up, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
                success: function (data) {
                    console.log(data)
                    //在点赞下方显示信息提示
                    $("#digg_tips").html(data.msg)
                    if (data.status == 100) {
                        //如果返回成功,点赞数或点踩数加1(第一种方案)
                        /*
                        if(is_up){
                            //这个值是字符串
                           var count= $("#digg_count").text()
                            //把count转成int类型
                            //这两种方式都可以
                            //$("#digg_count").text(parseInt(count)+1)
                            $("#digg_count").text(Number(count)+1)
                        }else{
                            var count= $("#bury_count").text()
                            $("#bury_count").text(Number(count)+1)
                        }
                        */
                        //第二种方案
                        //ojb是当前点击div中的span标签,先取出span中的数字,+1,然后放到span中
                        obj.text(Number(obj.text()) + 1)

                    }


                    //过三秒清除提示
                    setTimeout(function () {
                        $("#digg_tips").html("")
                    }, 3000)

                }
            })

        })
        /*
            $(".diggit").click(function () {


            })

            function test() {

                $.ajax({
                    url: '/diggit/',
                    type: 'post',
                    //谁对哪篇文章,点赞
                    //谁,可以不传吗?从后台取
                    data: {article_id: '{{ article.pk }}', is_up: true, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
                success: function (data) {
                    console.log(data)

                }
            })

        }
        */


    </script>


{% endblock %}

views.py

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO
from django.contrib import auth
from django.http import JsonResponse
from blog import myforms
from blog import models
from django.db.models import Count
from django.db.models.functions import TruncMonth
from django.contrib.auth.decorators import login_required
import json
from django.db import transaction
from django.db.models import F


# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    ## 判断前台发的请求是不是ajax的请求
    elif request.is_ajax():
        response = {'user': None, 'msg': None}
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        valid_code = request.POST.get('valid_code')

        if valid_code.upper() == request.session.get('valid_code').upper():
            user = auth.authenticate(request, username=name, password=pwd)
            if user:
                ## ajax请求,不能再返回render页面或者redirect页面,只能返回字符串或者json
                ## 校验通过,一定要登录
                auth.login(request, user)
                response['user'] = name
                response['msg'] = '登录成功'
            else:
                response['msg'] = '用户名密码错误'
        else:
            response['msg'] = '验证码错误'

    return JsonResponse(response)


def get_random_color_light():
    return (random.randint(0, 100), random.randint(0, 100), random.randint(0, 100))


def get_random_color_dark():
    return (random.randint(150, 255), random.randint(150, 255), random.randint(150, 255))


def get_valid_code(request):
    img = Image.new('RGB', (320, 35), color=get_random_color_light())
    # 拿到画笔,在图片上画画
    img_draw = ImageDraw.Draw(img)

    # 生成一个字体对象 ,第一个参数是字体文件路径,第二个参数,是字体大小
    font = ImageFont.truetype('static/font/ss.TTF', size=26)

    ## 保存验证码
    random_code = ''
    ## 写一个循环,循环5次,每次随机写一个(数字,大写,小写字母)
    for i in range(5):
        char_num = random.randint(0, 9)
        # 生成97 ~ 122的数字,生成字母
        char_lower = chr(random.randint(97, 122))
        char_upper = chr(random.randint(65, 90))
        char_str = str(random.choice([char_num, char_lower, char_upper]))

        ## 第一个参数,xy的坐标,第二个参数,文字,第三个参数,颜色,第四个参数,字体。
        img_draw.text((i * 60 + 20, 0), char_str, get_random_color_dark(), font=font)
        random_code += char_str

    ## 把验证码 保存到session中
    request.session['valid_code'] = random_code

    width = 350
    height = 45
    for i in range(10):
        x1 = random.randint(0, width)
        x2 = random.randint(0, width)
        y1 = random.randint(0, height)
        y2 = random.randint(0, height)

        # 在图片上画线
        img_draw.line((x1, y1, x2, y2), fill=get_random_color_light())

    for i in range(100):
        img_draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color_light())
        x = random.randint(0, width)
        y = random.randint(0, height)
        img_draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color_light())

    ## 在内存中生成一个空文件
    f = BytesIO()
    ## 把图片保存到f中
    img.save(f, 'png')
    ## 取出图片
    data = f.getvalue()
    return HttpResponse(data)


def register(request):
    if request.method == 'GET':
        my_form = myforms.RegForm()
        return render(request, 'register.html', {'my_form': my_form})
    elif request.is_ajax():
        response = {'status': 100, 'msg': None}
        my_form = myforms.RegForm(request.POST)
        if my_form.is_valid():
            ## 定义一个字段,把通过的数据赋值给字典
            dic = my_form.cleaned_data
            ## 移除确认密码的字段
            dic.pop('re_password')
            ## 取出上传的文件对象
            my_file = request.FILES.get('my_file')
            ## 放到字典中
            ## 如果没有上传文件,数据库存默认值
            if my_file:
                dic['avatar'] = my_file
            user = models.UserInfo.objects.create_user(**dic)
            print(user.username)
            response['url'] = '/login/'
        else:
            response['status'] = 101
            response['msg'] = my_form.errors

        return JsonResponse(response)


def check_username(request):
    response = {'status': 100, 'msg': None}
    name = request.POST.get('name')
    user = models.UserInfo.objects.filter(username=name).first()
    if user:
        response['status'] = 101
        response['msg'] = '用户名已存在'
    return JsonResponse(response)


def index(request):
    article_list = models.Article.objects.all()
    return render(request, 'index.html', {'article_list': article_list})


def logout(request):
    auth.logout(request)
    return redirect('/index/')


def user_blog(request, username, *args, **kwargs):
    username = username
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'error.html')
    blog = user.blog
    article_list = blog.article_set.all()
    condition = kwargs.get('condition')
    param = kwargs.get('param')
    if condition == 'tag':
        article_list = article_list.filter(tag__pk=param)
    elif condition == 'category':
        article_list = article_list.filter(category__pk=param)
    elif condition == 'archive':
        archive_list = param.split('-')
        article_list = article_list.filter(create_time__year=archive_list[0], create_time__month=archive_list[1])
    return render(request, 'user_blog.html', locals())


def article_detail(request, username, id):
    username = username
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return render(request, 'error.html')
    blog = user.blog
    article = models.Article.objects.filter(pk=id).first()
    content_list = article.commit_set.all().order_by('pk')

    return render(request, 'article_detail.html', locals())


def diggit(request):
    response = {'status': 100, 'msg': None}

    if request.user.is_authenticated():
        # 从前端传过来的数据,都转成str类型
        article_id = request.POST.get('article_id')
        is_up = request.POST.get('is_up')
        is_up = json.loads(is_up)
        user = request.user
        # 存之前先查询,当前用户对该篇文章是否点过
        ret = models.UpAndDown.objects.filter(user_id=user.pk, article_id=article_id).exists()
        if ret:
            # 当有数据,说明,已经点过赞或者踩了
            response['msg'] = '您已经点过了'
            response['status'] = 101
        else:
            # 原子性操作.用事务
            with transaction.atomic():
                models.UpAndDown.objects.create(user=user, article_id=article_id, is_up=is_up)
                # 先取出文章的queryset对象
                article = models.Article.objects.filter(pk=article_id)
                if is_up:
                    article.update(up_num=F('up_num') + 1)
                    response['msg'] = '点赞成功'
                else:
                    article.update(down_num=F('down_num') + 1)
                    response['msg'] = '反对成功'
    else:
        response['msg'] = '请先登录'
        response['status'] = 101
    return JsonResponse(response)


def commit_content(request):
    response = {'status': 100, 'msg': None}
    if request.is_ajax():
        if request.user.is_authenticated():
            # 核心逻辑
            user = request.user
            article_id = request.POST.get('article_id')
            content = request.POST.get('content')
            pid = request.POST.get('pid')
            print(pid)
            with transaction.atomic():
                ret = models.Commit.objects.create(user=user, article_id=article_id, content=content, parent_id_id=pid)
                models.Article.objects.filter(pk=article_id).update(commit_num=F('commit_num') + 1)

            response['msg'] = '评论成功'
            response['content'] = ret.content
            # 把datetime类型转成字符串,因为json是无法序列化datetime
            response['time'] = ret.create_time.strftime('%Y-%m-%d %X')
            response['user_name'] = ret.user.username
            if pid:
                # 如果是字评论,返回父评论的名字
                response['parent_name'] = ret.parent.user.username


        else:
            response['status'] = 101
            response['msg'] = '您没有登录'
    else:
        response['status'] = 101
        response['msg'] = '您请求非法'
    print(response)
    return JsonResponse(response)

urls.py

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
    ## 个人站点分类过路由
    # url(r'^(?P<username>[\w]+)/category/(?P<category_id>\d+)', views.user_blog),
    ## 个人站点标签过滤路由
    # url(r'^(?P<username>[\w]+)/tag/(?P<tag>\d+)', views.user_blog),

    # 点赞的路由
    url(r'^diggit/$', views.diggit),
    # p评论的路由
    url(r'^commit_content/$', views.commit_content),

    ## 路由三合一(分类,标签,随笔)
    # 分组分出三个,(用户名,category|tag|archive中的一个,可能是分类id  tagid或者时间)
    url(r'^(?P<username>[\w]+)/(?P<condition>category|tag|archive)/(?P<param>.*)', views.user_blog),
    url(r'^(?P<username>[\w]+)/article/(?P<id>\d+)', views.article_detail),

    # 放到最后,都匹配完成,没有匹配到,再匹配它
    url(r'^(?P<username>[\w]+)/$', views.user_blog),
]

后台管理功能


路由

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
    ## 个人站点分类过路由
    # url(r'^(?P<username>[\w]+)/category/(?P<category_id>\d+)', views.user_blog),
    ## 个人站点标签过滤路由
    # url(r'^(?P<username>[\w]+)/tag/(?P<tag>\d+)', views.user_blog),

    # 点赞的路由
    url(r'^diggit/$', views.diggit),
    # p评论的路由
    url(r'^commit_content/$', views.commit_content),
    url(r'^backend/', views.backend),

    ## 路由三合一(分类,标签,随笔)
    # 分组分出三个,(用户名,category|tag|archive中的一个,可能是分类id  tagid或者时间)
    url(r'^(?P<username>[\w]+)/(?P<condition>category|tag|archive)/(?P<param>.*)', views.user_blog),
    url(r'^(?P<username>[\w]+)/article/(?P<id>\d+)', views.article_detail),

    # 放到最后,都匹配完成,没有匹配到,再匹配它
    url(r'^(?P<username>[\w]+)/$', views.user_blog),
]

视图

代码语言:javascript
复制
@login_required(login_url='/login/')
def backend(request):
    if request.method == 'GET':
        return render(request,'back/backend.html')

    return HttpResponse('OK')

模板

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>后台管理</title>
</head>
<body>
这里是后台管理页面
</body>
</html>

查询所有文章


视图

代码语言:javascript
复制
@login_required(login_url='/login/')
def backend(request):
    if request.method == 'GET':
        blog = request.user.blog
        article_list = models.Article.objects.filter(blog=blog)
        return render(request,'back/backend.html',{'article_list':article_list})

    return HttpResponse('OK')

模板

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <link rel="stylesheet" href="/static/css/mycss.css">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <title>后台管理</title>
    <style>
        .head {
            height: 60px;
            background: aquamarine;
            margin-bottom: 20px;
        }
    </style>
</head>
<body>

<div class="head">
    <p>后台管理</p>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-3">

            <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
                <div class="panel panel-default">
                    <div class="panel-heading" role="tab" id="headingOne">
                        <h4 class="panel-title">
                            <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne"
                               aria-expanded="true" aria-controls="collapseOne">
                                操作
                            </a>
                        </h4>
                    </div>
                    <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
                         aria-labelledby="headingOne">
                        <div class="panel-body">
                            <a href="">添加文章</a>
                        </div>
                    </div>
                    <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
                         aria-labelledby="headingOne">
                        <div class="panel-body">
                            <a href="">添加随笔</a>
                        </div>
                    </div>
                    <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
                         aria-labelledby="headingOne">
                        <div class="panel-body">
                            <a href="">其他操作</a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="col-md-9">
            <table class="table table-hover table-striped">
                <thead>
                <tr>
                    <th>文章标题</th>
                    <th>发布时间</th>
                    <th>评论数</th>
                    <th>点赞数</th>
                    <th>修改</th>
                    <th>删除</th>
                </tr>
                </thead>
                <tbody>
                {% for article in article_list %}
                <tr>
                <td><a href="/{{ article.blog.userinfo.username }}/article/{{ article.pk }}">{{ article.title }}</a></td>
                <td>{{ article.create_time|date:'Y-m-d H:i:s' }}</td>
                <td>{{ article.commit_num }}</td>
                <td>{{ article.up_num }}</td>
                <td><a href="">修改</a></td>
                <td><a href="">删除</a></td>
                </tr>
                {% endfor %}

                </tbody>
            </table>
        </div>
    </div>
</div>
</body>
</html>

路由

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
    ## 个人站点分类过路由
    # url(r'^(?P<username>[\w]+)/category/(?P<category_id>\d+)', views.user_blog),
    ## 个人站点标签过滤路由
    # url(r'^(?P<username>[\w]+)/tag/(?P<tag>\d+)', views.user_blog),

    # 点赞的路由
    url(r'^diggit/$', views.diggit),
    # p评论的路由
    url(r'^commit_content/$', views.commit_content),
    url(r'^backend/', views.backend),

    ## 路由三合一(分类,标签,随笔)
    # 分组分出三个,(用户名,category|tag|archive中的一个,可能是分类id  tagid或者时间)
    url(r'^(?P<username>[\w]+)/(?P<condition>category|tag|archive)/(?P<param>.*)', views.user_blog),
    url(r'^(?P<username>[\w]+)/article/(?P<id>\d+)', views.article_detail),

    # 放到最后,都匹配完成,没有匹配到,再匹配它
    url(r'^(?P<username>[\w]+)/$', views.user_blog),
]


添加标签和样式

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <link rel="stylesheet" href="/static/css/mycss.css">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <title>后台管理</title>
    <style>
        .head {
            height: 60px;
            background: aquamarine;
            margin-bottom: 20px;
        }

        .a1 {
            background: red;
            color: black;
        }

        .a2 {
            background: orange;
            color: black;
        }

        .a3 {
            background: yellow;
            color: black;
        }

        .a4 {
            background: lawngreen;
            color: black;
        }

        .a5 {
            background: #2aabd2;
            color: black;
        }

        .a6 {
            background: pink;
            color: black;
        }

        .a7 {
            background: lightcoral;
            color: black;
        }

        .a8 {
            background: gray;
            color: black;

        }

        .a9 {
            background: beige;
            color: black;
        }


    </style>
</head>
<body>

<div class="head">
    <p>后台管理</p>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-3">

            <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
                <div class="panel panel-default">
                    <div class="panel-heading" role="tab" id="headingOne">
                        <h4 class="panel-title">
                            <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne"
                               aria-expanded="true" aria-controls="collapseOne">
                                操作
                            </a>
                        </h4>
                    </div>
                    <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
                         aria-labelledby="headingOne">
                        <div class="panel-body">
                            <a href="">添加文章</a>
                        </div>
                    </div>
                    <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
                         aria-labelledby="headingOne">
                        <div class="panel-body">
                            <a href="">添加随笔</a>
                        </div>
                    </div>
                    <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
                         aria-labelledby="headingOne">
                        <div class="panel-body">
                            <a href="">其他操作</a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="col-md-9">

            <div>

                <!-- Nav tabs -->
                <ul class="nav nav-tabs" role="tablist">
                    <li role="presentation" class="active"><a class="a1" href="#article" aria-controls="home" role="tab"
                                                              data-toggle="tab">文章</a></li>
                    <li role="presentation" class="nav2"><a class="a2" href="#note" aria-controls="profile"
                                                            role="tab" data-toggle="tab">随笔</a>
                    </li>
                    <li role="presentation" class="nav3"><a class="a3" href="#log" aria-controls="messages"
                                                            role="tab" data-toggle="tab">日志</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a4" href="#comment" aria-controls="settings"
                                                            role="tab" data-toggle="tab">评论</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a5" href="#linked" aria-controls="settings"
                                                            role="tab" data-toggle="tab">链接</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a6" href="#photo" aria-controls="settings"
                                                            role="tab" data-toggle="tab">相册</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a7" href="#file" aria-controls="settings"
                                                            role="tab" data-toggle="tab">文件</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a8" href="#settings" aria-controls="settings"
                                                            role="tab" data-toggle="tab">设置</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a9" href="#options" aria-controls="settings"
                                                            role="tab" data-toggle="tab">选项</a>
                    </li>
                </ul>

                <div class="tab-content">
                    <div role="tabpanel" class="tab-pane active" id="article">
                        <table class="table table-hover table-striped">
                            <thead>
                            <tr>
                                <th>文章标题</th>
                                <th>发布时间</th>
                                <th>评论数</th>
                                <th>点赞数</th>
                                <th>修改</th>
                                <th>删除</th>
                            </tr>
                            </thead>
                            <tbody>

                            {% for article in article_list %}
                                <tr>
                                    <td>
                                        <a href="/{{ article.blog.userinfo.username }}/article/{{ article.pk }}">{{ article.title }}</a>
                                    </td>
                                    <td>{{ article.create_time|date:'Y-m-d H:i:s' }}</td>
                                    <td>{{ article.commit_num }}</td>
                                    <td>{{ article.up_num }}</td>
                                    <td><a href="">修改</a></td>
                                    <td><a href="">删除</a></td>
                                </tr>
                            {% endfor %}

                            </tbody>
                        </table>

                    </div>
                    <div role="tabpanel" class="tab-pane" id="note">随笔页面</div>
                    <div role="tabpanel" class="tab-pane" id="log">日志页面</div>
                    <div role="tabpanel" class="tab-pane" id="comment">评论页面</div>
                    <div role="tabpanel" class="tab-pane" id="linked">链接页面</div>
                    <div role="tabpanel" class="tab-pane" id="photo">相册页面</div>
                    <div role="tabpanel" class="tab-pane" id="file">文件页面</div>
                    <div role="tabpanel" class="tab-pane" id="settings">设置页面</div>
                    <div role="tabpanel" class="tab-pane" id="options">选项页面</div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

写成模板

back/backbase.html

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <link rel="stylesheet" href="/static/css/mycss.css">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <title>后台管理</title>
    <style>
        .head {
            height: 60px;
            background: aquamarine;
            margin-bottom: 20px;
        }

        .a1 {
            background: red;
            color: black;
        }

        .a2 {
            background: orange;
            color: black;
        }

        .a3 {
            background: yellow;
            color: black;
        }

        .a4 {
            background: lawngreen;
            color: black;
        }

        .a5 {
            background: #2aabd2;
            color: black;
        }

        .a6 {
            background: pink;
            color: black;
        }

        .a7 {
            background: lightcoral;
            color: black;
        }

        .a8 {
            background: gray;
            color: black;

        }

        .a9 {
            background: beige;
            color: black;
        }


    </style>
</head>
<body>

<div class="head">
    <p>后台管理</p>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-3">

            <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
                <div class="panel panel-default">
                    <div class="panel-heading" role="tab" id="headingOne">
                        <h4 class="panel-title">
                            <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne"
                               aria-expanded="true" aria-controls="collapseOne">
                                操作
                            </a>
                        </h4>
                    </div>
                    <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
                         aria-labelledby="headingOne">
                        <div class="panel-body">
                            <a href="">添加文章</a>
                        </div>
                    </div>
                    <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
                         aria-labelledby="headingOne">
                        <div class="panel-body">
                            <a href="">添加随笔</a>
                        </div>
                    </div>
                    <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
                         aria-labelledby="headingOne">
                        <div class="panel-body">
                            <a href="">其他操作</a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="col-md-9">

            <div>

                <!-- Nav tabs -->
                <ul class="nav nav-tabs" role="tablist">
                    <li role="presentation" class="active"><a class="a1" href="#article" aria-controls="home" role="tab"
                                                              data-toggle="tab">文章</a></li>
                    <li role="presentation" class="nav2"><a class="a2" href="#note" aria-controls="profile"
                                                            role="tab" data-toggle="tab">随笔</a>
                    </li>
                    <li role="presentation" class="nav3"><a class="a3" href="#log" aria-controls="messages"
                                                            role="tab" data-toggle="tab">日志</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a4" href="#comment" aria-controls="settings"
                                                            role="tab" data-toggle="tab">评论</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a5" href="#linked" aria-controls="settings"
                                                            role="tab" data-toggle="tab">链接</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a6" href="#photo" aria-controls="settings"
                                                            role="tab" data-toggle="tab">相册</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a7" href="#file" aria-controls="settings"
                                                            role="tab" data-toggle="tab">文件</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a8" href="#settings" aria-controls="settings"
                                                            role="tab" data-toggle="tab">设置</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a9" href="#options" aria-controls="settings"
                                                            role="tab" data-toggle="tab">选项</a>
                    </li>
                </ul>

                <div class="tab-content">
                    <div role="tabpanel" class="tab-pane active" id="article">
                        {% block home %}
                        {% endblock %}

                    </div>
                    <div role="tabpanel" class="tab-pane" id="note">随笔页面</div>
                    <div role="tabpanel" class="tab-pane" id="log">日志页面</div>
                    <div role="tabpanel" class="tab-pane" id="comment">评论页面</div>
                    <div role="tabpanel" class="tab-pane" id="linked">链接页面</div>
                    <div role="tabpanel" class="tab-pane" id="photo">相册页面</div>
                    <div role="tabpanel" class="tab-pane" id="file">文件页面</div>
                    <div role="tabpanel" class="tab-pane" id="settings">设置页面</div>
                    <div role="tabpanel" class="tab-pane" id="options">选项页面</div>
                </div>

            </div>


        </div>
    </div>
</div>
</body>
</html>

back/backend.html

代码语言:javascript
复制
{% extends 'back/backbase.html' %}
{% block home %}
              <table class="table table-hover table-striped">
                            <thead>
                            <tr>
                                <th>文章标题</th>
                                <th>发布时间</th>
                                <th>评论数</th>
                                <th>点赞数</th>
                                <th>修改</th>
                                <th>删除</th>
                            </tr>
                            </thead>
                            <tbody>

                            {% for article in article_list %}
                                <tr>
                                    <td>
                                        <a href="/{{ article.blog.userinfo.username }}/article/{{ article.pk }}">{{ article.title }}</a>
                                    </td>
                                    <td>{{ article.create_time|date:'Y-m-d H:i:s' }}</td>
                                    <td>{{ article.commit_num }}</td>
                                    <td>{{ article.up_num }}</td>
                                    <td><a href="">修改</a></td>
                                    <td><a href="">删除</a></td>
                                </tr>
                            {% endfor %}

                            </tbody>
                        </table>
{% endblock %}

添加文章


视图

代码语言:javascript
复制
@login_required(login_url='/login/')
def add_article(request):
    if request.method == 'GET':

        return render(request,'back/add_article.html')

模板

back/backbase.html

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <link rel="stylesheet" href="/static/css/mycss.css">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
    <title>后台管理</title>
    <style>
        .head {
            height: 60px;
            background: aquamarine;
            margin-bottom: 20px;
        }

        .a1 {
            background: red;
            color: black;
        }

        .a2 {
            background: orange;
            color: black;
        }

        .a3 {
            background: yellow;
            color: black;
        }

        .a4 {
            background: lawngreen;
            color: black;
        }

        .a5 {
            background: #2aabd2;
            color: black;
        }

        .a6 {
            background: pink;
            color: black;
        }

        .a7 {
            background: lightcoral;
            color: black;
        }

        .a8 {
            background: gray;
            color: black;

        }

        .a9 {
            background: beige;
            color: black;
        }


    </style>
</head>
<body>

<div class="head">
    <p>后台管理</p>
</div>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-3">

            <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
                <div class="panel panel-default">
                    <div class="panel-heading" role="tab" id="headingOne">
                        <h4 class="panel-title">
                            <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne"
                               aria-expanded="true" aria-controls="collapseOne">
                                操作
                            </a>
                        </h4>
                    </div>
                    <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
                         aria-labelledby="headingOne">
                        <div class="panel-body">
                            <a href="/add_article/">添加文章</a>
                        </div>
                    </div>
                    <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
                         aria-labelledby="headingOne">
                        <div class="panel-body">
                            <a href="">添加随笔</a>
                        </div>
                    </div>
                    <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
                         aria-labelledby="headingOne">
                        <div class="panel-body">
                            <a href="">其他操作</a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="col-md-9">

            <div>

                <!-- Nav tabs -->
                <ul class="nav nav-tabs" role="tablist">
                    <li role="presentation" class="active"><a class="a1" href="#article" aria-controls="home" role="tab"
                                                              data-toggle="tab">文章</a></li>
                    <li role="presentation" class="nav2"><a class="a2" href="#note" aria-controls="profile"
                                                            role="tab" data-toggle="tab">随笔</a>
                    </li>
                    <li role="presentation" class="nav3"><a class="a3" href="#log" aria-controls="messages"
                                                            role="tab" data-toggle="tab">日志</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a4" href="#comment" aria-controls="settings"
                                                            role="tab" data-toggle="tab">评论</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a5" href="#linked" aria-controls="settings"
                                                            role="tab" data-toggle="tab">链接</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a6" href="#photo" aria-controls="settings"
                                                            role="tab" data-toggle="tab">相册</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a7" href="#file" aria-controls="settings"
                                                            role="tab" data-toggle="tab">文件</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a8" href="#settings" aria-controls="settings"
                                                            role="tab" data-toggle="tab">设置</a>
                    </li>
                    <li role="presentation" class="nav4"><a class="a9" href="#options" aria-controls="settings"
                                                            role="tab" data-toggle="tab">选项</a>
                    </li>
                </ul>

                <div class="tab-content">
                    <div role="tabpanel" class="tab-pane active" id="article">
                        {% block home %}
                        {% endblock %}

                    </div>
                    <div role="tabpanel" class="tab-pane" id="note">随笔页面</div>
                    <div role="tabpanel" class="tab-pane" id="log">日志页面</div>
                    <div role="tabpanel" class="tab-pane" id="comment">评论页面</div>
                    <div role="tabpanel" class="tab-pane" id="linked">链接页面</div>
                    <div role="tabpanel" class="tab-pane" id="photo">相册页面</div>
                    <div role="tabpanel" class="tab-pane" id="file">文件页面</div>
                    <div role="tabpanel" class="tab-pane" id="settings">设置页面</div>
                    <div role="tabpanel" class="tab-pane" id="options">选项页面</div>
                </div>

            </div>


        </div>
    </div>
</div>
</body>
</html>
代码语言:javascript
复制
{% extends 'back/backbase.html' %}

{% block home %}
<div>
<p>添加文章</p>
    <form action="/add_article/" method="post">
        <div>
            <p>标题</p>
            <p><input type="text" name="title" class="form-control"></p>
            <p>内容(KindEditor编辑器,不支持拖放/粘贴上传图片)</p>
            <p>
                <textarea name="" id="" cols="30" rows="10"></textarea>
            </p>
            <input type="submit" class="btn btn-danger" value="发表">
        </div>
    </form>
</div>
{% endblock %}

路由

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
    ## 个人站点分类过路由
    # url(r'^(?P<username>[\w]+)/category/(?P<category_id>\d+)', views.user_blog),
    ## 个人站点标签过滤路由
    # url(r'^(?P<username>[\w]+)/tag/(?P<tag>\d+)', views.user_blog),

    # 点赞的路由
    url(r'^diggit/$', views.diggit),
    # p评论的路由
    url(r'^commit_content/$', views.commit_content),
    url(r'^backend/', views.backend),
    url(r'^add_article/', views.add_article),

    ## 路由三合一(分类,标签,随笔)
    # 分组分出三个,(用户名,category|tag|archive中的一个,可能是分类id  tagid或者时间)
    url(r'^(?P<username>[\w]+)/(?P<condition>category|tag|archive)/(?P<param>.*)', views.user_blog),
    url(r'^(?P<username>[\w]+)/article/(?P<id>\d+)', views.article_detail),

    # 放到最后,都匹配完成,没有匹配到,再匹配它
    url(r'^(?P<username>[\w]+)/$', views.user_blog),
]

富文本编辑器


介绍

KindEditor官网:TP

KindEditor下载:TP

KindEditor使用:TP

下载下来是一个zip包,解压开是一个目录,直接放在static目录下


使用

代码语言:javascript
复制
{% extends 'back/backbase.html' %}

{% block home %}
    <div>
        <p>添加文章</p>
        <form action="/add_article/" method="post">
            <div>
                <p>标题</p>
                <p><input type="text" name="title" class="form-control"></p>
                <p>内容(KindEditor编辑器,不支持拖放/粘贴上传图片)</p>
                <p>
                    <textarea id="editor_id" name="content" style="width:700px;height:300px;"></textarea>
                </p>
                <input type="submit" class="btn btn-danger" value="发表">
            </div>
        </form>
    </div>
    <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
    <script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            window.editor = K.create('#editor_id', {
                width: '100%',
                height: '600px',
                resizeType: 0
            });
        });
    </script>
{% endblock %}


提交数据

代码语言:javascript
复制
{% extends 'back/backbase.html' %}

{% block home %}
    <div>
        <p>添加文章</p>
        <form action="/add_article/" method="post">
            {% csrf_token %}
            <div>
                <p>标题</p>
                <p><input type="text" name="title" class="form-control"></p>
                <p>内容(KindEditor编辑器,不支持拖放/粘贴上传图片)</p>
                <p>
                    <textarea id="editor_id" name="content" style="width:700px;height:300px;"></textarea>
                </p>
                <input type="submit" class="btn btn-danger" value="发表">
            </div>
        </form>
    </div>
    <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
    <script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            window.editor = K.create('#editor_id', {
                width: '100%',
                height: '600px',
                resizeType: 0
            });
        });
    </script>
{% endblock %}
代码语言:javascript
复制
@login_required(login_url='/login/')
def add_article(request):
    if request.method == 'GET':
        return render(request, 'back/add_article.html')
    elif request.method == 'POST':
        title = request.POST.get('title')
        content = request.POST.get('content')
        desc = content[0:150]
        blog =  request.user.blog
        models.Article.objects.create(title=title, desc=desc, content=content,blog=blog)
        return redirect('/backend/')


上传图片

上传文件教程:TP

代码语言:javascript
复制
{% extends 'back/backbase.html' %}

{% block home %}
    <div>
        <p>添加文章</p>
        <form action="/add_article/" method="post">
            {% csrf_token %}
            <div>
                <p>标题</p>
                <p><input type="text" name="title" class="form-control"></p>
                <p>内容(KindEditor编辑器,不支持拖放/粘贴上传图片)</p>
                <p>
                    <textarea id="editor_id" name="content" style="width:700px;height:300px;"></textarea>
                </p>
                <input type="submit" class="btn btn-danger" value="发表">
            </div>
        </form>
    </div>
    <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
    <script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            window.editor = K.create('#editor_id', {
                width: '100%',
                height: '600px',
                resizeType: 0,
                uploadJson: '/upload_img/', // 需要添加一个路由
                extraFileUploadParams:{
                    'csrfmiddlewaretoken':'{{ csrf_token }}'
                },
                filePostName:'myfile',
            });
        });
    </script>
{% endblock %}
代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO
from django.contrib import auth
from django.http import JsonResponse
from blog import myforms
from blog import models
from django.db.models import Count
from django.db.models.functions import TruncMonth
from django.contrib.auth.decorators import login_required
import json
from django.db import transaction
from django.db.models import F
import os
from bbs import settings

def upload_img(request):
    myfile = request.FILES.get('myfile')
    path = os.path.join(settings.BASE_DIR, 'media', 'img')
    if not os.path.isdir(path):
        os.mkdir(path)
    file_path = os.path.join(path, myfile.name)
    with open(file_path, 'wb', ) as f:
        for line in myfile:
            f.write(line)

    dic = {'error': 0, 'url': '/media/img/%s' % myfile.name}
    return JsonResponse(dic)
代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
    ## 个人站点分类过路由
    # url(r'^(?P<username>[\w]+)/category/(?P<category_id>\d+)', views.user_blog),
    ## 个人站点标签过滤路由
    # url(r'^(?P<username>[\w]+)/tag/(?P<tag>\d+)', views.user_blog),

    # 点赞的路由
    url(r'^diggit/$', views.diggit),
    # p评论的路由
    url(r'^commit_content/$', views.commit_content),
    url(r'^backend/', views.backend),
    url(r'^add_article/', views.add_article),
    # 富文本编辑器上床图片
    url(r'^upload_img/', views.upload_img),

    ## 路由三合一(分类,标签,随笔)
    # 分组分出三个,(用户名,category|tag|archive中的一个,可能是分类id  tagid或者时间)
    url(r'^(?P<username>[\w]+)/(?P<condition>category|tag|archive)/(?P<param>.*)', views.user_blog),
    url(r'^(?P<username>[\w]+)/article/(?P<id>\d+)', views.article_detail),

    # 放到最后,都匹配完成,没有匹配到,再匹配它
    url(r'^(?P<username>[\w]+)/$', views.user_blog),
]

文章描述截取文字


使用BeautifulSoup4

简称BS4,专门解析html网页的,爬虫会用到

代码语言:javascript
复制
MacBook-pro:auth_module driverzeng$ pip3 install BeautifulSoup4  -i https://mirrors.aliyun.com/pypi/simple/
代码语言:javascript
复制
## 导入模块
from bs4 import BeautifulSoup

视图层

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO
from django.contrib import auth
from django.http import JsonResponse
from blog import myforms
from blog import models
from django.db.models import Count
from django.db.models.functions import TruncMonth
from django.contrib.auth.decorators import login_required
import json
from django.db import transaction
from django.db.models import F
import os
from bbs import settings
from bs4 import BeautifulSoup

@login_required(login_url='/login/')
def add_article(request):
    if request.method == 'GET':
        return render(request, 'back/add_article.html')
    elif request.method == 'POST':
        title = request.POST.get('title')
        content = request.POST.get('content')
        ## 第一个参数,被解析对象,第二个参数,选择解析器
        soup = BeautifulSoup(content,'html.parser')
        ## 取出html标签中的文本内容
        ## soup.text
        desc = soup.text[0:150]
        blog = request.user.blog
        models.Article.objects.create(title=title, desc=desc, content=content, blog=blog)
        return redirect('/backend/')

我们点击文章的源码,然后在文章源码中,写js代码alert(xxx),因为html的页面会被soup解析。所以这个语法也会被解析。那网站....

点开文章,先解析了js语法,弹窗,然后再出来文章页面

处理XSS攻击


使用bs4处理

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO
from django.contrib import auth
from django.http import JsonResponse
from blog import myforms
from blog import models
from django.db.models import Count
from django.db.models.functions import TruncMonth
from django.contrib.auth.decorators import login_required
import json
from django.db import transaction
from django.db.models import F
import os
from bbs import settings
from bs4 import BeautifulSoup

@login_required(login_url='/login/')
def add_article(request):
    if request.method == 'GET':
        return render(request, 'back/add_article.html')
    elif request.method == 'POST':
        title = request.POST.get('title')
        content = request.POST.get('content')
        ## 第一个参数,被解析对象,第二个参数,选择解析器
        soup = BeautifulSoup(content,'html.parser')

        ## 查询出所有的标签
        tags = soup.find_all()
        ## 循环标签,找到script就删除
        for tag in tags:
            if tag.name == 'script':
                ## 删除标签
                tag.decompose()

        ## 取出html标签中的文本内容
        ## soup.text
        desc = soup.text[0:150]
        blog = request.user.blog
        models.Article.objects.create(title=title, desc=desc, content=content, blog=blog)
        return redirect('/backend/')

Django发送邮件


导入模块

代码语言:javascript
复制
from django.core.mail import send_mail

Django 发邮件

需要在settings中设置一下内容

代码语言:javascript
复制
# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.qq.com'
EMAIL_PORT = 465
EMAIL_HOST_USER = '253097001@qq.com'  # 帐号
EMAIL_HOST_PASSWORD = '***'  # 密码
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
#这样收到的邮件,收件人处就会这样显示
#DEFAULT_FROM_EMAIL = 'zls<'253097001@qq.com>'
EMAIL_USE_SSL = True   #使用ssl
#EMAIL_USE_TLS = False # 使用tls

#EMAIL_USE_SSL 和 EMAIL_USE_TLS 是互斥的,即只能有一个为 True


评论发邮件

代码语言:javascript
复制
def commit_content(request):
    response = {'status': 100, 'msg': None}
    if request.is_ajax():
        if request.user.is_authenticated():
            # 核心逻辑
            user = request.user
            article_id = request.POST.get('article_id')
            content = request.POST.get('content')
            pid = request.POST.get('pid')
            print(pid)
            with transaction.atomic():
                ret = models.Commit.objects.create(user=user, article_id=article_id, content=content, parent_id_id=pid)
                models.Article.objects.filter(pk=article_id).update(commit_num=F('commit_num') + 1)

            response['msg'] = '评论成功'
            response['content'] = ret.content
            # 把datetime类型转成字符串,因为json是无法序列化datetime
            response['time'] = ret.create_time.strftime('%Y-%m-%d %X')
            response['user_name'] = ret.user.username
            if pid:
                # 如果是字评论,返回父评论的名字
                response['parent_name'] = ret.parent.user.username
                ## 评论成功,发送邮件
                '''
                subject:邮件标题
                message:邮件内容
                from_email:邮件发送者
                recipient_list:接收者列表
                '''
            from bbs import settings
            ## 拿到文章标题
            article_name = ret.article.title

            ## 被当前登录人评论
            user_name = request.user.username

            ## 有返回值,邮件发送成功是true
            res = send_mail('您的《%s》文章被[%s]评论了' % (article_name, user_name), '这个人评论内容:%s' % content,
                            settings.EMAIL_HOST_USER, ['133411023@qq.com'])
            print(res)

        else:
            response['status'] = 101
            response['msg'] = '您没有登录'
    else:
        response['status'] = 101
        response['msg'] = '您请求非法'
    return JsonResponse(response)


多线程

发邮件是一个同步的操作 ,所以... 我们要开启多线程操作

代码语言:javascript
复制
from django.shortcuts import render, HttpResponse, redirect
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO
from django.contrib import auth
from django.http import JsonResponse
from blog import myforms
from blog import models
from django.db.models import Count
from django.db.models.functions import TruncMonth
from django.contrib.auth.decorators import login_required
import json
from django.db import transaction
from django.db.models import F
import os
from bbs import settings
from bs4 import BeautifulSoup
from django.core.mail import send_mail

def commit_content(request):
    response = {'status': 100, 'msg': None}
    if request.is_ajax():
        if request.user.is_authenticated():
            # 核心逻辑
            user = request.user
            article_id = request.POST.get('article_id')
            content = request.POST.get('content')
            pid = request.POST.get('pid')
            print(pid)
            with transaction.atomic():
                ret = models.Commit.objects.create(user=user, article_id=article_id, content=content, parent_id_id=pid)
                models.Article.objects.filter(pk=article_id).update(commit_num=F('commit_num') + 1)

            response['msg'] = '评论成功'
            response['content'] = ret.content
            # 把datetime类型转成字符串,因为json是无法序列化datetime
            response['time'] = ret.create_time.strftime('%Y-%m-%d %X')
            response['user_name'] = ret.user.username
            if pid:
                # 如果是字评论,返回父评论的名字
                response['parent_name'] = ret.parent.user.username
                ## 评论成功,发送邮件
                '''
                subject:邮件标题
                message:邮件内容
                from_email:邮件发送者
                recipient_list:接收者列表
                '''
            from bbs import settings
            ## 拿到文章标题
            # article_name = ret.article.title
            #
            # ## 被当前登录人评论
            # user_name = request.user.username
            #
            # ## 有返回值,邮件发送成功是true
            # res = send_mail('您的《%s》文章被[%s]评论了' % (article_name, user_name), '这个人评论内容:%s' % content,
            #                 settings.EMAIL_HOST_USER, ['133411023@qq.com'])
            # print(res)
            from threading import Thread
            article_name = ret.article.title
            user_name = request.user.username
            ## 实例化
            t1 = Thread(target=send_mail, args=('您的《%s》文章被[%s]评论了' % (article_name, user_name), '这个人评论内容:%s' % content,
                                           settings.EMAIL_HOST_USER, ['133411023@qq.com']))
            t1.start()

        else:
            response['status'] = 101
            response['msg'] = '您没有登录'
    else:
        response['status'] = 101
        response['msg'] = '您请求非法'
    return JsonResponse(response)

修改头像


模板层

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <title>修改头像</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <input type="file" name="head">
    <input type="submit" value="提交">
</form>
</body>
</html>

路由

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
    ## 个人站点分类过路由
    # url(r'^(?P<username>[\w]+)/category/(?P<category_id>\d+)', views.user_blog),
    ## 个人站点标签过滤路由
    # url(r'^(?P<username>[\w]+)/tag/(?P<tag>\d+)', views.user_blog),

    # 点赞的路由
    url(r'^diggit/$', views.diggit),
    # p评论的路由
    url(r'^commit_content/$', views.commit_content),
    url(r'^backend/', views.backend),
    url(r'^add_article/', views.add_article),
    # 富文本编辑器上床图片
    url(r'^upload_img/', views.upload_img),
    ## 修改头像
    url(r'^update_head/', views.update_head),

    ## 路由三合一(分类,标签,随笔)
    # 分组分出三个,(用户名,category|tag|archive中的一个,可能是分类id  tagid或者时间)
    url(r'^(?P<username>[\w]+)/(?P<condition>category|tag|archive)/(?P<param>.*)', views.user_blog),
    url(r'^(?P<username>[\w]+)/article/(?P<id>\d+)', views.article_detail),

    # 放到最后,都匹配完成,没有匹配到,再匹配它
    url(r'^(?P<username>[\w]+)/$', views.user_blog),
]

视图

代码语言:javascript
复制
@login_required
def update_head(request):
    if request.method == 'GET':
        return render(request,'update_head.html')
    else:
        myfile = request.FILES.get('head')
        ## 可以只删除数据库的地址,不删实际文件
        user = request.user
        user.avatar=myfile
        user.save()
        ret = #models.UserInfo.objects.filter(pk=request.user.pk).update(avatar=myfile)
        return redirect('/index/')

文章编辑


路由层

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
    ## 个人站点分类过路由
    # url(r'^(?P<username>[\w]+)/category/(?P<category_id>\d+)', views.user_blog),
    ## 个人站点标签过滤路由
    # url(r'^(?P<username>[\w]+)/tag/(?P<tag>\d+)', views.user_blog),

    # 点赞的路由
    url(r'^diggit/$', views.diggit),
    # p评论的路由
    url(r'^commit_content/$', views.commit_content),
    url(r'^backend/', views.backend),
    url(r'^add_article/', views.add_article),
    # 富文本编辑器上床图片
    url(r'^upload_img/', views.upload_img),
    ## 修改头像
    url(r'^update_head/', views.update_head),

    ##修改文章
    url(r'^update_article/(?P<pk>\d+)', views.update_article),

    ## 路由三合一(分类,标签,随笔)
    # 分组分出三个,(用户名,category|tag|archive中的一个,可能是分类id  tagid或者时间)
    url(r'^(?P<username>[\w]+)/(?P<condition>category|tag|archive)/(?P<param>.*)', views.user_blog),
    url(r'^(?P<username>[\w]+)/article/(?P<id>\d+)', views.article_detail),

    # 放到最后,都匹配完成,没有匹配到,再匹配它
    url(r'^(?P<username>[\w]+)/$', views.user_blog),
]

视图层

代码语言:javascript
复制
def update_article(request,pk):
    if request.method == 'GET':
        article = models.Article.objects.get(pk=pk)
        return render(request,'back/update_article.html',{'article':article})

模板层

backend.html

代码语言:javascript
复制
{% extends 'back/backbase.html' %}
{% block home %}
              <table class="table table-hover table-striped">
                            <thead>
                            <tr>
                                <th>文章标题</th>
                                <th>发布时间</th>
                                <th>评论数</th>
                                <th>点赞数</th>
                                <th>修改</th>
                                <th>删除</th>
                            </tr>
                            </thead>
                            <tbody>

                            {% for article in article_list %}
                                <tr>
                                    <td>
                                        <a href="/{{ article.blog.userinfo.username }}/article/{{ article.pk }}">{{ article.title }}</a>
                                    </td>
                                    <td>{{ article.create_time|date:'Y-m-d H:i:s' }}</td>
                                    <td>{{ article.commit_num }}</td>
                                    <td>{{ article.up_num }}</td>
                                    <td><a href="/update_article/{{ article.pk }}">修改</a></td>
                                    <td><a href="">删除</a></td>
                                </tr>
                            {% endfor %}

                            </tbody>
                        </table>
{% endblock %}

back/update_article.html

代码语言:javascript
复制
{% extends 'back/backbase.html' %}

{% block home %}
    <div>
        <p>修改文章</p>
        <form action="/add_article/" method="post">
            {% csrf_token %}
            <div>
                <p>标题</p>
                <p><input type="text" name="title" class="form-control" value="{{ article.title }}"></p>
                <p>内容(KindEditor编辑器,不支持拖放/粘贴上传图片)</p>
                <p>
                    <textarea id="editor_id" name="content" rows="10" value="{{ article.content|safe }}"></textarea>
                </p>
                <input type="submit" class="btn btn-danger" value="发表">
            </div>
        </form>
    </div>
    <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
    <script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            window.editor = K.create('#editor_id', {
                width: '100%',
                height: '600px',
                resizeType: 0,
                uploadJson: '/upload_img/', // 需要添加一个路由
                extraFileUploadParams:{
                    'csrfmiddlewaretoken':'{{ csrf_token }}'
                },
                filePostName:'myfile',
            });
        });
    </script>
{% endblock %}


AJAX方法渲染页面

back/update_article.html

代码语言:javascript
复制
{% extends 'back/back_base.html' %}

{% block home %}


    <div>
        <p>修改文章</p>
        <form action="/add_article/" method="post">
            {% csrf_token %}

            <p>标题</p>
            <p><input type="text" name="title" class="form-control" id="title" article_id="{{ article_id }}"></p>
            <p>内容(KindEdit编辑器,不支持拖放/粘贴上传图片)</p>
            <p>
             <textarea name="content" id="editor_id" cols="30" rows="10">


                </textarea>

            </p>
            <input type="submit" class="btn btn-danger" value="提交">


        </form>
    </div>



    <script charset="utf-8" src="/static/kindeditor/kindeditor-all.js"></script>
    <script>

        KindEditor.ready(function (K) {
            window.editor = K.create('#editor_id', {
                width: '100%',
                height: '500px',
                //item 控制要显示的控件
                //控制控件不能拖动
                resizeType: 0,
                //上传图片,uploadJson 指的是上传的路径,也就是咱们的路由
                uploadJson: '/upload_img/',
                //添加一些额外的参数
                extraFileUploadParams: {
                    'csrfmiddlewaretoken': '{{ csrf_token }}',
                    'article_id': '1'
                },
                //修改默认上传文件的名字
                filePostName: 'myfile'

            })

        });

        //当页面加载完成以后,发ajax请求,拿回文章数据

        //jquery 的页面加载完成

        $(function () {
            var id = $("#title").attr('article_id')
            $.ajax({
                url: '/get_article/' + '{{ article_id }}',
                type: 'get',
                success: function (data) {
                    console.log(data)
                    $("#title").val(data.title)
                    // 设置HTML内容
                    window.editor.html(data.content);

                }

            })
        })
        /*
        window.onload = function () {
            //拿到我隐藏的id
            var id = $("#title").attr('article_id')
            $.ajax({
                url: '/get_article/' + '{{ article_id }}',
                type: 'get',
                success: function (data) {
                    console.log(data)
                    $("#title").val(data.title)
                    // 设置HTML内容
                    window.editor.html(data.content);

                }

            })

        }
        */
    </script>
{% endblock %}

views.py

代码语言:javascript
复制
def update_article(request,pk):
    if request.method=='GET':
        return render(request,'back/update_article.html',{'article_id':pk})

def get_article(request,pk):
    article=models.Article.objects.get(pk=pk)

    return JsonResponse({'title':article.title,'content':article.content})

urls.py

代码语言:javascript
复制
from django.conf.urls import url
from django.contrib import admin
from blog import views
from django.views.static import serve
from bbs import settings
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^get_valid_code/$', views.get_valid_code),
    url(r'^register/$', views.register),
    url(r'^check_username/$', views.check_username),
    url(r'^index/$', views.index),
    url(r'^logout/$', views.logout),
    ##  有名分组
    # 1.url第一个参数,正则表达式
    # 2.第二个参数,函数的内存地址
    # 3.第三个参数,字典,它会以关键字参数的形式传到(第二个参数)
    # 4.当从浏览器输入media/后面的路径会去settings.MEDIA_ROOT这个变量对应的文件夹下面去找对应的图片
    url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
    ## 个人站点分类过路由
    # url(r'^(?P<username>[\w]+)/category/(?P<category_id>\d+)', views.user_blog),
    ## 个人站点标签过滤路由
    # url(r'^(?P<username>[\w]+)/tag/(?P<tag>\d+)', views.user_blog),

    # 点赞的路由
    url(r'^diggit/$', views.diggit),
    # p评论的路由
    url(r'^commit_content/$', views.commit_content),
    url(r'^backend/', views.backend),
    url(r'^add_article/', views.add_article),
    # 富文本编辑器上床图片
    url(r'^upload_img/', views.upload_img),
    ## 修改头像
    url(r'^update_head/', views.update_head),

    # 修改文章
    url(r'^update_article/(?P<pk>\d+)', views.update_article),
    # ajax获取文件的口
    url(r'^get_article/(?P<pk>\d+)', views.get_article),

    ## 路由三合一(分类,标签,随笔)
    # 分组分出三个,(用户名,category|tag|archive中的一个,可能是分类id  tagid或者时间)
    url(r'^(?P<username>[\w]+)/(?P<condition>category|tag|archive)/(?P<param>.*)', views.user_blog),
    url(r'^(?P<username>[\w]+)/article/(?P<id>\d+)', views.article_detail),

    # 放到最后,都匹配完成,没有匹配到,再匹配它
    url(r'^(?P<username>[\w]+)/$', views.user_blog),
]
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-04-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 需求分析
  • 数据库设计
  • 创建项目
  • 前端登录页面
  • 实现验证码功能
  • 登录功能
  • 注册功能
  • 提交注册信息
  • 渲染错误信息
  • 首页设计
  • 评论点赞处理
  • 显示头像
  • 个人站点
  • 随笔档案
  • 个人站点分类过滤文章
  • 个人站点标签过滤文章(三合一)
  • 修改点击跳转
  • 文章详情页
  • 文章点赞功能
  • 后台管理功能
    • 查询所有文章
      • 添加文章
        • 富文本编辑器
          • 文章描述截取文字
            • 处理XSS攻击
            • Django发送邮件
              • 修改头像
                • 文章编辑
                相关产品与服务
                验证码
                腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档