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