Python开发【项目】:博客后台


概述

  通过自己写的博客后台代码、思路,来与武sir的代码进行一个差异化的比较,记录之间的差距,改善以后写代码的思路

  博客后台这个项目,对之前Django学习的各个知识点都有涉及到,非常重要 

 

用户登录验证

数据库表:

from django.db import models


# 除了主键其他默认可以为空
# max_length在CharField中必填

class UserInfo(models.Model):
    """
    用户表
    """
    nid = models.BigAutoField(primary_key=True)         # 主键自增 8位数
    username = models.CharField(verbose_name='用户名', max_length=32, unique=True)     # unique 唯一性
    password = models.CharField(verbose_name='密码', max_length=64)                     # verbose_name ModeForm验证时显示名,
    nickname = models.CharField(verbose_name='昵称', max_length=32)                       # 等同于Form类里面的label
    email = models.EmailField(verbose_name='邮箱', unique=True)                        # unique 唯一性
    avatar = models.ImageField(verbose_name='头像')

    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)      # auto_now_add 更新时,自动更新为当前时间

    fans = models.ManyToManyField(verbose_name='粉丝们', to='UserInfo', through='UserFans',
                                  through_fields=('user', 'follower'))


class Blog(models.Model):
    """
    博客信息
    """
    nid = models.BigAutoField(primary_key=True)
    title = models.CharField(verbose_name='个人博客标题', max_length=64)
    site = models.CharField(verbose_name='个人博客前缀', max_length=32, unique=True)
    theme = models.CharField(verbose_name='博客主题', max_length=32)

    user = models.OneToOneField(to='UserInfo', to_field='nid')


class UserFans(models.Model):
    """
    互粉关系表
    """
    user = models.ForeignKey(verbose_name='博主', to='UserInfo', to_field='nid', related_name='users')
    follower = models.ForeignKey(verbose_name='粉丝', to='UserInfo', to_field='nid', related_name='followers')

    # 联合唯一索引 索引本身目的就是加快查询速度
    class Meta:
        unique_together = [
            ('user', 'follower'),
        ]


class Category(models.Model):
    """
    博主个人文章分类表,自创建
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='分类标题', max_length=32)

    blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid')


class ArticleDetail(models.Model):
    """
    文章详细表
    """
    content = models.TextField(verbose_name='文章内容', )

    article = models.OneToOneField(verbose_name='所属文章', to='Article', to_field='nid')


class UpDown(models.Model):
    """
    文章顶或踩
    """
    article = models.ForeignKey(verbose_name='文章', to='Article', to_field='nid')
    user = models.ForeignKey(verbose_name='赞或踩用户', to='UserInfo', to_field='nid')
    up = models.BooleanField(verbose_name='是否赞')

    class Meta:
        unique_together = [
            ('article', 'user'),
        ]


class Comment(models.Model):
    """
    评论表
    """
    nid = models.BigAutoField(primary_key=True)
    content = models.CharField(verbose_name='评论内容', max_length=255)
    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)

    reply = models.ForeignKey(verbose_name='回复评论', to='self', related_name='back', null=True)
    article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid')
    user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid')


class Tag(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='标签名称', max_length=32)
    blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid')



class Article(models.Model):
    '''
    文章简介表
    '''
    nid = models.BigAutoField(primary_key=True)
    title = models.CharField(verbose_name='文章标题', max_length=128)
    summary = models.CharField(verbose_name='文章简介', max_length=255)
    read_count = models.IntegerField(default=0)
    comment_count = models.IntegerField(default=0)
    up_count = models.IntegerField(default=0)
    down_count = models.IntegerField(default=0)
    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)

    blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid')
    category = models.ForeignKey(verbose_name='文章类型', to='Category', to_field='nid', null=True)

    type_choices = [
        (1, "Python"),
        (2, "Linux"),
        (3, "OpenStack"),
        (4, "GoLang"),
    ]

    article_type_id = models.IntegerField(choices=type_choices, default=None)

    tags = models.ManyToManyField(
        to="Tag",
        through='Article2Tag',
        through_fields=('article', 'tag'),
    )


class Article2Tag(models.Model):
    '''
    文章跟标签对应关系
    '''
    article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid')
    tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid')

    class Meta:
        unique_together = [
            ('article', 'tag'),
        ]
models.py

我的代码:

from django import forms
from django.forms import fields
from django.forms import widgets

from repository import models
from django.core.exceptions import ValidationError

class LoginForm(forms.Form):
    username = fields.CharField(
        max_length=12,
        widget=widgets.Input(attrs={'class':'form-control','placeholder':"请输入用户名"}),
        error_messages={'required': '用户名不能为空',
                        'max_length': '密码长度不能大于12位'}
    )
    password = fields.CharField(
        max_length=12,
        min_length=6,
        widget=widgets.PasswordInput(attrs={'class':'form-control','placeholder':"请输入密码"}),
        error_messages={'required': '密码不能为空', 'min_length': '密码长度不能小于6位',
                        'max_length': '密码长度不能大于12位'}
    )
    check_code = fields.CharField(
        error_messages={'required': '验证码不能为空', }
    )

    def clean(self):
        user_obj = models.UserInfo.objects.filter(
            username=self.cleaned_data.get('username'),password=self.cleaned_data.get('password')
        ).first()
        if user_obj:
            return self.cleaned_data['username']
        else:
            raise ValidationError(message='用户名或密码错误')

    def clean_check_code(self):
        code = self.request.POST.get('check_code')
        if code.upper() == self.request.session['CheckCode'].upper():
            return self.cleaned_data['check_code']
        else:
            raise ValidationError(message='验证码错误')

    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        if args:
            self.request = args[1]
from.py
from io import BytesIO
from django.shortcuts import HttpResponse
from django.shortcuts import render
from django.shortcuts import redirect
from utils.check_code import create_validate_code
from web.views.form import LoginForm
from web.views.form import RegisterForm
from repository import models


import json

def check_code(request):
    """
    验证码
    :param request:
    :return:
    """
    # 1. 创建一张图片 pip3 install Pillow
    # 2. 在图片中写入随机字符串
    # obj = object()
    # 3. 将图片写入到制定文件
    # 4. 打开制定目录文件,读取内容
    # 5. HttpResponse(data)

    stream = BytesIO()      #在内存中生成一个文件对象
    img, code = create_validate_code()  #生成图片img和字符串code
    img.save(stream,'PNG')      #把验证图片存放到内存中以PNG名存放
    request.session['CheckCode'] = code     #把生成的字符串code存放到session中
    # print('验证码',code)
    return HttpResponse(stream.getvalue())      #stream.getvalue()返回图片的内容


def login(request):
    """
    登陆
    """
    if request.method == 'GET':
        if request.session.get('is_login',None):
            return redirect('/index/')
        else:
            obj = LoginForm()
            return render(request, 'login.html',{'obj':obj})
    elif  request.method == 'POST':
        data = {'status': True, 'error': None,}
        obj = LoginForm(request.POST,request)
        result = obj.is_valid()
        if result:
            print('验证通过')
            username = request.POST.get('username')
            user_dict = models.UserInfo.objects.filter(username=username).values('nid','username','nickname','email','blog__site',
                                                                        'blog__title','blog__theme')[0]
            for key in user_dict:
                request.session[key] = user_dict[key]

            request.session['is_login']=True
            if request.POST.get('rmb') == '0':
                request.session.set_expiry(60*60*24*30)
        else:
            print(obj.errors.as_json)
            data['status'] = False
            data['error'] = obj.errors

        return HttpResponse(json.dumps(data))
views.py
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.css"/>
    <link rel="stylesheet" href="/static/plugins/font-awesome/css/font-awesome.css"/>
    <link rel="stylesheet" href="/static/css/edmure.css"/>
    <link rel="stylesheet" href="/static/css/commons.css"/>
    <link rel="stylesheet" href="/static/css/account.css"/>
    <style>
    </style>
</head>
<body>
<div class="login">
    <div style="font-size: 25px; font-weight: bold;text-align: center;">
        用户登陆
    </div>
    <form id='login_form' role="form" onsubmit = "return false" >   <!--不进行跳转-->
        {% csrf_token %}
        <div class="form-group">
            <label for="username">用户名</label>
{#            <input type="text" class="form-control"  placeholder="请输入用户名">#}
            {{ obj.username }}
        </div>
        <div class="form-group">
            <label for="password">密码</label>
{#            <input type="password" class="form-control"  placeholder="请输入密码">#}
            {{ obj.password }}
        </div>
        <div class="form-group">
            <label for="password">验证码</label>

            <div class="row">
                <div class="col-xs-7">
                    <input type="text" class="form-control" placeholder="请输入验证码" name="check_code">
                </div>
                <div class="col-xs-5">
                    <img id='changecheckcode' src="/check_code.html" onclick="changeCheckCode();">   <!--点击更换验证码-->
                </div>
            </div>

        </div>
        <div class="checkbox">
            <label>
                <input type="checkbox" name="rmb" value="0"> 一个月内自动登陆
            </label>
            <div class="right">
                <a href="/register.html"  style="margin-right: 10px">注册</a>
                <a href="#">忘记密码?</a>
            </div>
        </div>
        <button type="submit" id="loginsubmit" class="btn btn-default">登 陆</button>
        <div class="error_message"></div>
    </form>
</div>
    <script src="/static/js/jquery-1.12.4.js"></script>
    <script src="/static/js/account.js"></script>
    <script src="/static/js/account_login.js"></script>
</body>
</html>

function changeCheckCode(){
           var img = $('#changecheckcode')[0];
            img.src = img.src +  '?';   //刷新验证码
        }

function ShowError(error) {
    $('.error_message').text(error)
}

function EmptyError() {
     $('.error_message').text('')
}

/**
 * Created by L on 2017/2/17.
 */



$('#loginsubmit').click(function () {
            EmptyError() ;        //清空错误
            $.ajax({
                url:'/login.html',
                type:'POST',
                data:$('#login_form').serialize(),
                dataType:'JSON',
                success:function (data) {
                   if(data['status']){
                       location.href = '/';         //成功跳转

                   }else {
                       var error_message = DateHandel(data);
                       console.log(error_message)
                       var error_message = '**' + error_message;

                       ShowError(error_message)
                   }
                },error:function () {

                }
            })
        });

function DateHandel(data) {
    var error = data['error'];
    if(error['username']){           //用户名格式输入错误
       var error_message = error['username'][0]
    }else {
       if(error['password']){       //密码格式输入错误
           var error_message = error['password'][0]
       }else {
             if(error['check_code']){     //验证码为空
                 changeCheckCode();
                 var error_message = error['check_code'][0]
             }else {
                 if(error['__all__']){
                     var error_message = error['__all__'][0]
              }
            }
          }
      }
    return error_message
}
login.html

武sir的代码:

from django.core.exceptions import ValidationError
from django import forms as django_forms
from django.forms import fields as django_fields
from django.forms import widgets as django_widgets

from repository import models

from .base import BaseForm


class LoginForm(BaseForm, django_forms.Form):
    username = django_fields.CharField(
        min_length=6,
        max_length=20,
        error_messages={'required': '用户名不能为空.', 'min_length': "用户名长度不能小于6个字符", 'max_length': "用户名长度不能大于32个字符"}
    )
    password = django_fields.RegexField(
        '^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[!@#$\%\^\&\*\(\)])[0-9a-zA-Z!@#$\%\^\&\*\(\)]{8,32}$',
        min_length=12,
        max_length=32,
        error_messages={'required': '密码不能为空.',
                        'invalid': '密码必须包含数字,字母、特殊字符',
                        'min_length': "密码长度不能小于8个字符",
                        'max_length': "密码长度不能大于32个字符"}
    )
    rmb = django_fields.IntegerField(required=False)

    check_code = django_fields.CharField(
        error_messages={'required': '验证码不能为空.'}
    )

    def clean_check_code(self):
        if self.request.session.get('CheckCode').upper() != self.request.POST.get('check_code').upper():
            raise ValidationError(message='验证码错误', code='invalid')
from.py
from io import BytesIO
from django.shortcuts import HttpResponse
from django.shortcuts import render
from django.shortcuts import redirect
from utils.check_code import create_validate_code
from repository import models
from ..form.accout import LoginForm



def check_code(request):
    """
    验证码
    :param request:
    :return:
    """
    stream = BytesIO()
    img, code = create_validate_code()
    img.save(stream, 'PNG')
    request.session['CheckCode'] = code
    return HttpResponse(stream.getvalue())


def login(request):
    """
    登陆
    :param request:
    :return:
    """
    if request.method == 'GET':
        return render(request, 'login.html')
    elif request.method == 'POST':
        result = {'status': False, 'message': None, 'data': None}
        form = LoginForm(request=request, data=request.POST)
        if form.is_valid():
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            user_info = models.UserInfo.objects. \
                filter(username=username, password=password). \
                values('nid', 'nickname',
                       'username', 'email',
                       'avatar',
                       'blog__nid',
                       'blog__site').first()

            if not user_info:
                # result['message'] = {'__all__': '用户名或密码错误'}
                result['message'] = '用户名或密码错误'
            else:
                result['status'] = True
                request.session['user_info'] = user_info
                if form.cleaned_data.get('rmb'):
                    request.session.set_expiry(60 * 60 * 24 * 7)
        else:
            print(form.errors)
            if 'check_code' in form.errors:
                result['message'] = '验证码错误或者过期'
            else:
                result['message'] = '用户名或密码错误'
        return HttpResponse(json.dumps(result))
views.py
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.css"/>
    <link rel="stylesheet" href="/static/plugins/font-awesome/css/font-awesome.css"/>
    <link rel="stylesheet" href="/static/css/edmure.css"/>
    <link rel="stylesheet" href="/static/css/commons.css"/>
    <link rel="stylesheet" href="/static/css/account.css"/>
</head>
<body>
<div class="login">
    <div style="font-size: 25px; font-weight: bold;text-align: center;">
        用户登陆
    </div>
    <form id="fm" method="POST" action="/login.html">
        {% csrf_token %}
        <div class="form-group">
            <label for="username">用户名</label>
            <input type="text" class="form-control" name="username" id="username" placeholder="请输入用户名">
        </div>
        <div class="form-group">
            <label for="password">密码</label>
            <input type="password" class="form-control" name="password" id="password" placeholder="请输入密码">
        </div>
        <div class="form-group">
            <label for="password">验证码</label>

            <div class="row">
                <div class="col-xs-7">
                    <input type="text" class="form-control" name="check_code" id="check_code" placeholder="请输入验证码">
                </div>
                <div class="col-xs-5">
                    <img id="check_code_img" src="/check_code.html">
                </div>
            </div>

        </div>
        <div class="checkbox">
            <label>
                <input type="checkbox" value="1" name="rmb"> 一个月内自动登陆
            </label>

            <div class="right">
                <a href="#">忘记密码?</a>
            </div>
        </div>
        <div class="row">
            <div class="col-xs-3">
                <a id="submit" class="btn btn-default">登 陆</a>
            </div>
            <div class="col-xs-9" style="padding-left: 0;">
                <div class="alert alert-danger hide">
                    <span style="padding: 0 5px 0 5px;display: inline-block;font-size: 14px">
                        <i class="fa fa-minus-circle" aria-hidden="true"></i>
                    </span>
                    <span id="error_msg" style="font-size: 12px;"></span>
                </div>
            </div>
        </div>

    </form>
    <script src="/static/js/jquery-1.12.4.js"></script>
    <script type="text/javascript">
        $(function () {
            bindLogin();
        });
        function bindLogin() {
            $('#submit').click(function () {
                var $msg = $('#error_msg');
                $msg.parent().addClass('hide');
                $.ajax({
                    url: '/login.html',
                    type: 'POST',
                    data: $('#fm').serialize(),
                    dataType: 'JSON',
                    success: function (arg) {
                        if(arg.status){
                            location.href = '/'
                        }else{
                            $msg.parent().removeClass('hide');
                            $msg.text(arg.message);
                            var img = $('#check_code_img')[0];
                            img.src = img.src + '?';
                            $('#password,#check_code').val('');
                        }

                    }
                })

            })
        }
    </script>
</div>
</body>
</html>
login.html

区别:

、前后端交互

  前后端交互这块,自己没有活学活用,上课时老师讲课时讲到直接把obj.errors传到前端,然后就固执的这么用了,导致前端js要写很多的if进行判断(我的错误提示很完善,从不能为空,到用户错误,武sir这方面写的比较简单);武sir是直接向前端传输一个错误的字符串(result['message'] = '用户名或密码错误'),前端直接打印即可,js无需多的判断,如果想加多点的错误提示,可以在后台上多做写判断亦可

、form验证

  我from验证这一块,直接受到后台与前端传输数据为obj.errors影响,由于所有错误信息必须包含到obj.errors里面,导致用户密码验证必须写到form里面进行验证,获取用户名密码必须要把request传送到form,这个当时困住了好长时间,好算最后解决了;用户密码验证,武sir放到了处理函数里面,很机智

、session信息

  把要存的信息,key、value分别对应进行存储;武sir直接存了一个‘use_info’字段,包含了所有的信息

 

 

用户注册

下面的代码都是整合后的代码了:

class RegisterForm(forms.Form):
    username = fields.CharField(
        max_length=12,
        widget=widgets.Input(attrs={'class': 'form-control', 'placeholder': "请输入用户名"}),
        error_messages={'required': '用户名不能为空',
                        'max_length': '密码长度不能大于12位'}
    )
    email = fields.EmailField(
        widget=widgets.Input(attrs={'class': 'form-control', 'placeholder': "请输入邮箱"}),
        error_messages={'required': '邮箱不能为空', 'invalid':'邮箱格式不正确'}
    )
    password = fields.CharField(
        max_length=12,
        min_length=6,
        widget=widgets.PasswordInput(attrs={'class': 'form-control', 'placeholder': "请输入密码"}),
        error_messages={'required': '密码不能为空', 'min_length': '密码长度不能小于6位',
                        'max_length': '密码长度不能大于12位'}
    )
    confirm_password = fields.CharField(
        max_length=12,
        min_length=6,
        widget=widgets.PasswordInput(attrs={'class': 'form-control', 'placeholder': "请重新输入密码"}),
        error_messages={'required': '确认密码不能为空', 'min_length': '确认密码长度不能小于6位',
                        'max_length': '确认密码长度不能大于12位'}
    )
    check_code = fields.CharField(
        error_messages={'required': '验证码不能为空',}
    )

    def clean_check_code(self):
        print(self.request.POST.get('check_code').upper())
        print(self.request.session['CheckCode'].upper())
        if self.request.POST.get('check_code').upper() == self.request.session['CheckCode'].upper():
            return self.cleaned_data['check_code']
        else:
            raise ValidationError(message='验证码错误')

    def clean(self):
        confirm_password = self.cleaned_data.get('confirm_password')
        password = self.cleaned_data.get('password')
        if confirm_password == password:
            return self.cleaned_data
        else:
            raise ValidationError(message='两次密码输入的不一致')


    def __init__(self, request, *args, **kwargs):
        self.request = request
        super().__init__(*args, **kwargs)
form.py
from django.db.models import Q
def register(request):
    """
    注册
    """
    if request.method == 'GET':
        return render(request, 'register.html')
    elif  request.method == 'POST':
        message = {'status': False, 'error': None}
        form = RegisterForm(request,request.POST)
        result = form.is_valid()
        if result:          # 验证通过
            # 开始用户名、邮箱是否唯一
            username = request.POST.get('username')
            email = request.POST.get('email')

            user_obj = models.UserInfo.objects.filter(Q(username=username)|Q(email=email)).first()
            if user_obj:
                message['error'] = '用户名或邮箱已经注册过'
            else:
                print(form.cleaned_data)
                form.cleaned_data.pop('confirm_password')
                form.cleaned_data.pop('check_code')
                # print(form.cleaned_data)
                models.UserInfo.objects.create(**form.cleaned_data)
                user_dict = models.UserInfo.objects.filter(username=username).values('nid','username','email').first()
                # print(type(user_dict))
                request.session['user_info'] = user_dict
                message['status'] = True

        else:
            print(form.errors)
            if 'username' in form.errors:      # 按照顺序进行验证,先用户名、邮箱、密码、确实密码、密码一致、验证码,用户名邮箱唯一
                message['error'] = form.errors['username']
            else:
                if 'email' in form.errors:
                    message['error'] = form.errors['email']
                else:
                    if 'password' in form.errors:
                        message['error'] = form.errors['password']
                    else:
                        if 'confirm_password' in form.errors:
                            message['error'] = form.errors['confirm_password']
                        else:
                            if '__all__' in form.errors:
                                message['error'] = form.errors['__all__']
                            else:
                                if 'check_code' in form.errors:
                                    message['error'] = form.errors['check_code']
        # print(message)
        return HttpResponse(json.dumps(message))
views.py
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.css"/>
    <link rel="stylesheet" href="/static/plugins/font-awesome/css/font-awesome.css"/>
    <link rel="stylesheet" href="/static/css/edmure.css"/>
    <link rel="stylesheet" href="/static/css/commons.css"/>
    <link rel="stylesheet" href="/static/css/account.css"/>
    <style>

    </style>
</head>
<body>
<div class="register">
    <div style="font-size: 25px; font-weight: bold;text-align: center;">
        用户注册
    </div>
    <form role="form" id="reg_form">
        {% csrf_token %}
        <div class="form-group">
            <label for="username">用户名</label>
            <input name="username" class="form-control" placeholder="请输入用户名">
        </div>
        <div class="form-group">
            <label for="email">邮箱</label>
            <input name="email" class="form-control" placeholder="请输入邮箱">
        </div>
        <div class="form-group">
            <label for="password">密码</label>
            <input name="password" class="form-control" type="password" placeholder="请输入密码">
        </div>
        <div class="form-group">
            <label for="confirm_password">确认密码</label>
            <input name="confirm_password" class="form-control" type="password" placeholder="请重新输入密码">
        </div>

        <div class="form-group">
            <label for="password">验证码</label>

            <div class="row">
                <div class="col-xs-7">
                    <input type="text" class="form-control" id="password" placeholder="请输入验证码" name="check_code">
                </div>
                <div class="col-xs-5">
                    <img id='changecheckcode' src="/check_code.html" onclick="changeCheckCode();">   <!--点击更换验证码-->
                </div>
            </div>
        </div>
          <div class="row">
            <div class="col-xs-3">
                <a id="submit" class="btn btn-default">下一步</a>
            </div>

            <div class="col-xs-9" style="padding-left: 0;">
                    <div class="alert alert-danger hide">
                        <span style="padding: 0 5px 0 5px;display: inline-block;font-size: 14px">
                            <i class="fa fa-minus-circle" aria-hidden="true"></i>
                        </span>
                        <span id="error_msg" style="font-size: 12px;"></span>
                    </div>
            </div>
        </div>
{#        <input type="button" id="regsubmit" class="btn btn-default" value="下一步"/>#}
{#        <div class="error_message"></div>#}
    </form>
</div>
    <script src="/static/js/jquery-1.12.4.js"></script>
    <script src="/static/js/account.js"></script>
    <script src="/static/js/account_register.js"></script>

</body>
</html>
register.html

代码有意思的地方:

form验证中__init__传入request参数

class RegisterForm(forms.Form):
    def __init__(self, request, *args, **kwargs):
        self.request = request
        super().__init__(*args, **kwargs)

def register(request):
    """
    注册
    """
    elif  request.method == 'POST':
        message = {'status': False, 'error': None}
        form = RegisterForm(request,request.POST)   # 传参

model检索Q或请求:

from django.db.models import Q
def register(request):
            user_obj = models.UserInfo.objects.filter(Q(username=username)|Q(email=email)).first()

所有的验证错误提示信息在后端处理:

def register(request):
        else:
            print(form.errors)
            if 'username' in form.errors:      # 按照顺序进行验证,先用户名、邮箱、密码、确实密码、密码一致、验证码,用户名邮箱唯一
                message['error'] = form.errors['username']
            else:
                if 'email' in form.errors:
                    message['error'] = form.errors['email']
                else:
                    if 'password' in form.errors:
                        message['error'] = form.errors['password']
                    else:
                        if 'confirm_password' in form.errors:
                            message['error'] = form.errors['confirm_password']
                        else:
                            if '__all__' in form.errors:
                                message['error'] = form.errors['__all__']
                            else:
                                if 'check_code' in form.errors:
                                    message['error'] = form.errors['check_code']
        # print(message)
        return HttpResponse(json.dumps(message))

 

 

首页文章展示: 

def index(request,*args,**kwargs):
    print('innnnidex')
    """
    博客首页,展示全部博文
    :param request:
    :return:
    """

    if kwargs:
        article_type_id = int(kwargs.get('article_type_id'))
        kwargs['article_type_id'] = article_type_id
        article_list = models.Article.objects.filter(article_type_id=article_type_id).prefetch_related('blog').order_by('-nid')
    else:
        article_list = models.Article.objects.all().prefetch_related('blog').order_by('-nid')

    type_choices = models.Article.type_choices

    return render(request, 'index.html', {'article_list': article_list,
                                          'type_choices':type_choices,
                                          'kwargs':kwargs})
views.py
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.css"/>
    <link rel="stylesheet" href="/static/plugins/font-awesome/css/font-awesome.css"/>
    <link rel="stylesheet" href="/static/css/edmure.css"/>
    <link rel="stylesheet" href="/static/css/commons.css"/>
    <link rel="stylesheet" href="/static/css/row-avatar.css"/>

    <script type="text/javascript" src="/static/js/jquery-1.12.4.js"></script>
    <script type="text/javascript" src="/static/plugins/bootstrap/js/bootstrap.js"></script>
</head>
<body>
{% include 'include/header.html' %}

<div class="container">
    <div>
        <div class="col-md-8">
            <div class="article-list">
                {% for item in article_list %}
                <div class="article-item clearfix">
                    <h3><a href="/{{ item.blog.site }}/{{ item.nid }}.html">{{ item.title }}</a>
{#                        <small>{{ item.type_choices }}</small>#}
                        <small>{{ item.article_type_id}}</small>
                    </h3>
                    <div class="clearfix">
                        <a class="avatar left" href="#">
                            <img src="/{{ item.blog.user.avatar }}">
                        </a>
                       {{ item.summary}}
                    </div>
                    <div class="footers">
                        <a href="/{{ item.blog.site }}/index.html">
                            <i class="fa fa-user-o" aria-hidden="true"></i>
                            <span>{{ item.blog.user.username }}</span>
                        </a>
                        <span>发布于 {{ item.create_time|date:"Y-m-d H:i:s"  }}</span>
                        <a href="/{{ item.blog.site }}/{{ item.nid }}.html" class="ele">
                            <i class="fa fa-commenting-o" aria-hidden="true"></i>
                            <span>{{ item.read_count }}</span>
                        </a>
                        <a href="#" class="ele">
                            <i class="fa fa-thumbs-o-up" aria-hidden="true"></i>
                            <span>{{ item.comment_count }}</span>
                        </a>
                    </div>

                </div>
                {% endfor %}


            </div>

            <div class="clearfix">

                <ul class="pagination">
                    <li><a href="#">&laquo;</a></li>
                    <li><a href="#">1</a></li>
                    <li><a href="#">2</a></li>
                    <li><a href="#">3</a></li>
                    <li><a href="#">4</a></li>
                    <li><a href="#">5</a></li>
                    <li><a href="#">&raquo;</a></li>
                </ul>

            </div>

        </div>
        <div class="col-md-4">
            <div class="panel panel-default hot-recommend">
                <div class="panel-heading">吐血推荐</div>
                <div class="panel-body">
                    <ul class="list-unstyled">
                        <li>Lorem ipsum dolor sit amet</li>
                        <li>Consectetur adipiscing elit</li>
                        <li>Integer molestie lorem at massa</li>
                        <li>Facilisis in pretium nisl aliquet</li>
                        <li>Nulla volutpat aliquam velit
                        </li>
                        <li>Faucibus porta lacus fringilla vel</li>
                        <li>Aenean sit amet erat nunc</li>
                        <li>Eget porttitor lorem</li>
                    </ul>
                </div>
            </div>
            <div class="panel panel-default hot-comment">
                <div class="panel-heading">评论最多</div>
                <div class="panel-body">
                    <ul class="list-unstyled">
                        <li>Lorem ipsum dolor sit amet</li>
                        <li>Consectetur adipiscing elit</li>
                        <li>Integer molestie lorem at massa</li>
                        <li>Facilisis in pretium nisl aliquet</li>
                        <li>Nulla volutpat aliquam velit
                        </li>
                        <li>Faucibus porta lacus fringilla vel</li>
                        <li>Aenean sit amet erat nunc</li>
                        <li>Eget porttitor lorem</li>
                    </ul>
                </div>
            </div>

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

</body>
</html>
index.html

关键的几段代码:

前端对时间进行处理:

<span>发布于 {{ item.create_time|date:"Y-m-d H:i:s"  }}</span>

 

 

后台个人信息修改

@check_login
def base_info(request):
    """博主个人信息"""
    if request.method == 'GET':
        blogTheme = [
            {'id': '0', 'name': '默认主题'},
            {'id': '1', 'name': '黑不溜秋'},
            {'id': '2', 'name': '乌七八啦'},
            {'id': '3', 'name': '红色火焰'},
            {'id': '4', 'name': '哈哈哈嘿哈'},
        ]
        print(request.session['user_info'])
        return render(request, 'backend_base_info.html',{'blogTheme':blogTheme})

    elif request.method == 'POST':
        message = {'status':False,'error':None}

        site = request.session['user_info'].get('blog__site')         # session中查找博客地址

        if site:                                           # 博客地址之前已经创建,需要更新内容
            # print('------------',request.POST)
            nickname = request.POST.get('nickname')
            theme = request.POST.get('theme')
            title = request.POST.get('title')

            models.Blog.objects.filter(site=site).update(theme=theme,title=title)
            models.UserInfo.objects.filter(nid=request.session['user_info']['nid']).update(nickname=nickname)

            user_info_dict = request.session['user_info']
            user_info_dict['nickname']=nickname
            user_info_dict['blog__theme']=theme
            user_info_dict['blog__title']=title
            request.session['user_info']=request.session['user_info']
            print(request.session['user_info'])

            message['status'] = True

        else:
            site = request.POST.get('site')
            if site:                                # 博客地址提交
                blog_obj = models.Blog.objects.filter(site=site).first()
                if blog_obj:
                    message['error'] = '博客地址已被占用'
                else:
                    nickname = request.POST.get('nickname')
                    theme = request.POST.get('theme')
                    title = request.POST.get('title')


                    models.Blog.objects.create(site=site,
                                               theme=theme,
                                               title=title,
                                               user_id=request.session['user_info']['nid'])
                    models.UserInfo.objects.filter(nid=request.session['user_info']['nid']).update(nickname=nickname)
                    user_info_dict = request.session['user_info']
                    user_info_dict['nickname'] = nickname
                    user_info_dict['blog__theme'] = theme
                    user_info_dict['blog__title'] = title
                    user_info_dict['blog__site'] = site
                    request.session['user_info'] = request.session['user_info']
                    message['status'] = True
            else:
                message['error'] = '博客地址不能为空'

        return HttpResponse(json.dumps(message))


@check_login
def upload_avatar(request):             # 更新头像
    message = {'status': False, 'data': None}
    if request.method == 'POST':
        files = request.FILES.get('avatar_img')
        if files:                       # 省略判断文件格式的过程
            img_dir = 'static/imgs/savatar/%s'%(request.session['user_info']['username'])
            if not os.path.exists(img_dir):
                os.makedirs(img_dir)
            img_path = os.path.join(img_dir,'avatar.png')
            print(img_path)

            with open(img_path,'wb') as f:
                for item in files.chunks():
                    f.write(item)

            models.UserInfo.objects.filter(nid=request.session['user_info']['nid']).update(avatar=img_path)
            user_info_dict = request.session['user_info']
            user_info_dict['avatar'] = img_path
            request.session['user_info'] = user_info_dict
            print(request.session['user_info'])
            message['data'] = img_path
            message['status'] = True
        print(message)
    return HttpResponse(json.dumps(message))
view.py
{% extends 'backend_layout.html' %}
{% block css %}
    <style>
        .form-horizontal .control-label {
            padding-top: 7px;
            margin-bottom: 0;
            text-align: right;
        }
        .avatar-container{
            height: 200px;
            width: 200px;
            padding: 2px;
            border: 1px solid #dddddd;
            position: relative;
        }
        .avatar-container img{
            height: 198px;
            width: 198px;
            border: 0;
            overflow: hidden;
        }
        .avatar-container .text{
            text-align: center;
        }
        .avatar-container .img-file{
            top:0;
            left: 0;
            right: 0;
            bottom: 0;
            opacity: 0;
            position: absolute;
            z-index: 102;
        }
    </style>
{% endblock %}
{% block conent %}
    <ol class="breadcrumb">
        <li><a href="#">用户管理</a></li>
        <li class="active">用户信息</li>
    </ol>
    <div>

        <div class="row" style="position: relative;">
            <form class="form-horizontal" id="baseinfo_form">
                {% csrf_token %}
                <div class="col-xs-12">
                    <div class="form-group">
                        <label class="col-xs-2 control-label">用户名</label>

                        <div class="col-xs-5">
                            <p class="form-control-static">{{ request.session.user_info.username }}</p>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-xs-2 control-label">邮箱</label>

                        <div class="col-xs-5">
                            <p class="form-control-static">{{ request.session.user_info.email }}</p>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="nickname" class="col-xs-2 control-label">昵称</label>

                        <div class="col-xs-5">
                            {% if request.session.user_info.nickname %}
                                <input type="text" class="form-control" name="nickname" value="{{ request.session.user_info.nickname }}">

                            {% else %}
                                <input type="text" class="form-control" name="nickname" placeholder="请输入昵称">
                            {% endif %}
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="blogUrl" class="col-xs-2 control-label">博客地址</label>

                        <div class="col-xs-5">
                            {% if request.session.user_info.blog__site %}
                                <p class="form-control-static">{{ request.session.user_info.blog__site }}</p>
{#                                <p class="form-control"  readonly>{{ request.session.user_info.blog__site }}</p>#}
                            {% else %}
                            <input type="text" class="form-control" id="blogUrl" name="site"
                                   placeholder="如:wupeiqi,则个人博客为http://www.xxx.com/wupeiqi.html">
                            {% endif %}
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="blogTheme" class="col-xs-2 control-label">博客主题</label>

                        <div class="col-xs-5">
                            <select id="blogTheme" class="form-control" name="theme" >
                                {% for item in blogTheme %}
                                    {% if item.id  == request.session.user_info.blog__theme %}
                                        <option value="{{ item.id }}" selected="selected">{{ item.name }}</option>
                                    {% else %}
                                         <option value="{{ item.id }}">{{ item.name }}</option>
                                    {% endif%}
                                {% endfor %}
                            </select>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="blogTitle" class="col-xs-2 control-label">博客标题内容</label>

                        <div class="col-xs-8">
                            {% if request.session.user_info.blog__title %}
                                <textarea id="blogTitle" style="min-height: 100px" class="form-control"
                                        name="title">{{ request.session.user_info.blog__title  }}</textarea>
                            {% else %}
                                <textarea id="blogTitle" style="min-height: 100px" class="form-control"
                                          placeholder="来一杯鸡汤..." name="title"></textarea>
                            {% endif %}
                        </div>
                    </div>

                    <div class="form-group">
                        <div class="col-xs-offset-2 col-xs-10">
                            <button type="button" id='baseinfo_save' class="btn btn-primary">保 存</button>
                            <div class="error_message"></div>
                        </div>
                    </div>

                </div>
            </form>
            <div style="position: absolute;" class="col-xs-offset-7 col-xs-5">
                <div class="avatar-container">
                    <iframe style="display: none;" id="upload_iframe" name="upload_iframe"></iframe>
                    <form method="POST" action="/backend/upload-avatar.html" enctype="multipart/form-data"
                          target="upload_iframe" id="form1">
                        {% csrf_token %}
                        {% if request.session.user_info.avatar %}
                            <img id='previewImg' origin="/static/imgs/avatar/default.png" src='/{{ request.session.user_info.avatar }}'
                                style="border-radius: 50%;" >
                        {% else %}
                            <img id='previewImg' origin="/static/imgs/avatar/default.png" src="/static/imgs/avatar/default.png"
                                 style="border-radius: 50%;width: 198px;">
                        {% endif %}
                        <div class="text">点击图片更换(<a href="#">撤销</a>)</div>
                        <input id="avatarImg" name="avatar_img" type="file" class="img-file" onchange="ChangeAvatar();"/>
                    </form>
                </div>
            </div>
        </div>
    </div>
    <script src="/static/js/jquery-1.12.4.js"></script>
    <script>
    $('#baseinfo_save').click(function () {
        $.ajax({
            url:'/backend/base-info.html',
            type:'POST',
            data:$('#baseinfo_form').serialize(),
            dataType:'JSON',
            success:function (data) {

                if(data['status']){
                    console.log('ok');
                    location.reload()
                }else {
                    var error_message ='**' + data['error'];
                    ShowError(error_message)
                }
            },
            error:function () {
            }
        })
    });

    function ShowError(error) {
        $('.error_message').text(error)
    }

     function ChangeAvatar() {
{#            $('#avatarImg').change(function () {#}
{#                $(this).submit();#}
                $('#form1').submit();
                $('#upload_iframe').load(function () {
                    var iframeContents = this.contentWindow.document.body.innerText;
                    iframeContents = JSON.parse(iframeContents);
                    console.log(iframeContents);
                    if (iframeContents.status) {
                        $('#previewImg').attr('src', '/' + iframeContents.data +'?');
                    }
                })
        }
    </script>
{% endblock %}

{% block js %}

{% endblock %}
backend_base_info.html

里面包含了图片的点击上传,更新 非常重要

 

 

文章列表+组合检索

@check_login
def article(request,*args, **kwargs):
    """
    博主个人文章管理
    """
    blog_id = request.session['user_info']['blog__nid']
    condition = {}
    for k, v in kwargs.items():
        if v == '0':
            pass
        else:
            condition[k] = v
    condition['blog_id'] = blog_id
    data_count = models.Article.objects.filter(**condition).count()
    page = Pagination(request.GET.get('p', 1), data_count)
    result = models.Article.objects.filter(**condition).order_by('-nid').only('nid', 'title','blog').select_related('blog')[page.start:page.end]
    page_str = page.page_str(reverse('article', kwargs=kwargs))
    category_list = models.Category.objects.filter(blog_id=blog_id).values('nid', 'title')
    type_list = map(lambda item: {'nid': item[0], 'title': item[1]}, models.Article.type_choices)
    kwargs['p'] = page.current_page
    return render(request,
                  'backend_article.html',
                  {'result': result,
                   'page_str': page_str,
                   'category_list': category_list,
                   'type_list': type_list,
                   'arg_dict': kwargs,
                   'data_count': data_count
                   }
                  )
views.py
{% extends 'backend_layout.html' %}
{% load search %}
{% block css %}
<style>
    .conditions a{
        display: inline-block;
        padding: 2px 5px;
        margin-left: 5px;
    }
    .conditions a.active{
        background-color: #b35215;
        color: #ffffff;
    }
</style>
{% endblock %}
{% block conent %}
    <ol class="breadcrumb" style="margin-bottom: 0;">
        <li><a href="#">文章管理</a></li>
        <li class="active">文章列表</li>
    </ol>
    <div>

        <div style="border: 1px dashed #dddddd;padding: 8px;border-left: 3px solid #337ab7;">
            <i class="fa fa-search" aria-hidden="true"></i> 搜索条件
        </div>
        <div style="padding: 10px">
            <div class="conditions row clearfix" style="margin: 0;padding: 8px 0;">
                <div class="col-xs-1" style="text-align: right">
                    {% category_all arg_dict %}
                </div>
                <div class="col-xs-11">
                    {% category_combine category_list arg_dict %}
                </div>
            </div>
            <div class="conditions row clearfix" style="margin: 0;padding: 8px 0;">
                <div class="col-xs-1" style="text-align: right">
                    {% article_type_all arg_dict %}
                </div>
                <div class="col-xs-11">
                    {% article_type_combine type_list arg_dict %}
                </div>
            </div>
        </div>
        <div class="clearfix"
             style="height: 36px;line-height: 35px;padding: 0 15px;border-top: 1px solid #dddddd;background-color: #f1f0f0">
            <i class="fa fa-table" aria-hidden="true"></i>
            搜索文章({{ data_count }}篇)
            <a target="_blank" href="/backend/add-article.html" class="right"
               style="display: inline-block;padding:0 10px;background-color: #428bca;color: #ffffff;">
                <i class="fa fa-plus-circle" aria-hidden="true"></i>
                创建新文章
            </a>
        </div>

        <table class="table table-bordered">
            <thead>
            <tr>
                <th>文章标题</th>
                <th>操作</th>
            </tr>
            </thead>
            <tbody>
            {% for row in result %}
                <tr nid="{{ row.nid }}">
                    <td><a href="/{{ row.blog.site }}/{{ row.nid }}.html">{{ row.title }}</a></td>
                    <td>
                        <a class="btn btn-danger btn-xs" href="/backend/del-article-{{ row.nid }}.html">
                            <i class="fa fa-times" aria-hidden="true"></i>
                            删除
                        </a>
                        |
                        <a class="btn btn-primary btn-xs" href="/backend/edit-article-{{ row.nid }}.html">
                            <i class="fa fa-pencil-square-o" aria-hidden="true"></i>
                            编辑
                        </a>
                    </td>
                </tr>
            {% endfor %}

            </tbody>
        </table>
        <div class="clearfix">
            <ul class="pagination right" style="margin-top: 0">
               {{ page_str }}
            </ul>
        </div>
    </div>


{% endblock %}

{% block js %}

{% endblock %}
backend_article.html

组合搜索-跳转

 

 

套路之新建文章和编辑文章

@check_login
def add_article(request):
    """
    添加文章
    """
    if request.method == 'GET':
        form = ArticleForm(request=request)
        return render(request, 'backend_add_article.html', {'form': form})
    elif request.method == 'POST':
        form = ArticleForm(request=request, data=request.POST)   # 传参request 当然也可以用之前自己的方式进行上传
        if form.is_valid():
            with transaction.atomic():                  # 固定用法,下面操作多表进行添加数据,如果有一个数据添加数据,那么其他刚刚添加的数据进行回滚
                #dict格式cleaned_data 包含的字段{'tags'  'article_type' 'category_id'  'summary'  'title'  'content'}
                print(form.cleaned_data)
                tags = form.cleaned_data.pop('tags')            # 移出 tags需要在第三张Article2Tag表上手动添加
                content = form.cleaned_data.pop('content')      # 移出 content需要在表ArticleDetail上手动添加、关联Article
                content = XSSFilter().process(content)           # 对content进行数据过滤  过滤到script等标签
                form.cleaned_data['blog_id'] = request.session['user_info']['blog__nid'] # 把博客id添加到cleande_data中

                # dict格式cleaned_data 包含的字段{ 'blog_id' 'article_type' 'category_id'  'summary'  'title'  }
                obj = models.Article.objects.create(**form.cleaned_data)        # Article表添加cleaned_data里的内容
                # ****直接关联obj
                models.ArticleDetail.objects.create(content=content, article=obj) # ArticleDetail添加数据、关联

            # 第一种写法
            #     tag_list = []           # 准备添加第三张Article2Tag表
            #     for tag_id in tags:     # tags ['1'] 是个列表
            #         tag_id = int(tag_id)
            #         tag_list.append(models.Article2Tag(article_id=obj.nid, tag_id=tag_id))
            #         # models.Article2Tag(article_id=obj.nid, tag_id=tag_id) 创建Article2Tag object对象
            #         print(tag_list)
            #     models.Article2Tag.objects.bulk_create(tag_list)   # 直接添加对象 类型[obj1,obj2]

            # 第二种写法
                for tag_id in tags:  # tags ['1'] 是个列表
                    tag_id = int(tag_id)
                    models.Article2Tag.objects.create(article_id=obj.nid,tag_id=tag_id)

            return redirect('/backend/article-0-0.html')
        else:
            return render(request, 'backend_add_article.html', {'form': form})
    else:
        return redirect('/')

def edit_article(request,nid):
    """
    编辑文章
    :param request:
    :return:
    """
    print('编辑')
    blog_id = request.session['user_info']['blog__nid']
    if request.method == 'GET':
        obj = models.Article.objects.filter(nid=nid, blog_id=blog_id).first()
        if not obj:
            return render(request, 'backend_no_article.html')
        tags = obj.tags.values_list('nid')
        if tags:
            tags = list(zip(*tags))[0]
        init_dict = {
            'nid': obj.nid,
            'title': obj.title,
            'summary': obj.summary,
            'category_id': obj.category_id,
            'article_type_id': obj.article_type_id,
            'content': obj.articledetail.content,
            'tags': tags
        }
        form = ArticleForm(request=request, data=init_dict)
        return render(request, 'backend_edit_article.html', {'form': form, 'nid': nid})
    elif request.method == 'POST':
        form = ArticleForm(request=request, data=request.POST)
        if form.is_valid():
            obj = models.Article.objects.filter(nid=nid, blog_id=blog_id).first()
            if not obj:
                return render(request, 'backend_no_article.html')
            with transaction.atomic():
                content = form.cleaned_data.pop('content')
                content = XSSFilter().process(content)
                tags = form.cleaned_data.pop('tags')
                models.Article.objects.filter(nid=obj.nid).update(**form.cleaned_data)
                models.ArticleDetail.objects.filter(article=obj).update(content=content)
                models.Article2Tag.objects.filter(article=obj).delete()
                tag_list = []
                for tag_id in tags:
                    tag_id = int(tag_id)
                    tag_list.append(models.Article2Tag(article_id=obj.nid, tag_id=tag_id))
                models.Article2Tag.objects.bulk_create(tag_list)
            return redirect('/backend/article-0-0.html')
        else:
            return render(request, 'backend_edit_article.html', {'form': form, 'nid': nid})
views.py
{% extends 'backend_layout.html' %}

{% block css %}
    <link rel="stylesheet" href="/static/plugins/kindeditor/themes/default/default.css"/>
    <style>
        .kind-content {
            width: 100%;
            min-height: 500px;
        }
    </style>
{% endblock %}

{% block conent %}
    <ol class="breadcrumb" style="margin-bottom: 0;">
        <li><a href="#">文章管理</a></li>
        <li class="active">添加文章</li>
    </ol>
    <div style="padding: 5px 8px;">
        <form method="POST" action="/backend/add-article.html" novalidate>
            {% csrf_token %}
            <div class="form-group">
                <label for="{{ form.title.id_for_label }}">标题 <span>{{ form.title.errors.0 }}</span></label>
                {{ form.title }}
            </div>
            <div class="form-group">
                <label for="summary">简介 <span>{{ form.summary.errors.0 }}</span></label>
                {{ form.summary }}
            </div>
            <div class="form-group">
                <label for="content">内容 <span>{{ form.content.errors.0 }}</span></label>
                {{ form.content }}
            </div>
            <div class="form-group">
                <label>类型 <span>{{ form.article_type.errors.0 }}</span></label>

                <div>
                    {{ form.article_type_id }}
                </div>

            </div>
            <div class="form-group">
                <label>分类 <span>{{ form.category_id.errors.0 }}</span></label>

                <div>
                    {{ form.category_id }}
                </div>
            </div>
            <div class="form-group">
                <label>标签 <span>{{ form.tags.errors.0 }}</span></label>

                <div>
                    {{ form.tags }}
                </div>
            </div>
            <div class="form-group">
                <input type="submit" class="btn btn-primary" value="保 存">
            </div>
        </form>
    </div>


{% endblock %}

{% block js %}
    <script charset="utf-8" src="/static/plugins/kindeditor/kindeditor-min.js"></script>
    <script charset="utf-8" src="/static/plugins/kindeditor/lang/zh_CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            var editor = K.create('textarea[name="content"]', {
                resizeType: 1
            });
        });
    </script>
{% endblock %}
backend_article.html
{% extends 'backend_layout.html' %}

{% block css %}
    <link rel="stylesheet" href="/static/plugins/kindeditor/themes/default/default.css"/>
    <style>
        .kind-content {
            width: 100%;
            min-height: 500px;
        }
    </style>
{% endblock %}

{% block conent %}
    <ol class="breadcrumb" style="margin-bottom: 0;">
        <li><a href="#">文章管理</a></li>
        <li class="active">修改文章</li>
    </ol>
    <div style="padding: 5px 8px;">
        <form method="POST" action="/backend/edit-article-{{ nid }}.html">
            {% csrf_token %}
            <div class="form-group">
                <label for="{{ form.title.id_for_label }}">标题 <span>{{ form.title.errors.0 }}</span></label>
                {{ form.title }}
            </div>
            <div class="form-group">
                <label for="summary">简介 <span>{{ form.summary.errors.0 }}</span></label>
                {{ form.summary }}
            </div>
            <div class="form-group">
                <label for="content">内容 <span>{{ form.content.errors.0 }}</span></label>
                {{ form.content }}
            </div>
            <div class="form-group">
                <label>类型 <span>{{ form.article_type.errors.0 }}</span></label>

                <div>
                    {{ form.article_type_id }}
                </div>

            </div>
            <div class="form-group">
                <label>分类 <span>{{ form.category_id.errors.0 }}</span></label>

                <div>
                    {{ form.category_id }}
                </div>
            </div>
            <div class="form-group">
                <label>标签 <span>{{ form.tags.errors.0 }}</span></label>

                <div>
                    {{ form.tags }}
                </div>
            </div>
            <div class="form-group">
                <input type="submit" class="btn btn-primary" value="保 存">
            </div>
        </form>
    </div>


{% endblock %}

{% block js %}
    <script charset="utf-8" src="/static/plugins/kindeditor/kindeditor-min.js"></script>
    <script charset="utf-8" src="/static/plugins/kindeditor/lang/zh_CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            var editor = K.create('textarea[name="content"]', {
                resizeType: 1
            });
        });
    </script>
{% endblock %}
backend_edit_article.html

最重要的是生成编辑框:

{% block js %}
    <script charset="utf-8" src="/static/plugins/kindeditor/kindeditor-min.js"></script>
    <script charset="utf-8" src="/static/plugins/kindeditor/lang/zh_CN.js"></script>
    <script>
        KindEditor.ready(function (K) {
            var editor = K.create('textarea[name="content"]', {
                resizeType: 1
            });
        });
    </script>
{% endblock %}

 

 

显示具体的文章页

from django.utils.safestring import mark_safe
def detail(request, site, nid):
    """
    博文详细页
    :param request:     http://127.0.0.1:8000/lzl/6.html
    :param site:        博客地址名
    :param nid:         文章ID
    :return:
    """
    # print(site,nid)
    blog_obj = models.Blog.objects.filter(site = site).prefetch_related('user').first()      # 当前用户对应的博客对象
    user_obj = blog_obj.user
    # print(user_obj)
    article_obj = models.Article.objects.filter(nid=nid,blog=blog_obj).first()  # 当前访问的文章对象
    content_obj = models.Comment.objects.filter(article=article_obj)

    if article_obj:
        detail_obj = models.ArticleDetail.objects.filter(article=article_obj).first()   # 访问文章的内容
        content = mark_safe(detail_obj.content)                                         # 标记安全
        return render(request, 'home_detail.html',{'article_obj':article_obj,
                                                        'content':content,
                                                        'user_obj':user_obj,
                                                   'blog_obj':blog_obj,
                                                   'content_obj':content_obj})
    else:

        return HttpResponse('文章不存在')


import json
@check_login
def update_comment(request):
    data = {'status':True,'message':None}
    user_id = request.session['user_info']['nid']
    print(user_id)
    if request.method == 'POST':
        print(request.POST)
        content = request.POST.get('content')
        article_id = request.POST.get('article_id')
        reply_id = request.POST.get('reply_id')
        nickname, new_content = content_handle(content)

        if reply_id:
            comment_obj = models.Comment.objects.filter(article_id=article_id,nid=reply_id).first()
            models.Comment.objects.create(user_id=user_id, content=new_content, article_id=article_id,reply=comment_obj)
        else:
            models.Comment.objects.create(user_id=user_id, content=content, article_id=article_id)
    return HttpResponse(json.dumps(data))


import re
def content_handle(content):            #对评论内容进行处理
    obj = re.match('@.+\s', content)
    if obj:
        print(obj.group())
        nickname = re.sub('@', '', obj.group()).strip()     # 用户名
        print(nickname)
        new_content = content.split(nickname)[1].strip()    # 评论内容
        print(new_content)
    else:
        nickname = None
        new_content = content
    return nickname,new_content
views.py
{% extends 'home_layout.html' %}

{% block css %}
    <link rel="stylesheet" href="/static/plugins/kindeditor/themes/default/default.css"/>
{% endblock %}

{% block content %}
    <div class="art-title">
        <a>{{ article_obj.title }}</a>
    </div>
    <div class="art-content">
        {{ content }}
    </div>
    <div class="art-recommend clearfix">
        <div class="recommend">
            <a href="#" class="up"
               style="margin: 5px 10px;display: inline-block;padding: 5px 15px;border: 1px solid #dddddd;text-align: center;">
                <i class="fa fa-thumbs-o-up fa-3" aria-hidden="true" style="font-size: 25px"></i>

                <div>0</div>
            </a>
            <a href="#" class="down"
               style="margin: 5px 30px 5px 10px;display: inline-block;padding: 5px 15px;border: 1px solid #dddddd;text-align: center;">
                <i class="fa fa-thumbs-o-down fa-3" aria-hidden="true" style="font-size: 25px"></i>

                <div>0</div>
            </a>
        </div>
    </div>
    <div class="art-tips clearfix">
        <div class="tips">
            <span class="ctime">{{ article_obj.create_time }}</span>
            <a class="author">{{ user_obj.nickname }}</a>
            <span class="comment-count">评论(0)</span>
            <span class="read-count">阅读(0)</span>
        </div>
    </div>
    <div id="AllanboltSignature">
        <div style="border-bottom: #e0e0e0 1px dashed; border-left: #e0e0e0 1px dashed; padding: 10px; font-family: 微软雅黑; font-size: 11px; border-top: #e0e0e0 1px dashed; border-right: #e0e0e0 1px dashed; "
             id="PSignature">
            <div style="float:left;width:70px;">
                <img src="/static/imgs/o_Warning.png" style="width:65px;height:65px">
            </div>
            <div style="float:left;padding-top:10px;">

                <div style="padding: 1px">作者:<a href="#" target="_blank">{{ user_obj.nickname }}</a></div>
                <div style="padding: 1px">出处:<a href="#" target="_blank">http://http://127.0.0.1:8000/{{ blog_obj.site }}/</a>
                </div>
                <div style="padding: 1px">本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接</div>
            </div>
            <div style="clear:both;"></div>
        </div>
    </div>
    <div class="art-comment">
        <div class="comment-title">
            评论列表
        </div>
        <div class="comment-list">
            {% for item in content_obj %}
            <div class="comment-item">
                <div class="reply-title clearfix">
                    <div class="user-info">
                        <span style="color: #399ab2;">#{{ forloop.counter }}</span>
                         <span>{{ item.create_time }}</span>
                        <span id="nickname"style="color: #399ab2;"> {{ item.user.nickname }}</span>
                    </div>
                    <div class="reply">
                        <a  nid="{{ item.nid }}" onclick="Reply(this);">回复</a>
                    </div>
                </div>
                <div class="reply-body" style="font-size: 12px;color:#888">
                    {% if item.reply_id %}
                        <div class="reply-user">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@{{ item.reply.user.nickname }}</div>
                    {% endif %}
                    <div class="content">
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ item.content }}
                    </div>
                    <div style="margin-top: 30px"> </div>
                </div>
            </div>
            {% endfor %}
        </div>
        <div class="comment-list-pager">
            <ul class="pagination">
                <li><a href="#">&laquo;</a></li>
                <li><a href="#">1</a></li>
                <li><a href="#">2</a></li>
                <li><a href="#">3</a></li>
                <li><a href="#">4</a></li>
                <li><a href="#">5</a></li>
                <li><a href="#">&raquo;</a></li>
            </ul>
        </div>
        <div class="comment-area">
            <div class="replay-comment-user"></div>
                <div class="reply-area" style="position: relative;">
                    {% if not request.session.user_info %}
                        <div style="text-align:center;line-height:200px;position: absolute;top:0;left:0;right:0;bottom: 0;background-color: rgba(255,255,255,.6)">
                            您需要登录后才可以回帖 <a href="/login.html">登录</a> | <a href="/register.html">立即注册</a>
                        </div>
                    {% endif %}
                    <textarea  name="content" style="width: 100%;height:200px;visibility:hidden;"></textarea>
                </div>
                <div class="reply-btn">
                    <span><span>21</span>/255字</span>
                    <a onclick="CommentSub();">发表回复</a>
                    <div id="reply_id"></div>
                </div>
            </div>
        </div>
{% endblock %}


{% block js %}
    <script charset="utf-8" src="/static/plugins/kindeditor/kindeditor-min.js"></script>
    <script charset="utf-8" src="/static/plugins/kindeditor/lang/zh_CN.js"></script>
    <script src="/static/js/jquery.cookie.js"></script>
    <script>
        var editor;
        KindEditor.ready(function (K) {
            editor = K.create('textarea[name="content"]', {
                resizeType: 1,
                allowPreviewEmoticons: false,
                allowImageUpload: false,
                items: [
                    'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold', 'italic', 'underline',
                    'removeformat', '|', 'justifyleft', 'justifycenter', 'justifyright', 'insertorderedlist',
                    'insertunorderedlist', '|', 'emoticons', 'image', 'link']
            });
        });

        function CommentSub() {
            var content =  $('iframe.ke-edit-iframe').contents().find('body').text();
            var reply_id = $('#reply_id').attr('reply_id');
            $.ajax({
                url:'/update_comment.html',
                type:'POST',
                data:{'content':content,'article_id':{{article_obj.nid}},'reply_id':reply_id},
                headers:{'X-CSRFtoken':$.cookie('csrftoken')},
                success:function (data) {
                    var data = JSON.parse(data);
                    if(data['status']){
                        location.reload()
                    }
                },error:function () {

                }

            })
        }

        function Reply(ths) {
            var name = $(ths).parent().prev().find('span#nickname').text();
            var nickname ='@'+name+ " \n\r ";
            console.log(nickname);
            $('iframe.ke-edit-iframe').contents().find('body').text(nickname);
            var nid = $(ths).attr('nid');
            console.log(nid);
            $('#reply_id').attr('reply_id',nid);
            console.log($('#reply_id')[0])
        }

    </script>
{% endblock %}
home_detail.html

最重要的是评论功能!...

  

 

 

 

代码-》跳转

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM