BBS項目

settings.pycss

"""
Django settings for bbs project.

Generated by 'django-admin startproject' using Django 1.11.7.

For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'zcj3do(fmw&pec7g4%0kw0+-9+hnb)3c=--*zech#ek*lp_ius'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog.apps.BlogConfig',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',  # 在這個中間件以後 request.user
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'bbs.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'bbs.wsgi.application'


# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'bbs',
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '127.0.0.1',
        'PORT': 3306
    }
}


# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/

# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = False


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/

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


# Django用戶上傳的都叫media文件
MEDIA_URL = "/media/"
# media配置,用戶上傳的文件都默認放在這個文件夾下
MEDIA_ROOT = os.path.join(BASE_DIR, "media")

# 告訴Django項目用哪張表作認證
AUTH_USER_MODEL = 'blog.UserInfo'


# 項目級別的日誌配置
BASE_LOG_DIR = os.path.join(BASE_DIR, "log")

LOGGING = {
    'version': 1,  # 保留的參數,默認是1
    'disable_existing_loggers': False,  # 是否禁用已經存在的logger示例,不由用
    'formatters': {
        'standard': {  # 定義一個標準的日誌格式化
            'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'
                      '[%(levelname)s][%(message)s]'
        },
        'simple': {  # 定義一個簡單的日誌格式化
            'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
        },
        'collect': {  # 定義一個特殊的日誌格式化
            'format': '%(message)s'
        }
    },
    # 過濾器
    'filters': {
        # 只有在deubg=True的過濾器
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    # 處理器
    'handlers': {
        # 定義一個專門往終端打印日誌的控制器
        'console': {
            'level': 'DEBUG',
            'filters': ['require_debug_true'],  # 只有在Django debug爲True時纔在屏幕打印日誌
            'class': 'logging.StreamHandler',  # 使用什麼類去處理日誌流
            'formatter': 'simple'
        },
        # 定義一個默認的日誌處理器
        'default': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自動切
            'filename': os.path.join(BASE_LOG_DIR, "bbs_info.log"),  # 日誌文件
            'maxBytes': 1024 * 1024 * 50,  # 日誌大小 50M
            'backupCount': 5,  # 日誌文件備份的數量
            'formatter': 'standard',
            'encoding': 'utf-8',
        },
        # 定義一個專門及錯誤日誌的處理器
        'error': {
            'level': 'ERROR',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自動切
            'filename': os.path.join(BASE_LOG_DIR, "bbs_err.log"),  # 日誌文件
            'maxBytes': 1024 * 1024 * 50,  # 日誌大小 50M
            'backupCount': 5,
            'formatter': 'standard',
            'encoding': 'utf-8',
        },
    },


    'loggers': {
       # 默認的logger應用以下配置
        '': {
            'handlers': ['default', 'console', 'error'],  # 上線以後能夠把'console'移除
            'level': 'DEBUG',
            'propagate': True,  # 若是有父級的logger示例,表示要不要向上傳遞日誌流
        }
    },
}
View Code

 models.pyhtml

 

from django.db import models

# Create your models here.

from django.contrib.auth.models import AbstractUser


class UserInfo(AbstractUser):
    """
    用戶信息表
    """
    nid = models.AutoField(primary_key=True)
    phone = models.CharField(max_length=11, null=True, unique=True)
    avatar = models.FileField(upload_to="avatars/", default="avatars/default.png", verbose_name="頭像")
    create_time = models.DateTimeField(auto_now_add=True)

    blog = models.OneToOneField(to="Blog", to_field="nid", null=True)

    def __str__(self):
        return self.username

    class Meta:
        verbose_name = "用戶"
        verbose_name_plural = verbose_name


class Blog(models.Model):
    """
    博客信息
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)  # 我的博客標題
    site = models.CharField(max_length=32, unique=True)  # 我的博客後綴
    theme = models.CharField(max_length=32)  # 博客主題

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "blog站點"
        verbose_name_plural = verbose_name


class Category(models.Model):
    """
    我的博客文章分類
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)  # 分類標題
    blog = models.ForeignKey(to="Blog", to_field="nid")  # 外鍵關聯博客,一個博客站點能夠有多個分類

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "文章分類"
        verbose_name_plural = verbose_name


class Tag(models.Model):
    """
    標籤
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)  # 標籤名
    blog = models.ForeignKey(to="Blog", to_field="nid")  # 所屬博客

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "標籤"
        verbose_name_plural = verbose_name


class Article(models.Model):
    """
    文章
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=50, verbose_name="文章標題")  # 文章標題
    desc = models.CharField(max_length=255)  # 文章描述
    create_time = models.DateTimeField(auto_now_add=True)  # 建立時間  --> datetime()

    # 評論數
    comment_count = models.IntegerField(verbose_name="評論數", default=0)
    # 點贊數
    up_count = models.IntegerField(verbose_name="點贊數", default=0)
    #
    down_count = models.IntegerField(verbose_name="踩數", default=0)

    category = models.ForeignKey(to="Category", to_field="nid", null=True)
    user = models.ForeignKey(to="UserInfo", to_field="nid")
    tags = models.ManyToManyField(  # 中介模型
        to="Tag",
        through="Article2Tag",
        through_fields=("article", "tag"),  # 注意順序!!!
    )

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "文章"
        verbose_name_plural = verbose_name


class ArticleDetail(models.Model):
    """
    文章詳情表
    """
    nid = models.AutoField(primary_key=True)
    content = models.TextField()
    article = models.OneToOneField(to="Article", to_field="nid")

    class Meta:
        verbose_name = "文章詳情"
        verbose_name_plural = verbose_name


class Article2Tag(models.Model):
    """
    文章和標籤的多對多關係表
    """
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(to="Article", to_field="nid")
    tag = models.ForeignKey(to="Tag", to_field="nid")

    def __str__(self):
        return "{}-{}".format(self.article.title, self.tag.title)

    class Meta:
        unique_together = (("article", "tag"),)
        verbose_name = "文章-標籤"
        verbose_name_plural = verbose_name


class ArticleUpDown(models.Model):
    """
    點贊表
    """
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey(to="UserInfo", null=True)
    article = models.ForeignKey(to="Article", null=True)
    is_up = models.BooleanField(default=True)

    class Meta:
        unique_together = (("article", "user"),)
        verbose_name = "文章點贊"
        verbose_name_plural = verbose_name


class Comment(models.Model):
    """
    評論表
    """
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(to="Article", to_field="nid")
    user = models.ForeignKey(to="UserInfo", to_field="nid")
    content = models.CharField(max_length=255)  # 評論內容
    create_time = models.DateTimeField(auto_now_add=True)
    parent_comment = models.ForeignKey("self", null=True, blank=True)  # blank=True 在django admin裏面能夠不填

    def __str__(self):
        return self.content

    class Meta:
        verbose_name = "評論"
        verbose_name_plural = verbose_name
View Code

 

froms.pypython

 

from django import forms
from django.core.exceptions import ValidationError
from blog import models


# 定義一個註冊的form類
class RegForm(forms.Form):
    username = forms.CharField(
        max_length=16,
        label="用戶名",
        error_messages={
            "max_length": "用戶名最長16位",
            "required": "用戶名不能爲空",
        },

        widget=forms.widgets.TextInput(
            attrs={"class": "form-control"},
        )
    )

    password = forms.CharField(
        min_length=6,
        label="密碼",
        widget=forms.widgets.PasswordInput(
            attrs={"class": "form-control"},
            render_value=True,
        ),
        error_messages={
            "min_length": "密碼至少要6位!",
            "required": "密碼不能爲空",
        }
    )

    re_password = forms.CharField(
        min_length=6,
        label="確認密碼",
        widget=forms.widgets.PasswordInput(
            attrs={"class": "form-control"},
            render_value=True,
        ),
        error_messages={
            "min_length": "確認密碼至少要6位!",
            "required": "確認密碼不能爲空",
        }
    )

    email = forms.EmailField(
        label="郵箱",
        widget=forms.widgets.EmailInput(
            attrs={"class": "form-control"},

        ),
        error_messages={
            "invalid": "郵箱格式不正確!",
            "required": "郵箱不能爲空",
        }
    )

    # 重寫username字段的局部鉤子
    def clean_username(self):
        username = self.cleaned_data.get("username")
        is_exist = models.UserInfo.objects.filter(username=username)
        if is_exist:
            # 表示用戶名已註冊
            self.add_error("username", ValidationError("用戶名已存在"))
        else:
            return username

    # 重寫email字段的局部鉤子
    def clean_email(self):
        email = self.cleaned_data.get("email")
        is_exist = models.UserInfo.objects.filter(email=email)
        if is_exist:
            # 表示郵箱已註冊
            self.add_error("email", ValidationError("郵箱已被註冊"))
        else:
            return email

    # 重寫全局的鉤子函數,對確認密碼作校驗
    def clean(self):
        password = self.cleaned_data.get("password")
        re_password = self.cleaned_data.get("re_password")

        if re_password and re_password != password:
            self.add_error("re_password", ValidationError("兩次密碼不一致"))

        else:

            return self.cleaned_data
View Code

 

urls.pymysql

 

from django.conf.urls import url, include
from django.contrib import admin
from blog import views
from django.views.static import serve
from django.conf import settings
from blog import urls as blog_urls

urlpatterns = [
    url(r'^admin/', admin.site.urls),

    url(r'^login/', views.login),
    url(r'^logout/', views.logout),
    url(r'^reg/', views.register),

    url(r'^index/', views.index),
    # 將全部以blog開頭的url都交給app下面的urls.py來處理
    url(r'^blog/', include(blog_urls)),


    url(r'^get_valid_img.png/', views.get_valid_img),

    # 極驗滑動驗證碼 獲取驗證碼的url
    url(r'^pc-geetest/register', views.get_geetest),
    # 專門用來校驗用戶名是否已被註冊的接口
    url(r'^check_username_exist/$', views.check_username_exist),

    # media相關的路由設置
    url(r'^media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),

    url(r'^upload/', views.upload),

    url(r'^$', views.index)
]
View Code

 

app中的urls.pyjquery

from django.conf.urls import url
from blog import views


urlpatterns = [


    url(r"backend/add_article/",views.add_article),

    url(r"up_down/",views.up_down),
    url(r"comment/",views.comment),
    url(r"comment_tree/(\d+)/",views.comment_tree),
    # /blog/xiaohei/tag/python
    # /blog/xiaohei/category/技術
    # /blog/xiaohei/archive/2018-05
    # url(r'(\w+)/tag/(\w+)', views.tag),
    # url(r'(\w+)/category/(\w+)', views.category),
    # url(r'(\w+)/archive/(.+)', views.archive),

    # 三和一 URL

    url(r'(\w+)/(tag|category|archive)/(.+)/', views.home),  # home(request, username, tag, 'python')

    url(r'(\w+)/article/(\d+)/$', views.article_detail),  # 文章詳情  article_detail(request, xiaohei, 1)

    url(r'(\w+)', views.home),  # home(request, username)

]
View Code

views.pygit

from django.shortcuts import render, redirect, HttpResponse
from django.http import JsonResponse
from django.contrib import auth
from geetest import GeetestLib
from blog import forms, models
from django.db.models import Count
import logging

# 生成一個logger實例,專門用來記錄日誌
logger = logging.getLogger(__name__)
# logger_s10 = logging.getLogger("collect")

# Create your views here.

# VALID_CODE = ""


# 本身生成驗證碼的登陸
# def login(request):
#     # if request.is_ajax():  # 若是是AJAX請求
#     if request.method == "POST":
#         # 初始化一個給AJAX返回的數據
#         ret = {"status": 0, "msg": ""}
#         # 從提交過來的數據中 取到用戶名和密碼
#         username = request.POST.get("username")
#         pwd = request.POST.get("password")
#         valid_code = request.POST.get("valid_code")  # 獲取用戶填寫的驗證碼
#         print(valid_code)
#         print("用戶輸入的驗證碼".center(120, "="))
#         if valid_code and valid_code.upper() == request.session.get("valid_code", "").upper():
#             # 驗證碼正確
#             # 利用auth模塊作用戶名和密碼的校驗
#             user = auth.authenticate(username=username, password=pwd)
#             if user:
#                 # 用戶名密碼正確
#                 # 給用戶作登陸
#                 auth.login(request, user)
#                 ret["msg"] = "/index/"
#             else:
#                 # 用戶名密碼錯誤
#                 ret["status"] = 1
#                 ret["msg"] = "用戶名或密碼錯誤!"
#         else:
#             ret["status"] = 1
#             ret["msg"] = "驗證碼錯誤"
#
#         return JsonResponse(ret)
#     return render(request, "login.html")


# 使用極驗滑動驗證碼的登陸

def login(request):
    # if request.is_ajax():  # 若是是AJAX請求
    if request.method == "POST":
        # 初始化一個給AJAX返回的數據
        ret = {"status": 0, "msg": ""}
        # 從提交過來的數據中 取到用戶名和密碼
        username = request.POST.get("username")
        pwd = request.POST.get("password")
        # 獲取極驗 滑動驗證碼相關的參數
        gt = GeetestLib(pc_geetest_id, pc_geetest_key)
        challenge = request.POST.get(gt.FN_CHALLENGE, '')
        validate = request.POST.get(gt.FN_VALIDATE, '')
        seccode = request.POST.get(gt.FN_SECCODE, '')
        status = request.session[gt.GT_STATUS_SESSION_KEY]
        user_id = request.session["user_id"]

        if status:
            result = gt.success_validate(challenge, validate, seccode, user_id)
        else:
            result = gt.failback_validate(challenge, validate, seccode)
        if result:
            # 驗證碼正確
            # 利用auth模塊作用戶名和密碼的校驗
            user = auth.authenticate(username=username, password=pwd)
            if user:
                # 用戶名密碼正確
                # 給用戶作登陸
                auth.login(request, user)  # 將登陸用戶賦值給 request.user
                ret["msg"] = "/index/"
            else:
                # 用戶名密碼錯誤
                ret["status"] = 1
                ret["msg"] = "用戶名或密碼錯誤!"
        else:
            ret["status"] = 1
            ret["msg"] = "驗證碼錯誤"

        return JsonResponse(ret)
    return render(request, "login2.html")


# 註銷
def logout(request):
    auth.logout(request)
    return redirect("/index/")


def index(request):
    # 查詢全部的文章列表
    article_list = models.Article.objects.all()


    return render(request, "index.html", {"article_list": article_list})


# 獲取驗證碼圖片的視圖
def get_valid_img(request):
    # with open("valid_code.png", "rb") as f:
    #     data = f.read()
    # 本身生成一個圖片
    from PIL import Image, ImageDraw, ImageFont
    import random

    # 獲取隨機顏色的函數
    def get_random_color():
        return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)

    # 生成一個圖片對象
    img_obj = Image.new(
        'RGB',
        (220, 35),
        get_random_color()
    )
    # 在生成的圖片上寫字符
    # 生成一個圖片畫筆對象
    draw_obj = ImageDraw.Draw(img_obj)
    # 加載字體文件, 獲得一個字體對象
    font_obj = ImageFont.truetype("static/font/kumo.ttf", 28)
    # 開始生成隨機字符串而且寫到圖片上
    tmp_list = []
    for i in range(5):
        u = chr(random.randint(65, 90))  # 生成大寫字母
        l = chr(random.randint(97, 122))  # 生成小寫字母
        n = str(random.randint(0, 9))  # 生成數字,注意要轉換成字符串類型

        tmp = random.choice([u, l, n])
        tmp_list.append(tmp)
        draw_obj.text((20+40*i, 0), tmp, fill=get_random_color(), font=font_obj)

    print("".join(tmp_list))
    print("生成的驗證碼".center(120, "="))
    # 不能保存到全局變量
    # global VALID_CODE
    # VALID_CODE = "".join(tmp_list)

    # 保存到session
    request.session["valid_code"] = "".join(tmp_list)
    # 加干擾線
    # width = 220  # 圖片寬度(防止越界)
    # height = 35
    # for i in range(5):
    #     x1 = random.randint(0, width)
    #     x2 = random.randint(0, width)
    #     y1 = random.randint(0, height)
    #     y2 = random.randint(0, height)
    #     draw_obj.line((x1, y1, x2, y2), fill=get_random_color())
    #
    # # 加干擾點
    # for i in range(40):
    #     draw_obj.point((random.randint(0, width), random.randint(0, height)), fill=get_random_color())
    #     x = random.randint(0, width)
    #     y = random.randint(0, height)
    #     draw_obj.arc((x, y, x+4, y+4), 0, 90, fill=get_random_color())

    # 將生成的圖片保存在磁盤上
    # with open("s10.png", "wb") as f:
    #     img_obj.save(f, "png")
    # # 把剛纔生成的圖片返回給頁面
    # with open("s10.png", "rb") as f:
    #     data = f.read()

    # 不須要在硬盤上保存文件,直接在內存中加載就能夠
    from io import BytesIO
    io_obj = BytesIO()
    # 將生成的圖片數據保存在io對象中
    img_obj.save(io_obj, "png")
    # 從io對象裏面取上一步保存的數據
    data = io_obj.getvalue()
    return HttpResponse(data)


# 請在官網申請ID使用,示例ID不可以使用
pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c"
pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4"


# 處理極驗 獲取驗證碼的視圖
def get_geetest(request):
    user_id = 'test'
    gt = GeetestLib(pc_geetest_id, pc_geetest_key)
    status = gt.pre_process(user_id)
    request.session[gt.GT_STATUS_SESSION_KEY] = status
    request.session["user_id"] = user_id
    response_str = gt.get_response_str()
    return HttpResponse(response_str)


# 註冊的視圖函數
def register(request):
    if request.method == "POST":
        print(request.POST)
        print("=" * 120)
        ret = {"status": 0, "msg": ""}
        form_obj = forms.RegForm(request.POST)
        print(request.POST)
        # 幫我作校驗
        if form_obj.is_valid():

            # 校驗經過,去數據庫建立一個新的用戶
            form_obj.cleaned_data.pop("re_password")
            avatar_img = request.FILES.get("avatar")
            models.UserInfo.objects.create_user(**form_obj.cleaned_data, avatar=avatar_img)
            ret["msg"] = "/index/"
            return JsonResponse(ret)
        else:
            print(form_obj.errors)
            ret["status"] = 1
            ret["msg"] = form_obj.errors
            print(ret)
            print("=" * 120)
            return JsonResponse(ret)
    # 生成一個form對象
    form_obj = forms.RegForm()
    print(form_obj.fields)
    return render(request, "register.html", {"form_obj": form_obj})
    # return render(request, "form_test.html", {"form_obj": form_obj})


# 校驗用戶名是否已被註冊
def check_username_exist(request):
    ret = {"status": 0, "msg": ""}
    username = request.GET.get("username")
    print(username)
    is_exist = models.UserInfo.objects.filter(username=username)
    if is_exist:
        ret["status"] = 1
        ret["msg"] = "用戶名已被註冊!"
    return JsonResponse(ret)


# 我的博客主頁
def home(request, username, *args):
    logger.debug("home視圖獲取到用戶名:{}".format(username))
    # 去UserInfo表裏把用戶對象取出來
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        logger.warning("又有人訪問不存在頁面了...")
        return HttpResponse("404")
    # 若是用戶存在須要將TA寫的全部文章找出來
    blog = user.blog
    if not args:
        logger.debug("args沒有接收到參數,默認走的是用戶的我的博客頁面!")
        # 個人文章列表
        article_list = models.Article.objects.filter(user=user)
        # 個人文章分類及每一個分類下文章數
        # 將個人文章按照個人分類分組,並統計出每一個分類下面的文章數
        # category_list = models.Category.objects.filter(blog=blog)
        # category_list = models.Category.objects.filter(blog=blog).annotate(c=Count("article")).values("title", "c")
        # # [{'title': '技術', 'c': 2}, {'title': '生活', 'c': 1}, {'title': 'LOL', 'c': 1}]
        # # 統計當前站點下有哪一些標籤,而且按標籤統計出文章數
        # tag_list = models.Tag.objects.filter(blog=blog).annotate(c=Count("article")).values("title", "c")
        #
        # # 按日期歸檔
        # archive_list = models.Article.objects.filter(user=user).extra(
        #     select={"archive_ym": "date_format(create_time,'%%Y-%%m')"}
        # ).values("archive_ym").annotate(c=Count("nid")).values("archive_ym", "c")
    else:
        logger.debug(args)
        logger.debug("------------------------------")
        # 表示按照文章的分類或tag或日期歸檔來查詢
        # args = ("category", "技術")
        # article_list = models.Article.objects.filter(user=user).filter(category__title="技術")
        if args[0] == "category":
            article_list = models.Article.objects.filter(user=user).filter(category__title=args[1])
        elif args[0] == "tag":
            article_list = models.Article.objects.filter(user=user).filter(tags__title=args[1])
        else:
            # 按照日期歸檔
            try:
                year, month = args[1].split("-")
                logger.debug("分割獲得參數year:{}, month:{}".format(year, month))
                # logger_s10.info("獲得年和月的參數啦!!!!")
                logger.debug("************************")
                article_list = models.Article.objects.filter(user=user).filter(
                    create_time__year=year, create_time__month=month
                )
            except Exception as e:
                logger.warning("請求訪問的日期歸檔格式不正確!!!")
                logger.warning((str(e)))
                return HttpResponse("404")
    return render(request, "home.html", {
        "username": username,
        "blog": blog,
        "article_list": article_list,
    })


def get_left_menu(username):
    user = models.UserInfo.objects.filter(username=username).first()
    blog = user.blog
    category_list = models.Category.objects.filter(blog=blog).annotate(c=Count("article")).values("title", "c")
    tag_list = models.Tag.objects.filter(blog=blog).annotate(c=Count("article")).values("title", "c")
    # 按日期歸檔
    archive_list = models.Article.objects.filter(user=user).extra(
        select={"archive_ym": "date_format(create_time,'%%Y-%%m')"}
    ).values("archive_ym").annotate(c=Count("nid")).values("archive_ym", "c")

    return category_list, tag_list, archive_list


def article_detail(request, username, pk):
    """
    :param username: 被訪問的blog的用戶名
    :param pk: 訪問的文章的主鍵id值
    :return:
    """
    user = models.UserInfo.objects.filter(username=username).first()
    if not user:
        return HttpResponse("404")
    blog = user.blog
    # 找到當前的文章
    article_obj = models.Article.objects.filter(pk=pk).first()

    # 全部評論列表

    comment_list=models.Comment.objects.filter(article_id=pk)


    return render(
        request,
        "article_detail.html",
        {
            "username": username,
            "article": article_obj,
            "blog": blog,
            "comment_list":comment_list
         }
    )




import json

from django.db.models import F

def up_down(request):
    print(request.POST)
    article_id=request.POST.get('article_id')
    is_up=json.loads(request.POST.get('is_up'))
    user=request.user
    response={"state":True}
    print("is_up",is_up)
    try:
        models.ArticleUpDown.objects.create(user=user,article_id=article_id,is_up=is_up)
        models.Article.objects.filter(pk=article_id).update(up_count=F("up_count")+1)

    except Exception as e:
        response["state"]=False
        response["fisrt_action"]=models.ArticleUpDown.objects.filter(user=user,article_id=article_id).first().is_up




    return JsonResponse(response)
    #return HttpResponse(json.dumps(response))












def comment(request):

    print(request.POST)

    pid=request.POST.get("pid")
    article_id=request.POST.get("article_id")
    content=request.POST.get("content")
    user_pk=request.user.pk
    response={}
    if not pid:  #根評論
        comment_obj=models.Comment.objects.create(article_id=article_id,user_id=user_pk,content=content)
    else:
        comment_obj=models.Comment.objects.create(article_id=article_id,user_id=user_pk,content=content,parent_comment_id=pid)



    response["create_time"]=comment_obj.create_time.strftime("%Y-%m-%d")
    response["content"]=comment_obj.content
    response["username"]=comment_obj.user.username

    return JsonResponse(response)




def comment_tree(request,article_id):

    ret=list(models.Comment.objects.filter(article_id=article_id).values("pk","content","parent_comment_id"))
    print(ret)
    return JsonResponse(ret,safe=False)




def add_article(request):

    if request.method=="POST":
        title=request.POST.get('title')
        article_content=request.POST.get('article_content')
        user=request.user

        from bs4 import BeautifulSoup

        bs=BeautifulSoup(article_content,"html.parser")
        desc=bs.text[0:150]+"..."


        # 過濾非法標籤
        for tag in bs.find_all():

            print(tag.name)

            if tag.name in ["script", "link"]:
                tag.decompose()

        article_obj=models.Article.objects.create(user=user,title=title,desc=desc)
        models.ArticleDetail.objects.create(content=str(bs),article=article_obj)


        return HttpResponse("添加成功")




    return render(request,"add_article.html")



from bbs import settings
import os,json
def upload(request):
    print(request.FILES)
    obj = request.FILES.get("upload_img")

    print("name",obj.name)

    path=os.path.join(settings.MEDIA_ROOT,"add_article_img",obj.name)

    with open(path,"wb") as f:
        for line in obj:
            f.write(line)


    res={
        "error":0,
        "url":"/media/add_article_img/"+obj.name
    }


    return HttpResponse(json.dumps(res))
View Code

my_tags.pyajax

from django import template
from blog import models
from django.db.models import Count

register = template.Library()


@register.inclusion_tag("left_menu.html")
def get_left_menu(username):
    user = models.UserInfo.objects.filter(username=username).first()
    blog = user.blog
    # 查詢文章分類及對應的文章數
    category_list = models.Category.objects.filter(blog=blog).annotate(c=Count("article")).values("title", "c")
    # 查文章標籤及對應的文章數
    tag_list = models.Tag.objects.filter(blog=blog).annotate(c=Count("article")).values("title", "c")

    # 按日期歸檔
    archive_list = models.Article.objects.filter(user=user).extra(
        select={"archive_ym": "date_format(create_time,'%%Y-%%m')"}
    ).values("archive_ym").annotate(c=Count("nid")).values("archive_ym", "c")

    return {
        "username": username,
        "category_list" :category_list,
        "tag_list": tag_list,
        "archive_list": archive_list
    }
View Code

 

 login.html 普通驗證碼sql

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>歡迎登陸</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/mystyle.css">
</head>
<body>

<div class="container">
    <div class="row">
        <form class="form-horizontal col-md-6 col-md-offset-3 login-form">
            {% csrf_token %}
            <div class="form-group">
                <label for="username" class="col-sm-2 control-label">用戶名</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" id="username" name="username" placeholder="用戶名">
                </div>
            </div>
            <div class="form-group">
                <label for="password" class="col-sm-2 control-label">密碼</label>
                <div class="col-sm-10">
                    <input type="password" class="form-control" id="password" name="password" placeholder="密碼">
                </div>
            </div>
            <div class="form-group">
                <label for="password" class="col-sm-2 control-label">驗證碼</label>
                <div class="col-sm-10">
                    <input type="text" name="valid_code" id="valid_code">
                    <img id="valid-img" class="valid-img" src="/get_valid_img.png?" alt="">
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-10">
                    <button type="button" class="btn btn-default" id="login-button">登陸</button>
                    <span class="login-error"></span>
                </div>
            </div>
        </form>
    </div>
</div>

<script src="/static/jquery-3.3.1.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
<script>
    $("#login-button").click(function () {
        // 1. 取到用戶填寫的用戶名和密碼 -> 取input框的值
        var username = $("#username").val();
        var password = $("#password").val();
        var valid_code = $("#valid_code").val();
        // 2. 用AJAX發送到服務端
        $.ajax({
            url: "/login/",
            type: "post",
            data: {
                "username": username,
                "password": password,
                "valid_code": valid_code,
                "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val()
            },
            success: function (data) {
                console.log(data);
                if (data.status){
                    // 有錯誤,在頁面上提示
                    $(".login-error").text(data.msg);
                }else {
                    // 登錄成功
                    location.href = data.msg;
                }
            }
        })
    });

    // 當input框獲取焦點時將以前的錯誤清空
    $("#username,#password").focus(function () {
        // 將以前的錯誤清空
        $(".login-error").text("");
    });

    // 點擊驗證碼圖片 刷新驗證碼
    $("#valid-img").click(function () {
        $(this)[0].src += "?";
    })
</script>
</body>
</html>
View Code

login2.html  極驗驗證碼數據庫

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>歡迎登陸</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/mystyle.css">
</head>
<body>

<div class="container">
    <div class="row">
        <form class="form-horizontal col-md-6 col-md-offset-3 login-form">
            {% csrf_token %}
            <div class="form-group">
                <label for="username" class="col-sm-2 control-label">用戶名</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" id="username" name="username" placeholder="用戶名">
                </div>
            </div>
            <div class="form-group">
                <label for="password" class="col-sm-2 control-label">密碼</label>
                <div class="col-sm-10">
                    <input type="password" class="form-control" id="password" name="password" placeholder="密碼">
                </div>
            </div>
            <div class="form-group">
                <!-- 放置極驗的滑動驗證碼 -->
                <div id="popup-captcha"></div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-10">
                    <button type="button" class="btn btn-default" id="login-button">登陸</button>
                    <span class="login-error"></span>
                </div>
            </div>
        </form>
    </div>
</div>

<script src="/static/jquery-3.3.1.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
<!-- 引入封裝了failback的接口--initGeetest -->
<script src="http://static.geetest.com/static/tools/gt.js"></script>
<script>

    // 極驗 發送登陸數據的
    var handlerPopup = function (captchaObj) {
        // 成功的回調
        captchaObj.onSuccess(function () {
            var validate = captchaObj.getValidate();
            // 1. 取到用戶填寫的用戶名和密碼 -> 取input框的值
            var username = $("#username").val();
            var password = $("#password").val();
            $.ajax({
                url: "/login/", // 進行二次驗證
                type: "post",
                dataType: "json",
                data: {
                    username: username,
                    password: password,
                    csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(),
                    geetest_challenge: validate.geetest_challenge,
                    geetest_validate: validate.geetest_validate,
                    geetest_seccode: validate.geetest_seccode
                },
                success: function (data) {
                    console.log(data);
                    if (data.status) {
                        // 有錯誤,在頁面上提示
                        $(".login-error").text(data.msg);
                    } else {
                        // 登錄成功
                        location.href = data.msg;
                    }
                }
            });
        });

         $("#login-button").click(function () {
            captchaObj.show();
        });
        // 將驗證碼加到id爲captcha的元素裏
        captchaObj.appendTo("#popup-captcha");
        // 更多接口參考:http://www.geetest.com/install/sections/idx-client-sdk.html
    };
    // 當input框獲取焦點時將以前的錯誤清空
    $("#username,#password").focus(function () {
        // 將以前的錯誤清空
        $(".login-error").text("");
    });

    // 驗證開始須要向網站主後臺獲取id,challenge,success(是否啓用failback)
    $.ajax({
        url: "/pc-geetest/register?t=" + (new Date()).getTime(), // 加隨機數防止緩存
        type: "get",
        dataType: "json",
        success: function (data) {
            // 使用initGeetest接口
            // 參數1:配置參數
            // 參數2:回調,回調的第一個參數驗證碼對象,以後可使用它作appendTo之類的事件
            initGeetest({
                gt: data.gt,
                challenge: data.challenge,
                product: "popup", // 產品形式,包括:float,embed,popup。注意只對PC版驗證碼有效
                offline: !data.success // 表示用戶後臺檢測極驗服務器是否宕機,通常不須要關注
                // 更多配置參數請參見:http://www.geetest.com/install/sections/idx-client-sdk.html#config
            }, handlerPopup);
        }
    })


</script>
</body>
</html>
View Code

register.htmldjango

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>歡迎註冊</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/mystyle.css">
</head>
<body>

<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <form novalidate autocomplete="off" action="/reg/" method="post" class="form-horizontal reg-form" enctype="multipart/form-data">
                {% csrf_token %}

                <div class="form-group">
                    <label for="{{ form_obj.username.id_for_label }}"
                           class="col-sm-2 control-label">{{ form_obj.username.label }}</label>
                    <div class="col-sm-8">
                        {{ form_obj.username }}
                        <span class="help-block">{{ form_obj.username.errors.0 }}</span>
                    </div>
                </div>

                <div class="form-group">
                    <label for="{{ form_obj.password.id_for_label }}"
                           class="col-sm-2 control-label">{{ form_obj.password.label }}</label>
                    <div class="col-sm-8">
                        {{ form_obj.password }}
                        <span class="help-block">{{ form_obj.password.errors.0 }}</span>
                    </div>
                </div>

                <div class="form-group">
                    <label for="{{ form_obj.re_password.id_for_label }}"
                           class="col-sm-2 control-label">{{ form_obj.re_password.label }}</label>
                    <div class="col-sm-8">
                        {{ form_obj.re_password }}
                        <span class="help-block">{{ form_obj.re_password.errors.0 }}</span>
                    </div>
                </div>

                <div class="form-group">
                    <label for="{{ form_obj.email.id_for_label }}"
                           class="col-sm-2 control-label">{{ form_obj.email.label }}</label>
                    <div class="col-sm-8">
                        {{ form_obj.email }}
                        <span class="help-block">{{ form_obj.email.errors.0 }}</span>
                    </div>
                </div>

                <div class="form-group">
                    <label
                            class="col-sm-2 control-label">頭像</label>
                    <div class="col-sm-8">
                        <label for="id_avatar"><img id="avatar-img" src="/static/img/default.png" alt=""></label>
                        <input accept="image/*" type="file" name="avatar" id="id_avatar" style="display: none">
                        <span class="help-block"></span>
                    </div>
                </div>

                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-10">
                        <button type="button" class="btn btn-success" id="reg-submit">註冊</button>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>


<script src="/static/jquery-3.3.1.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
<script>
    // 找到頭像的input標籤綁定change事件
    $("#id_avatar").change(function () {
        // 1. 建立一個讀取文件的對象
        var fileReader = new FileReader();
        // 取到當前選中的頭像文件
        // console.log(this.files[0]);
        // 讀取你選中的那個文件
        fileReader.readAsDataURL(this.files[0]);  // 讀取文件是須要時間的
        fileReader.onload = function () {
            // 2. 等上一步讀完文件以後才 把圖片加載到img標籤中
            $("#avatar-img").attr("src", fileReader.result);
        };
    });
    // AJAX提交註冊的數據
    $("#reg-submit").click(function () {
        // 取到用戶填寫的註冊數據,向後端發送AJAX請求
        var formData = new FormData();
        formData.append("username", $("#id_username").val());
        formData.append("password", $("#id_password").val());
        formData.append("re_password", $("#id_re_password").val());
        formData.append("email", $("#id_email").val());
        formData.append("avatar", $("#id_avatar")[0].files[0]);
        formData.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val());

        $.ajax({
            url: "/reg/",
            type: "post",
            processData: false,  // 告訴jQuery不要處理個人數據
            contentType: false,  // 告訴jQuery不要設置content類型
            data: formData,
            success:function (data) {
                if (data.status){
                    // 有錯誤就展現錯誤
                    // console.log(data.msg);
                    // 將報錯信息填寫到頁面上
                    $.each(data.msg, function (k,v) {
                        // console.log("id_"+k, v[0]);
                        // console.log($("#id_"+k));
                        $("#id_"+k).next("span").text(v[0]).parent().parent().addClass("has-error");
                    })

                }else {
                    // 沒有錯誤就跳轉到指定頁面
                    location.href = data.msg;
                }
            }
        })
    });

    // 將全部的input框綁定獲取焦點的事件,將全部的錯誤信息清空
    $("form input").focus(function () {
        $(this).next().text("").parent().parent().removeClass("has-error");
    });

    // 給username input框綁定一個失去焦點的事件,失去焦點以後就校驗用戶名是否已被註冊
    {#$("#id_username").blur(function () {#}
    $("#id_username").on("input", function () {
        // 取到用戶填寫的值
        var username = $(this).val();
        // 發請求
        $.ajax({
            url: "/check_username_exist/",
            type: "get",
            data: {"username": username},
            success: function (data) {
                if (data.status){
                    // 用戶名已被註冊
                    $("#id_username").next().text(data.msg).parent().parent().addClass("has-error");
                }
            }
        })
    })
</script>
</body>
</html>
View Code

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/fontawesome/css/font-awesome.min.css">
    <link rel="stylesheet" href="/static/mystyle.css">
</head>
<body>

<nav class="navbar navbar-inverse">
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">The Blog</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
                <li><a href="#">Link</a></li>
            </ul>
            <ul class="nav navbar-nav navbar-right">
                {% if request.user.username %}
                    <li><a href="#">{{ request.user.username }}</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                           aria-expanded="false">我的中心<span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="#">Action</a></li>
                            <li><a href="#">Another action</a></li>
                            <li><a href="#">Something else here</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="/logout/">註銷</a></li>
                        </ul>
                    </li>
                {% else %}
                    <li><a href="/login/">登陸</a></li>
                    <li><a href="/reg/">註冊</a></li>
                {% endif %}
            </ul>
        </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
</nav>

<!-- 主頁面 開始-->
<div class="container">
    <div class="row">
        <div class="col-md-2">
            <div class="panel panel-primary">
                <div class="panel-heading">左側廣告位一</div>
                <div class="panel-body">
                    Panel content
                </div>
            </div>
            <div class="panel panel-info">
                <div class="panel-heading">左側廣告位二</div>
                <div class="panel-body">
                    Panel content
                </div>
            </div>
        </div>
        <div class="col-md-8">
            <!-- 文章列表 開始 -->
            <div class="article-list">
                {% for article in article_list %}
                    <div class="article">
                        <h3><a href="/blog/{{ article.user.username }}/article/{{ article.pk }}/">{{ article.title }}</a></h3>
                        <div class="media">
                            <div class="media-left">
                                <a href="#">
                                    <img class="media-object author-img" src="/media/{{ article.user.avatar }}" alt="...">
                                </a>
                            </div>
                            <div class="media-body">
                                <p>{{ article.desc|safe }}</p>
                            </div>
                        </div>
                        <div class="article-footer">
                            <span><a href="/blog/{{ article.user.username }}/">{{ article.user.username }}</a></span>發佈於
                            <span>{{ article.create_time|date:'Y-m-d H:i:s' }}</span>
{#                            <span class="glyphicon glyphicon-comment">評論({{ article.comment_count }})</span>#}
{#                            <span class="glyphicon glyphicon-thumbs-up">點贊({{ article.up_count }})</span>#}
                            <span><i class="fa fa-commenting-o fa-fw" aria-hidden="true"></i>評論({{ article.comment_count }})</span>
                            <span><i class="fa fa-thumbs-o-up fa-fw" aria-hidden="true"></i>點贊({{ article.up_count }})</span>
                        </div>
                        <hr>
                    </div>
                {% endfor %}

            </div>
            <!-- 文章列表 結束-->

        </div>
        <div class="col-md-2">
            <div class="panel panel-primary">
                <div class="panel-heading">右側廣告位一</div>
                <div class="panel-body">
                    Panel content
                </div>
            </div>
            <div class="panel panel-info">
                <div class="panel-heading">右側廣告位二</div>
                <div class="panel-body">
                    Panel content
                </div>
            </div>
        </div>
    </div>
</div>
<!-- 主頁面 結束-->

<script src="/static/jquery-3.3.1.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>
View Code

home.html

{% extends 'base.html' %}


{% block page-main %}
 <!-- 我的博客列表 開始-->
        <div class="article-list">
            {% for article in article_list %}
                <div class="article">
                    <p class="article-title"><a href="/blog/{{ article.user.username }}/article/{{ article.pk }}/">{{ article.title }}</a></p>
                    <div class="media">
                        <div class="media-left">
                            <a href="#">
                                <img class="media-object author-img" src="/media/{{ article.user.avatar }}" alt="...">
                            </a>
                        </div>
                        <div class="media-body">
                            <p>{{ article.desc }}</p>
                        </div>
                    </div>
                    <div class="article-footer">
                        <span><a href="/blog/{{ article.user.username }}/">{{ article.user.username }}</a></span>發佈於
                        <span>{{ article.create_time|date:'Y-m-d H:i:s' }}</span>
                        {#                            <span class="glyphicon glyphicon-comment">評論({{ article.comment_count }})</span>#}
                        {#                            <span class="glyphicon glyphicon-thumbs-up">點贊({{ article.up_count }})</span>#}
                        <span><i class="fa fa-commenting-o fa-fw" aria-hidden="true"></i>評論({{ article.comment_count }})</span>
                        <span><i class="fa fa-thumbs-o-up fa-fw"
                                 aria-hidden="true"></i>點贊({{ article.up_count }})</span>
                    </div>
                    <hr>
                </div>
            {% endfor %}

        </div>
        <!-- 我的博客列表 結束-->
{% endblock %}
View Code

base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ blog.title }}</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/fontawesome/css/font-awesome.min.css">
    <link rel="stylesheet" href="/static/mystyle.css">
    <link rel="stylesheet" href="/static/theme/{{ blog.theme }}">
    <script src="/static/jquery-3.3.1.js"></script>
    <script src="/static/bootstrap/js/bootstrap.min.js"></script>
</head>
<body>

<div class="header">
    <p>{{ blog.title }}</p>
</div>

<div class="container">
    <div class="col-md-3">
            {% load my_tags %}

            {% get_left_menu username %}
        </div>
        <div class="col-md-8">
            {% block page-main %}

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

</body>
</html>
View Code

left_menu.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ blog.title }}</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/fontawesome/css/font-awesome.min.css">
    <link rel="stylesheet" href="/static/mystyle.css">
    <link rel="stylesheet" href="/static/theme/{{ blog.theme }}">
    <script src="/static/jquery-3.3.1.js"></script>
    <script src="/static/bootstrap/js/bootstrap.min.js"></script>
</head>
<body>

<div class="header">
    <p>{{ blog.title }}</p>
</div>

<div class="container">
    <div class="col-md-3">
            {% load my_tags %}

            {% get_left_menu username %}
        </div>
        <div class="col-md-8">
            {% block page-main %}

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

</body>
</html>
View Code

article.html

{% extends 'base.html' %}

{% block page-main %}

    <div class="article-detail">
        <h1>{{ article.title }}</h1>
        <p>{{ article.articledetail.content|safe }}</p>
    </div>

    <div class="poll clearfix">
        <div id="div_digg">
            <div class="diggit action">
                <span class="diggnum" id="digg_count">{{ article.up_count }}</span>
            </div>
            <div class="buryit action">
                <span class="burynum" id="bury_count">{{ article.down_count }}</span>
            </div>
            <div class="clear"></div>
            <div class="diggword" id="digg_tips" style="color: red;"></div>
        </div>

    </div>
    <p>評論樹</p>

    <div class="comment_tree">

    </div>
    <hr>
    <p>評論列表</p>
    <ul class="comment_list">
        {% for comment in comment_list %}
            <li class="list-group-item">
                <div>
                    <a href="">#{{ forloop.counter }}樓</a> &nbsp;&nbsp;
                    <span style="color: gray">{{ comment.create_time|date:"Y-m-d H:i" }}</span> &nbsp;&nbsp;
                    <a href=""><span>{{ comment.user.username }}</span></a>
                    <a class="pull-right reply_btn" username="{{ comment.user.username }}"
                       comment_pk="{{ comment.pk }}"><span>回覆</span></a>
                </div>
                {% if comment.parent_comment_id %}
                    <div class="pid_info well">
                        <p> {{ comment.parent_comment.user.username }}:
                            &nbsp;&nbsp;&nbsp;{{ comment.parent_comment.content }} </p>
                    </div>
                {% endif %}

                <div class="con">
                    <p>
                        {{ comment.content }}
                    </p>
                </div>
            </li>
        {% endfor %}
    </ul>

    {% if request.user.username %}
        <div class="div_comment">
            <p>暱稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                         value="{{ request.user.username }}"></p>
            <p>評論內容</p>
            <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
            <p>
                <button id="comment_btn">提交評論</button>
            </p>

        </div>
    {% else %}
        <a href="/login/">登陸</a>
    {% endif %}



    <script>
        // 獲取評論數據,展現評論樹結構
        $.ajax({
            url: "/blog/comment_tree/" + '{{ article.pk }}/',
            success: function (data) {
                console.log(data);

                $.each(data, function (index, comment_dict) {
                    var s = '<div class="comment_item" comment_id=' + comment_dict.pk + '> <span class="content">' + comment_dict.content + '</span> </div>'
                    if (comment_dict.parent_comment_id) {
                        // 子評論
                        var pid=comment_dict.parent_comment_id;
                        $("[comment_id="+pid+"]").append(s);
                    }
                    else {   //  根評論
                        $(".comment_tree").append(s);
                    }
                })

            }
        });


        // 提交評論
        var pid = "";
        $("#comment_btn").click(function () {

            var article_id = $(".info").attr("article_id");
            var content = $("#comment_content").val();
            if (pid) {
                var index = content.indexOf("\n");
                content = content.slice(index + 1)
            }


            $.ajax({
                url: "/blog/comment/",
                type: "post",
                data: {
                    article_id: article_id,
                    content: content,
                    pid: pid,
                    csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(),
                },
                success: function (data) {
                    console.log(data);
                    var create_time = data.create_time;
                    var content = data.content;
                    var username = data.username;


                    var comment_li = '<li class="list-group-item"><div><span style="color: gray">' + create_time + '</span> &nbsp;&nbsp; <a href=""><span>' + username + '</span></a></div> <div class="con"> <p> ' + content + ' </p> </div> </li>';

                    $(".comment_list").append(comment_li);

                    // 清空文本框
                    $("#comment_content").val('');
                    // 清空pid
                    pid = ""
                }
            })


        });


        // 回覆按鈕事件

        $(".list-group-item .reply_btn").click(function () {

            $("#comment_content").focus();

            var v = "@" + $(this).attr("username") + "\n";
            $("#comment_content").val(v);


            //pid賦值
            pid = $(this).attr("comment_pk")


        })

    </script>








    <div class="info" article_id="{{ article.pk }}" username="{{ request.user.username }}"></div>

    {% csrf_token %}

    <script src="/static/js/article_detail.js"></script>

{% endblock %}
View Code

add_article.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        * {
            margin: 0;
        }

        .header {
            width: 100%;
            height: 60px;
            background-color: #369;
        }

        .content {
            width: 80%;
            margin: 20px auto;
        }
    </style>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
     <script src="/static/jquery-3.3.1.js"></script>
</head>
<body>

<div class="header"></div>
<div class="content">
    <h3>添加文章</h3>
    <form action="" method="post">
        {% csrf_token %}
        <div>
            <label for="">文章標題</label>
            <input type="text" name="title" class="form-control" style="width: 200px">
        </div>

        <div>
            <p>內容(TinyMCE編輯器,支持拖放/粘貼上傳圖片) </p>
            <textarea name="article_content" id="article_content" cols="60" rows="20"></textarea>
        </div>
        <input type="submit" class="btn btn-info">
    </form>
</div>

<script charset="utf-8" src="/static/kindeditor/kindeditor-all.js"></script>
<script>
        KindEditor.ready(function(K) {
                window.editor = K.create('#article_content',{
                    width:"800",
                    height:"500px",
                    resizeType:0,
                    uploadJson:"/upload/",
                    extraFileUploadParams:{
                            csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val()
                    },
                    filePostName:"upload_img"

                });
        });
</script>

</body>
</html>
View Code
相關文章
相關標籤/搜索