dya72-day75.BBS项目开发流程

目录

day72

Django版本:django4.x或者1.x(区别在于url和models中的外键字段)

数据库:mysql

以下是利用终端开发项目的流程,使用django则更加简单

1.创建项目+创建&注册app(提前安装好django,Navicat等软件)

 1.1 windows+r,输入cmd,打开终端,切换到自己要创建项目的目录下

  比如:D盘中,输入以下命令,进入D盘中

D:
cd D:\

1.2 输入下面的命令,创建BBS项目:

django-admin startproject BBS

1.3 切换到项目下(cd D:\BBS\),输入下面的命令,创建app:

python manage.py startapp app01

1.4 在settings文件中注册app(最后一行)

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',  # 该行为注册的app01
]

1.5 注意:终端命令行创建的django项目中,没有templates文件夹,手动去BBS项目下,创建templates文件夹

1.6 创建好了templates文件夹之后,去配置文件settings.py中配置templates路径

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR,'templates')],  # 该行需要配置,记得导入import os
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

2.创建数据库&配置数据库

2.1 在Navicat中创建数据库(步骤省略)

2.2 在settings文件中配置数据库信息(由于django自带的sqlite3数据库对日期不敏感,所以我们在这个项目中使用mysql数据库)

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'bbs02',
        'USER': 'root',
        'PASSWORD':'123',
        'HOST':'127.0.0.1',
        'PORT':3306,
        'CHARSET':'utf8',
    }
}

2.3 在项目或者应用下的__init__中书写如下代码

import pymysql
pymysql.install_as_MySQLdb()

3.表准备

3.1 在app01下的models.py 中创建表(由于是django 4.X版本,所以外键字段&OneToOneField中需要加上 :on_delete=models.CASCADE,Django 1.X版本则不需要

from django.db import models
from django.contrib.auth.models import AbstractUser

# Create your models here.

"""我们不用django的Auth表,自己创建一张UserInfo表"""
class UserInfo(AbstractUser):
    phone = models.BigIntegerField(verbose_name='手机号',null=True)
    avatar = models.FileField(verbose_name='用户头像',upload_to='avatar/',default='avatar/default.png')
    create_time = models.DateField(verbose_name='创建时间',auto_now_add=True)
    blog = models.OneToOneField(to='Blog',null=True,on_delete=models.CASCADE)
    

class Blog(models.Model):
    site_name = models.CharField(verbose_name='站点名称',max_length=32)
    site_title = models.CharField(verbose_name='站点标题',max_length=32)
    site_theme = models.CharField(verbose_name='站点样式',max_length=64)
    
    

class Category(models.Model):
    name = models.CharField(verbose_name='分类名称',max_length=32)
    blog = models.ForeignKey(to='Blog',null=True,on_delete=models.CASCADE)
    
    

class Tag(models.Model):
    name = models.CharField(verbose_name='标签名称',max_length=32)
    blog = models.ForeignKey(to='Blog',null=True,on_delete=models.CASCADE)
    
    
    
class Article(models.Model):
    title = models.CharField(verbose_name='文章标题',max_length=64)
    desc = models.CharField(verbose_name='文章简介',max_length=255)
    content = models.TextField(verbose_name='文章内容')
    create_time = models.DateField(verbose_name='创建时间',auto_now_add=True)
    
    # 数据库字段优化设计
    up_num = models.BigIntegerField(verbose_name='点赞数',default=0)
    down_num = models.BigIntegerField(verbose_name='点踩数',default=0)
    comment_num = models.BigIntegerField(verbose_name='评论数',default=0)
    
    # 外键字段
    blog = models.ForeignKey(to='Blog',null=True,on_delete=models.CASCADE)
    category = models.ForeignKey(to='Category',null=True,on_delete=models.CASCADE)
    tags = models.ManyToManyField(to='Tag',through='Article2Tag',through_fields=('article','tag'))
    
    
    
class Article2Tag(models.Model):
    article = models.ForeignKey(to='Article',on_delete=models.CASCADE)
    tag = models.ForeignKey(to='Tag',on_delete=models.CASCADE)
    
    
class UpAndDown(models.Model):
    user = models.ForeignKey(to='UserInfo',on_delete=models.CASCADE)
    article = models.ForeignKey(to='Article',on_delete=models.CASCADE)
    is_up = models.BooleanField()
    
    
class Comment(models.Model):
    user = models.ForeignKey(to='UserInfo',on_delete=models.CASCADE)
    article = models.ForeignKey(to='Article',on_delete=models.CASCADE)
    cotent = models.CharField(verbose_name='评论内容',max_length=255)
    comment_time = models.DateTimeField(verbose_name='评论时间',auto_now_add=True)
  # 自关联
    parent = models.ForeignKey(to='self',null=True,on_delete=models.CASCADE) #一定要指定null=True,因为有些评论是根评论
models.py

3.2 由于创建的表中,有我们自己创建的UserInfo表,因此,需要在settings.py 中配置

AUTH_USER_MODEL = 'app01.UserInfo'

3.3 表创建完成,执行数据库迁移命令,创建表

python manage.py makemigrations

python manage.py migrate

4.书写注册功能

4.1 在urls.py中开启路由

path('register/', views.register, name='register'),

4.2 由于要进行用户名和密码的校验等功能,所以我们考虑使用forms组件,在app01下创建一个myforms.py的文件,书写如下代码:

from django import forms
from app01 import models


class MyRegForm(forms.Form):
    username = forms.CharField(
        label='用户名',
        min_length=3,
        max_length=8,
        error_messages={
          'required':'用户名不能为空',
          'min_length':'用户名最少为3位',
          'max_length':'用户名最多为8位',
        },
        widget = forms.widgets.TextInput(attrs={'class':'form-control'})
    )
    password = forms.CharField(
        label='密码',
        min_length=3,
        max_length=8,
        error_messages={
            'required':'密码不能为空',
            'min_length':'密码最少为3位',
            'max_length':'密码最多为8位',
        },
        widget = forms.widgets.PasswordInput(attrs={'class':'form-control'})
    )
    confirm_password = forms.CharField(
        label='确认密码',
        min_length=3,
        max_length=8,
        error_messages={
            'required':'确认密码不能为空',
            'min_length':'确认密码最少为3位',
            'max_length':'确认密码最多为8位',
        },
        widget = forms.widgets.PasswordInput(attrs={'class':'form-control'})
    )
    email = forms.EmailField(
        label='邮箱',
        error_messages={
            'required':'邮箱不能为空',
            'invalid':'邮箱格式不正确'
        },
        widget = forms.widgets.EmailInput(attrs={'class':'form-control'})
    )
    
    # 钩子函数
    def clean_username(self):
        username=self.cleaned_data.get('username')
        is_exist=models.UserInfo.objects.filter(username=username)
        if is_exist:
            self.add_error('username','用户名已存在')
        return username
        
     
    def clean(self):
        password=self.cleaned_data.get('password')
        confirm_password=self.cleaned_data.get('confirm_password')
        if not password == confirm_password:
            self.add_error('confirm_password','两次输入的密码不一致')
        return self.cleaned_data
myforms.py

 

4.3 在views.py中书写后端代码(register部分)

def register(request):
    form_obj = MyRegForm()
    back_dic = {'code':1000,'msg':''}
    if request.method == 'POST':
        form_obj = MyRegForm(request.POST)
        if form_obj.is_valid():
            clean_data = form_obj.cleaned_data
            clean_data.pop('confirm_password')
            file_obj = request.FILES.get('avatar')
            if file_obj:
                clean_data['avatar']=file_obj
            models.UserInfo.objects.create_user(**clean_data)
            back_dic['url']='/login/'  # 为了跳转准备
        else:
            back_dic['code'] = 2000
            back_dic['msg'] = form_obj.errors
        return JsonResponse(back_dic)
    return render(request,'register.html', locals())
views.py

 

4.4 在templates文件夹下创建register.html代码,输写以下代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    {% load static %}
</head>
<body>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <h1 class="text-center">注册页面</h1>
                <form id="myform">
                    {% csrf_token %}
                    {% for form in form_obj %}
                        <div class="form-group">
                            <label for="{{ form.auto_id }}">{{ form.label }}</label>
                            {{form}}
                            <span style="color:red" class="pull-right"></span>
                        </div>
                    {% endfor %}
                    
                    <div class="form-group">
                        <label for="myfile">头像
                            <img src="{% static 'img/default.png' %}" alt="" id="myimg" width="100" style="margin-left:10px">
                        </label>
                        <input type='file' id='myfile' name="avatar" style="display:none">
                    </div>
                    <input type="button" id="id_commit" class="btn btn-primary pull-right" value="注册">
                </form>                
            </div>
        </div>
    </div>
    
    <script>
        // 头像展示相关
        $("#myfile").change(function(){
            // alert(123)
            let myFileReadObj = new FileReader();
            let fileObj = $(this)[0].files[0];
            myFileReadObj.readAsDataURL(fileObj);
            myFileReadObj.onload = function(){
                $("#myimg").attr('src',myFileReadObj.result)
            }
        });
        // ajax提交数据并展示错误信息
        $("#id_commit").click(function(){
            let formDataObj = new FormData();
        // 添加普通键值对
            $.each($("#myform").serializeArray(),function(index,obj){
                formDataObj.append(obj.name,obj.value)
            })
        // 添加文件
            formDataObj.append('avatar',$("#myfile")[0].files[0])
            $.ajax({
                url:'',
                type:'post',
                data:formDataObj,
                contentType:false,
                processData:false,
                success:function(args){
                    if(args.code==1000){
                        window.location.href=args.url
                    }else{
               // 展示错误信息
                        $.each(args.msg,function(index,obj){
                            // console.log(index,obj)
                            let targetId = '#id_' + index
                            $(targetId).next().text(obj[0]).parent().addClass('has-error')
                        })
                    }
                }
            })
        })
        
        $('input').focus(function(){
            $(this).next().text('').parent().removeClass('has-error')
        })
    </script>
</body>
</html>
register.html

 

5.书写登录功能

5.1 在urls.py中开启路由

# 登录功能
path('login/', views.login, name='login'),

# 图片验证码相关
path('get_code/', views.get_code, name='get_code'),

 

5.2 在views.py 中书写后端代码

def login(request):
    return render(request,'login.html')
    
    
# 图片验证码相关
from PIL import Image,ImageDraw,ImageFont
import random
from io import BytesIO, StringIO

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

def get_code(request):
    img_obj = Image.new('RGB',(400,35),get_random())
    img_draw = ImageDraw.Draw(img_obj)
    img_font = ImageFont.truetype('static/font/333.ttf',35)
    
    code = ''
    for i in range(5):
        rand_upper = chr(random.randint(65,90))
        rand_lower = chr(random.randint(97,122))
        rand_int = str(random.randint(0,9))
        tmp = random.choice([rand_upper,rand_lower,rand_int])
        img_draw.text((i*60+60,-5),tmp,get_random(),img_font)
        code += tmp
    request.session['code'] = code
    io_obj = BytesIO()
    img_obj.save(io_obj,'png')
    return HttpResponse(io_obj.getvalue())
view.py(login+get_code)

 

5.3 在templates文件夹下创建login.html代码,输写以下代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    {% load static %}
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <h1 class="text-center">登录页面</h1>
            <div class="form-group">
                <label>用户名</label>
                <input type="text" id="id_username" name="id_username" class="form-control">
            </div>
            <div class="form-group">
                <label>密码</label>
                <input type="password" id="id_password" name="id_password" class="form-control">
            </div>
            <div class="form-group">
                <label>验证码</label>
                <div class="row">
                    <div class="col-md-6">
                        <input type="text" id="id_code" name="id_code" class="form-control">
                    </div>
                    <div class="col-md-6">
                        <img src="/get_code/" alt="" id="id_img" width="400" height="35">
                    </div>
                </div>
                
            </div>
            <input type="button" class="btn btn-success" id="id_commit" value="登录">
        </div>
    </div>
</div>

<script>
    $("#id_img").click(function(){
        let oldVal = $(this).attr('src')
        $(this).attr('src', oldVal += "?")
    })
</script>
</body>
</html>
login.html

 

day73

1.登陆功能完善

1.1 前端发送ajax请求

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    {% load static %}
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <h1 class="text-center">登录页面</h1>
            <div class="form-group">
                <label for="">用户名</label>
                <input type="text" id="id_username" class="form-control">
            </div>
            <div class="form-group">
                <label for="">密码</label>
                <input type="password" id="id_password" class="form-control">
            </div>
            <div class="form-group">
                <label for="">验证码</label>
                <div class="row">
                    <div class="col-md-6">
                        <input type="text" id="id_code" class="form-control">
                    </div>
                    <div class="col-md-6">
                        <img src="/get_code/" alt="" width="400" height="35" id="myimg">
                        <input type="button" value="看不清?换一张" id="id_change_code">
                    </div>
                </div>

            </div>
            <button class="btn btn-success" id="id_button">登陆</button>
            <span style="color:red" id="id_error"></span>
        </div>
    </div>
</div>

<script>
  // 随机验证码点击更换
    $("#id_change_code").click(function () {
        let oldVal = $("#myimg").attr('src')
        $("#myimg").attr('src', oldVal += '?')
    })
  // 后端发送ajax请求的关键代码
    $("#id_button").click(function () {
        $.ajax({
            url:'',
            type:'post',
            data:{
                'username':$("#id_username").val(),
                'password':$("#id_password").val(),
                'code':$("#id_code").val(),
                'csrfmiddlewaretoken':'{{ csrf_token }}'    // 一定不要忘记发送csrfmiddlewaretoken数据
            },
       // 接收后端返回的数据,利用if做判断
            success:function (args) {
                if(args.code==1000){
                    window.location.href=args.url
                }else{
                    $("#id_error").text(args.msg)
                }
            }
        })
    })
</script>
</body>
</html>
login.html

1.2 后端接收前端数据(后端login处理前端传过来的数据)

# 登陆功能
def login(request):
    if request.method == 'POST':
        back_dic = {'code':1000,'msg':''}
        username = request.POST.get('username')
        password = request.POST.get('password')
        code = request.POST.get('code')
        if request.session.get('code').upper() == code.upper():
            user_obj = auth.authenticate(request,username=username,password=password)
            if user_obj:
                auth.login(request,user_obj)
                back_dic['url'] = '/home/'
            else:
                back_dic['code'] = 2000
                back_dic['msg'] = '用户名或者密码不正确'
        else:
            back_dic['code'] = 3000
            back_dic['msg'] = '验证码不正确'
        return JsonResponse(back_dic)
    return render(request, 'login.html')
views.py

2.首页导航条搭建

2.1 开设url

# 首页
    url(r'^home/', views.home, name='home'),

2.2 后端代码书写

# 首页
def home(request):
    return render(request, 'home.html', locals())

2.3 前端页面搭建导航条(bootstrap中拷贝)

判断用户是否登陆,如果登陆,则在导航条右边显示登陆用户的用户名+更多操作,如果没有登陆,导航条右端显示注册+登陆。如下图

未登录状态

 登陆状态

 

<nav class="navbar navbar-inverse">
  <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="#">BBS</a>
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li class="active"><a href="#">博客 <span class="sr-only">(current)</span></a></li>
        <li><a href="#">文章</a></li>
        <li 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="#">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">
    {# 判断用户是否登陆,如果登陆,则在导航条右边显示登陆用户的用户名+更多操作,如果没有登陆,导航条右端显示注册+登陆#}
          {% 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="#" data-toggle="modal" data-target=".bs-example-modal-lg">修改密码</a></li>
            <li><a href="#">修改头像</a></li>
            <li><a href="#">后台管理</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="{% url 'logout' %}">退出登陆</a></li>
          </ul>
        </li>
          {% else %}
              <li><a href="{% url 'register' %}">注册</a></li>
              <li><a href="{% url 'login' %}">登陆</a></li>
          {% endif %}
      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>
home.html

3.导航条的修改密码及退出登陆功能

3.1 开设修改密码的url

# 修改密码
    url(r'^set_password/', views.set_password, name='set_password'),

3.2 后端书写set_password

由于登陆的用户才能修改密码,因此,需要在views.py 中导入auth模块及auth模块的装饰器

关键:request.user.check_password、request.user.set_password、request.user.save()

from django.contrib import auth
from django.contrib.auth.decorators import login_required
# 修改密码
@login_required
def set_password(request):
    if request.is_ajax():
        back_dic = {'code':1000, 'msg':''}
        if request.method == 'POST':
            old_password = request.POST.get('old_password')
            new_password = request.POST.get('new_password')
            confirm_password = request.POST.get('confirm_password')
            if request.user.check_password(old_password):
                if new_password == confirm_password:
                    request.user.set_password(new_password)
                    request.user.save()
                    back_dic['url'] = '/login/'
                    back_dic['msg'] = '密码修改成功'
                else:
                    back_dic['code'] = 1001
                    back_dic['msg'] = '两次密码不一致'
            else:
                back_dic['code'] = 1002
                back_dic['msg'] = '原密码错误'
        return JsonResponse(back_dic)
set_password.py

3.3 前端导航条代码,发送ajax请求

拷贝bootstrap中的模态框,注意以下这一句,拷贝的代码中class里面少了:bs-example-modal-lg

<div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel">
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>

{#导航条#}
<nav class="navbar navbar-inverse">
  <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="#">BBS</a>
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li class="active"><a href="#">博客 <span class="sr-only">(current)</span></a></li>
        <li><a href="#">文章</a></li>
        <li 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="#">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">
          {% 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="#" data-toggle="modal" data-target=".bs-example-modal-lg">修改密码</a></li>
            <li><a href="#">修改头像</a></li>
            <li><a href="#">后台管理</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="{% url 'logout' %}">退出登陆</a></li>
          </ul>
            <!-- Large modal -->
            <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel">
              <div class="modal-dialog modal-lg" role="document">
                <div class="modal-content">
                  <h1 class="text-center">修改密码</h1>
                    <div class="row">
                        <div class="col-md-8 col-md-offset-2">
                            <div class="form-group">
                                <label for="">用户名</label>
                                <input type="text" disabled value="{{ request.user.username }}" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">原密码</label>
                                <input type="password" id="id_old_password" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">新密码</label>
                                <input type="password" id="id_new_password" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">确认密码</label>
                                <input type="password" id="id_confirm_password" class="form-control">
                            </div>
                            <div class="modal-footer">
                                <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
                                <button type="button" class="btn btn-primary" id="id_change_password">修改</button>
                                <span style="color: red" id="id_error"></span>
                            </div>
                        </div>
                    </div>
                </div>
              </div>
            </div>
        </li>
          {% else %}
              <li><a href="{% url 'register' %}">注册</a></li>
              <li><a href="{% url 'login' %}">登陆</a></li>
          {% endif %}
      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>

<script>
    $("#id_change_password").click(function () {
        $.ajax({
            url:'/set_password/',
            type:'post',
            data:{
                'old_password':$("#id_old_password").val(),
                'new_password':$("#id_new_password").val(),
                'confirm_password':$("#id_confirm_password").val(),
                'csrfmiddlewaretoken':'{{ csrf_token }}'
            },
            success:function (args) {
                if(args.code==1000){
                    window.location.reload()
                }else{
                    $("#id_error").text(args.msg)
                }
            }
        })
    })
</script>

</body>
</html>
home.html

3.4 退出登陆功能

url

# 退出登陆
    url(r'^logout/',views.logout, name='logout'),

views.py

# 退出登陆
@login_required
def logout(request):
    auth.logout(request)
    return redirect('/home/')

4.admin后台管理

home页面的导航条搭建好了之后,需要搭建侧边栏,布局采用2-8-2的形式,侧边栏两边展示广告,中间展示文章,这个时候,中间的文章,我们没有数据,因此需要在admin后台管理中去创建数据

4.1 侧边栏展示页搭建

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>

{#导航条#}
<nav class="navbar navbar-inverse">
  <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="#">BBS</a>
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li class="active"><a href="#">博客 <span class="sr-only">(current)</span></a></li>
        <li><a href="#">文章</a></li>
        <li 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="#">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">
          {% 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="#" data-toggle="modal" data-target=".bs-example-modal-lg">修改密码</a></li>
            <li><a href="#">修改头像</a></li>
            <li><a href="#">后台管理</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="{% url 'logout' %}">退出登陆</a></li>
          </ul>
            <!-- Large modal -->
            <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel">
              <div class="modal-dialog modal-lg" role="document">
                <div class="modal-content">
                  <h1 class="text-center">修改密码</h1>
                    <div class="row">
                        <div class="col-md-8 col-md-offset-2">
                            <div class="form-group">
                                <label for="">用户名</label>
                                <input type="text" disabled value="{{ request.user.username }}" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">原密码</label>
                                <input type="password" id="id_old_password" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">新密码</label>
                                <input type="password" id="id_new_password" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">确认密码</label>
                                <input type="password" id="id_confirm_password" class="form-control">
                            </div>
                            <div class="modal-footer">
                                <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
                                <button type="button" class="btn btn-primary" id="id_change_password">修改</button>
                                <span style="color: red" id="id_error"></span>
                            </div>
                        </div>
                    </div>
                </div>
              </div>
            </div>
        </li>
          {% else %}
              <li><a href="{% url 'register' %}">注册</a></li>
              <li><a href="{% url 'login' %}">登陆</a></li>
          {% endif %}
      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>

{#侧边栏及文章部分展示#}
<div class="container-fluid">
    <div class="row">
{#        左侧边栏#}
        <div class="col-md-2">
            <div class="panel panel-danger">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-info">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-warning">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-primary">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-success">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
        </div>
{#        中间#}
        <div class="col-md-8">
            
        </div>
{#        右侧边栏#}
        <div class="col-md-2">
            <div class="panel panel-danger">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-info">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-warning">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-primary">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-success">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
        </div>
    </div>
</div>


<script>
    $("#id_change_password").click(function () {
        $.ajax({
            url:'/set_password/',
            type:'post',
            data:{
                'old_password':$("#id_old_password").val(),
                'new_password':$("#id_new_password").val(),
                'confirm_password':$("#id_confirm_password").val(),
                'csrfmiddlewaretoken':'{{ csrf_token }}'
            },
            success:function (args) {
                if(args.code==1000){
                    window.location.reload()
                }else{
                    $("#id_error").text(args.msg)
                }
            }
        })
    })
</script>

</body>
</html>
home.html

Django框架中的auth模块提供了图形化界面,方便管理和操作数据,创建数据之前要先创建一个超级用户

4.2 创建Django-admin超级用户(在pycharm的terminal里面书写如下代码)

python3 manage.py createsuperuser

4.3 注册所有表

由于django-admin页面登陆之后,需要先注册所有表,才能在admin页面显示出来,因此先去app01下的admin.py中注册所有表

from django.contrib import admin
from app01 import models

# Register your models here.


admin.site.register(models.UserInfo)
admin.site.register(models.Blog)
admin.site.register(models.Category)
admin.site.register(models.Tag)
admin.site.register(models.Article2Tag)
admin.site.register(models.Article)
admin.site.register(models.UpAndDown)
admin.site.register(models.Comment)
admin.py

此时,admin页面中展示了所有表,但是表名都是英文,如果要让表名显示成中文,需要在models.py中的每个表下书写下面的代码

    class Meta:
        verbose_name_plural = '你想要的表的中文名字'

4.4 创建数据

在admin页面中依次创建category,blog,article,tag,user,article2tag中的数据,注意3点:

1.在关联user和blog表的时候,UserInfo中的phone字段中,需要指定blank=True,否则在关联的时候会让你填写手机号,比较麻烦

2.在每个表下面书写如下代码,返回的数据是一个可区分的字段,否则在做关联的时候,所有的外键字段都是以对象的形式展示的,没法作区分

3. 创建数据的时候,一定要注意绑定关系,不要把一个人的文章或者博客绑定到另外一个人下面,否则在后面做查询的时候,结果会不准确,然后找

bug非常不好找,因此一定一定要注意绑定关系!

    def __str__(self):
        return self.site_name

如果不写上面的代码,关联的时候,blog就这样显示

5.展示文章数据+配置用户头像

5.1 展示文章数据,后端代码

# 首页
def home(request):
    article_queryset = models.Article.objects.all()   # 查询出所有文章,传递给login.html页面
    return render(request, 'home.html', locals())

5.2 文章展示页面

文章下面的一些展示项目也需要书写,比如:Milton  发布于  

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>

{#导航条#}
<nav class="navbar navbar-inverse">
  <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="#">BBS</a>
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li class="active"><a href="#">博客 <span class="sr-only">(current)</span></a></li>
        <li><a href="#">文章</a></li>
        <li 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="#">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">
          {% 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="#" data-toggle="modal" data-target=".bs-example-modal-lg">修改密码</a></li>
            <li><a href="#">修改头像</a></li>
            <li><a href="#">后台管理</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="{% url 'logout' %}">退出登陆</a></li>
          </ul>
            <!-- Large modal -->
            <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel">
              <div class="modal-dialog modal-lg" role="document">
                <div class="modal-content">
                  <h1 class="text-center">修改密码</h1>
                    <div class="row">
                        <div class="col-md-8 col-md-offset-2">
                            <div class="form-group">
                                <label for="">用户名</label>
                                <input type="text" disabled value="{{ request.user.username }}" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">原密码</label>
                                <input type="password" id="id_old_password" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">新密码</label>
                                <input type="password" id="id_new_password" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">确认密码</label>
                                <input type="password" id="id_confirm_password" class="form-control">
                            </div>
                            <div class="modal-footer">
                                <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
                                <button type="button" class="btn btn-primary" id="id_change_password">修改</button>
                                <span style="color: red" id="id_error"></span>
                            </div>
                        </div>
                    </div>
                </div>
              </div>
            </div>
        </li>
          {% else %}
              <li><a href="{% url 'register' %}">注册</a></li>
              <li><a href="{% url 'login' %}">登陆</a></li>
          {% endif %}
      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>

{#侧边栏及文章部分展示#}
<div class="container-fluid">
    <div class="row">
{#        左侧边栏#}
        <div class="col-md-2">
            <div class="panel panel-danger">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-info">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-warning">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-primary">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-success">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
        </div>
{#        中间#}
      {#文章展示的核心代码#}
        <div class="col-md-8">
            {% for article_obj in article_queryset %}
                <ul class="media-list">
                  <li class="media">
                    <h4 class="media-heading"><a href="">{{ article_obj.title}}</a></h4>
                    <div class="media-left">
                      <a href="#">
              {#用户头像展示核心代码#}
                        <img class="media-object" src="/media/{{ article_obj.blog.userinfo.avatar }}" alt="..." width="80">
                      </a>
                    </div>
                    <div class="media-body">
                        {{article_obj.desc}}
                    </div>
                      <br>
                      <span><a href="/{{ article_obj.blog.userinfo.username }}/">{{ article_obj.blog.userinfo.username }}&nbsp;&nbsp;</a></span>
                      <span>发布于&nbsp;&nbsp;</span>
                      <span>{{ article_obj.create_time|date:'Y-m-d' }}&nbsp;&nbsp;</span>
                      <span><span class="glyphicon glyphicon-comment"></span>评论({{ article_obj.comment_num }})&nbsp;&nbsp;</span>
                      <span><span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ article_obj.up_num }})&nbsp;&nbsp;</span>
                  </li>
                </ul>
                <hr>
            {% endfor %}

        </div>
{#        右侧边栏#}
        <div class="col-md-2">
            <div class="panel panel-danger">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-info">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-warning">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-primary">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
            <div class="panel panel-success">
              <div class="panel-heading">
                <h3 class="panel-title">广告位标题</h3>
              </div>
              <div class="panel-body">
                广告位内容
              </div>
            </div>
        </div>
    </div>
</div>


<script>
    $("#id_change_password").click(function () {
        $.ajax({
            url:'/set_password/',
            type:'post',
            data:{
                'old_password':$("#id_old_password").val(),
                'new_password':$("#id_new_password").val(),
                'confirm_password':$("#id_confirm_password").val(),
                'csrfmiddlewaretoken':'{{ csrf_token }}'
            },
            success:function (args) {
                if(args.code==1000){
                    window.location.reload()
                }else{
                    $("#id_error").text(args.msg)
                }
            }
        })
    })
</script>

</body>
</html>
home.html

5.3 用户头像展示

5.3.1 我们在书写项目的时候,项目用到的文件,我们存放在static文件夹下面

  而用户上传的头像,简历等文件,我们也需要放在一个文件夹下,通常是media文件夹

  首先去settings.py中配置media文件路径

MEDIA_ROOT = os.path.join(BASE_DIR,'media')

5.3.2 光是配置media文件夹是没有作用的,需要将media文件夹中的内容暴露给外界使用,因此需要在urls.py 中书写如下代码:

# 用户头像资源暴露
    url(r'^media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT}),

5.3.3 在home.html中给用户头像配置路径及头像的文件数据

<a href="#">
   <img class="media-object" src="/media/{{ article_obj.blog.userinfo.avatar }}" alt="..." width="80">
</a>

6.个人站点页面搭建

6.1 urls.py

# 个人站点
    url(r'^(?P<username>\w+)/$', views.site, name='site'),

6.2 view.py

# 个人站点
def site(request,username,**kwargs):
    user_obj = models.UserInfo.objects.filter(username=username).first()
  
# 1.判断用户是否存在 if not user_obj: # 2.如果用户不存在,则返回一个错误的404页面 return render(request, 'error.html') # 3.如果用户存在,返回一个 个人站点页面 return render(request, 'site.html', locals())

6.3 errorr.html

直接抄写博客园的错误页面代码即可

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    {% load static %}
</head>
<body>

<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8'>
    <link rel="icon" href="//common.cnblogs.com/favicon.ico" type="image/x-icon" />
    <title>404 页面不存在 - 博客园</title>
    <style type='text/css'>
        body {
            margin: 8% auto 0;
            max-width: 400px;
            min-height: 200px;
            padding: 10px;
            font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
            font-size: 14px;
            padding-right: 200px;
            position: relative;
        }
        p { color: #555;margin: 15px 0px; }
        img { border: 0px; }
        .d { color: #404040; }
        .robot img { max-width: 192px; }
        .robot { position: absolute; top: 0; right: 0; }
    </style>
</head>
<body>
    <p style="margin-left: 5px;"><a href="/home/"><img src="{% static 'img/blog.jpg' %}" style="height:45px" alt="cnblogs"></a></p>
    <div style="margin-top:20px">
        <p style=""><b style="">404.</b> 抱歉,您访问的资源不存在。</p>
        <p class="d">可能是网址有误,或者对应的内容被删除,或者处于私有状态。</p>
        <p style="color:#777;"><a href="/home/">返回网站首页</a></p>
    </div>
    <div class="robot"><a href="/home/"><img src="{% static 'img/404-robot.png' %}" alt="404 robot" /></a></div>
    <script async src="https://www.googletagmanager.com/gtag/js?id=G-4CQQXWHK3C"></script>
    <script>
      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());

      gtag('config', 'G-4CQQXWHK3C');
    </script>
</body>
</html>
</body>
</html>
View Code

6.4 site.html

个人站点页面布局:导航条+2/8布局

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<nav class="navbar navbar-inverse">
  <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="#">{{ blog.site_title }}</a>   {# 这里不再展示BBS,而是展示每个人的站点名称 #}
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li class="active"><a href="#">博客 <span class="sr-only">(current)</span></a></li>
        <li><a href="#">文章</a></li>
        <li 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="#">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">
          {% 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="#" data-toggle="modal" data-target=".bs-example-modal-lg">修改密码</a></li>
            <li><a href="#">修改头像</a></li>
            <li><a href="#">后台管理</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="{% url 'logout' %}">退出登陆</a></li>
          </ul>
            <!-- Large modal -->
            <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel">
              <div class="modal-dialog modal-lg" role="document">
                <div class="modal-content">
                  <h1 class="text-center">修改密码</h1>
                    <div class="row">
                        <div class="col-md-8 col-md-offset-2">
                            <div class="form-group">
                                <label for="">用户名</label>
                                <input type="text" disabled value="{{ request.user.username }}" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">原密码</label>
                                <input type="password" id="id_old_password" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">新密码</label>
                                <input type="password" id="id_new_password" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">确认密码</label>
                                <input type="password" id="id_confirm_password" class="form-control">
                            </div>
                            <div class="modal-footer">
                                <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
                                <button type="button" class="btn btn-primary" id="id_change_password">修改</button>
                                <span style="color: red" id="id_error"></span>
                            </div>
                        </div>
                    </div>
                </div>
              </div>
            </div>
        </li>
          {% else %}
              <li><a href="{% url 'register' %}">注册</a></li>
              <li><a href="{% url 'login' %}">登陆</a></li>
          {% endif %}
      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>

<div class="cotainer-fluid">
    <div class="row">
    {# 以下不再是广告内容,而是文章的分类、标签、日期归档 #}
        <div class="col-md-3">
            <div class="panel panel-warning">
              <div class="panel-heading">
                <h3 class="panel-title">文章分类</h3>
              </div>
              <div class="panel-body">
                ...
              </div>
            </div>
            <div class="panel panel-primary">
              <div class="panel-heading">
                <h3 class="panel-title">文章标签</h3>
              </div>
              <div class="panel-body">
                ...
              </div>
            </div>
            <div class="panel panel-success">
              <div class="panel-heading">
                <h3 class="panel-title">日期归档</h3>
              </div>
              <div class="panel-body">
                ...
              </div>
            </div>
        </div>
        <div class="col-md-9">
        {# 以下是文章部分,直接拷贝home.html页面中的代码即可 ,只需要其中的article_queryset换成article_list即可#}
            {% for article_obj in article_list %}
                <ul class="media-list">
                  <li class="media">
                    <h4 class="media-heading"><a href="">{{ article_obj.title}}</a></h4>
                    <div class="media-left">
                      <a href="#">
                        <img class="media-object" src="/media/{{ article_obj.blog.userinfo.avatar }}" alt="..." width="80">
                      </a>
                    </div>
                    <div class="media-body">
                        {{article_obj.desc}}
                    </div>
                      <div class="pull-right">
                      <span>posted&nbsp;&nbsp;</span>
                      <span>@&nbsp;&nbsp;</span>
                      <span>{{ article_obj.create_time|date:'Y-m-d' }}</span>
                      <span>{{ article_obj.blog.userinfo.username }}&nbsp;&nbsp;</span>
                      <span><span class="glyphicon glyphicon-comment"></span>评论({{ article_obj.comment_num }})&nbsp;&nbsp;</span>
                      <span><span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ article_obj.up_num }})&nbsp;&nbsp;</span>
                      <span><a href="">编辑</a>&nbsp;&nbsp;</span>
                      </div>
                  </li>
                </ul>
                <hr>
            {% endfor %}
        </div>
    </div>
</div>
</body>
</html>
site.html

7.侧边栏展示+筛选

7.1 urls.py

关键点:利用有名分组的特性,合并侧边栏筛选功能

 

# 侧边栏筛选功能
    url(r'^(?P<username>\w+)/(?P<condition>category|tag|archive)/(?P<param>.*)/', views.site)

7.1 views.py

关键点1:3条orm分组查询必须得会(重点是正反向查询)

   # 1.查询每个用户所有的文章分类及分类下的文章数
    category_list = models.Category.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('name','count_num','pk')
    # 2.查询每个用户的所有标签及标签下的文章数
    tag_list = models.Tag.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('name','count_num','pk')
    # 3.按照年月筛选文章(这一条参考django提供的语法)
    date_list = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values('month').annotate(count_num=Count('pk')).values_list('month','count_num')

关键点2:对article_list 做进一步筛选,利用字符串的切片操作以及神奇的双下划线查询。

# 个人站点
def site(request,username,**kwargs):
    user_obj = models.UserInfo.objects.filter(username=username).first()
    # 1.判断用户是否存在
    if not user_obj:
        # 2.如果用户不存在,则返回一个错误的404页面
        return render(request, 'error.html')
    blog = user_obj.blog
    article_list = models.Article.objects.filter(blog=blog)
    if kwargs:
        # print(kwargs)
        condition = kwargs.get('condition')
        param = kwargs.get('param')
        if condition == 'category':
            article_list = article_list.filter(category_id=param)
            # print(article_list)
        elif condition == 'tag':
            article_list = article_list.filter(tags__pk=param)  # 多对多查询,使用__
        else:
            year,month = param.split('-')
            article_list = article_list.filter(create_time__month=month,create_time__year=year)
    # 1.查询每个用户所有的文章分类及分类下的文章数
    category_list = models.Category.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('name','count_num','pk')
    # 2.查询每个用户的所有标签及标签下的文章数
    tag_list = models.Tag.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('name','count_num','pk')
    # 3.按照年月筛选文章
    date_list = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values('month').annotate(count_num=Count('pk')).values_list('month','count_num')
    # 3.如果用户存在,返回一个个人站点页面
    return render(request, 'site.html', locals())
views.py-site

7.2 site.html

关键点1:利用模版语法的点点点的功能,展示筛选出来的数据

关键点2:构造url:<a href="/{{ username }}/archive/{{ date.0|date:'Y-m' }}">

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<nav class="navbar navbar-inverse">
  <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="#">{{ blog.site_title }}</a>  {# 这里不再展示BBS,而是展示每个人的站点名称 #}
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li class="active"><a href="#">博客 <span class="sr-only">(current)</span></a></li>
        <li><a href="#">文章</a></li>
        <li 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="#">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">
          {% 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="#" data-toggle="modal" data-target=".bs-example-modal-lg">修改密码</a></li>
            <li><a href="#">修改头像</a></li>
            <li><a href="#">后台管理</a></li>
            <li role="separator" class="divider"></li>
            <li><a href="{% url 'logout' %}">退出登陆</a></li>
          </ul>
            <!-- Large modal -->
            <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel">
              <div class="modal-dialog modal-lg" role="document">
                <div class="modal-content">
                  <h1 class="text-center">修改密码</h1>
                    <div class="row">
                        <div class="col-md-8 col-md-offset-2">
                            <div class="form-group">
                                <label for="">用户名</label>
                                <input type="text" disabled value="{{ request.user.username }}" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">原密码</label>
                                <input type="password" id="id_old_password" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">新密码</label>
                                <input type="password" id="id_new_password" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="">确认密码</label>
                                <input type="password" id="id_confirm_password" class="form-control">
                            </div>
                            <div class="modal-footer">
                                <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
                                <button type="button" class="btn btn-primary" id="id_change_password">修改</button>
                                <span style="color: red" id="id_error"></span>
                            </div>
                        </div>
                    </div>
                </div>
              </div>
            </div>
        </li>
          {% else %}
              <li><a href="{% url 'register' %}">注册</a></li>
              <li><a href="{% url 'login' %}">登陆</a></li>
          {% endif %}
      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>

<div class="cotainer-fluid">
    <div class="row">
        <div class="col-md-3">
            <div class="panel panel-warning">
              <div class="panel-heading">
                <h3 class="panel-title">文章分类</h3>
              </div>
              <div class="panel-body">
                {% for category in category_list %}
                    <p><a href="/{{ username }}/category/{{ category.2 }}/">{{ category.0 }}({{ category.1 }})</a></p>
                {% endfor %}

              </div>
            </div>
            <div class="panel panel-primary">
              <div class="panel-heading">
                <h3 class="panel-title">文章标签</h3>
              </div>
              <div class="panel-body">
                {% for tag in tag_list %}
                    <p><a href="/{{ username }}/tag/{{ tag.2 }}">{{ tag.0 }}({{ tag.1 }})</a></p>
                {% endfor %}

              </div>
            </div>
            <div class="panel panel-success">
              <div class="panel-heading">
                <h3 class="panel-title">日期归档</h3>
              </div>
              <div class="panel-body">
                {% for date in date_list %}
                    <p><a href="/{{ username }}/archive/{{ date.0|date:'Y-m' }}">{{ date.0|date:'Y年m月' }}({{ date.1 }})</a></p>
                {% endfor %}

              </div>
            </div>
        </div>
        <div class="col-md-9">
            {% for article_obj in article_list %}
                <ul class="media-list">
                  <li class="media">
                    <h4 class="media-heading"><a href="">{{ article_obj.title}}</a></h4>
                    <div class="media-left">
                      <a href="#">
                        <img class="media-object" src="/media/{{ article_obj.blog.userinfo.avatar }}" alt="..." width="80">
                      </a>
                    </div>
                    <div class="media-body">
                        {{article_obj.desc}}
                    </div>
                      <div class="pull-right">
                      <span>posted&nbsp;&nbsp;</span>
                      <span>@&nbsp;&nbsp;</span>
                      <span>{{ article_obj.create_time|date:'Y-m-d' }}</span>
                      <span>{{ article_obj.blog.userinfo.username }}&nbsp;&nbsp;</span>
                      <span><span class="glyphicon glyphicon-comment"></span>评论({{ article_obj.comment_num }})&nbsp;&nbsp;</span>
                      <span><span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ article_obj.up_num }})&nbsp;&nbsp;</span>
                      <span><a href="">编辑</a>&nbsp;&nbsp;</span>
                      </div>
                  </li>
                </ul>
                <hr>
            {% endfor %}
        </div>
    </div>
</div>
</body>
</html>
View Code

 

 

 返回目录

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

posted @ 2021-11-05 17:29  五仁味儿月饼  阅读(117)  评论(0编辑  收藏  举报