Django快速开发
设置setting配置
AUTH_USER_MODEL="blog.UserInfo" # 继承基类需要配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'cnblog', # 要连接的数据库,连接前需要创建好 'USER':'root', # 连接数据库的用户名 'PASSWORD':'', # 连接数据库的密码 'HOST':'127.0.0.1', # 连接主机,默认本级 'PORT':3306 # 端口 默认3306 } } STATICFILES_DIRS=[ os.path.join(BASE_DIR,"static"), ] # 与用户上传相关的配置 MEDIA_ROOT=os.path.join(BASE_DIR,"media") MEDIA_URL="/media/" LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } } EMAIL_HOST = 'smtp.exmail.qq.com' # 如果是 163 改成 smtp.163.com EMAIL_PORT = 465 EMAIL_HOST_USER = '' # 帐号 EMAIL_HOST_PASSWORD = '' # 密码 # DEFAULT_FROM_EMAIL = EMAIL_HOST_USER EMAIL_USE_SSL = True LOGIN_URL="/login/"
创建表格
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) telephone = models.CharField(max_length=11, null=True, unique=True) avatar = models.FileField(upload_to='avatars/', default="avatars/default.png") create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.CASCADE) def __str__(self): return self.username class Blog(models.Model): """ 博客信息 """ nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name='个人博客标题', max_length=64) site_name = models.CharField(verbose_name='站点名称', max_length=64) theme = models.CharField(verbose_name='博客主题', max_length=32) def __str__(self): return self.title 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', on_delete=models.CASCADE) def __str__(self): return self.title 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', on_delete=models.CASCADE) def __str__(self): return self.title class Article(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=50, verbose_name='文章标题') desc = models.CharField(max_length=255, verbose_name='文章描述') create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) content = models.TextField() comment_count = models.IntegerField(default=0) up_count = models.IntegerField(default=0) down_count = models.IntegerField(default=0) user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid', on_delete=models.CASCADE) category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE) tags = models.ManyToManyField( to="Tag", through='Article2Tag', through_fields=('article', 'tag'), ) def __str__(self): return self.title class Article2Tag(models.Model): nid = models.AutoField(primary_key=True) article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid', on_delete=models.CASCADE) tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid', on_delete=models.CASCADE) class Meta: unique_together = [ ('article', 'tag'), ] def __str__(self): v = self.article.title + "---" + self.tag.title return v class ArticleUpDown(models.Model): """ 点赞表 """ nid = models.AutoField(primary_key=True) user = models.ForeignKey('UserInfo', null=True, on_delete=models.CASCADE) article = models.ForeignKey("Article", null=True, on_delete=models.CASCADE) is_up = models.BooleanField(default=True) class Meta: unique_together = [ ('article', 'user'), ] class Comment(models.Model): """ 评论表 """ nid = models.AutoField(primary_key=True) article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.CASCADE) user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.CASCADE) content = models.CharField(verbose_name='评论内容', max_length=255) create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) parent_comment = models.ForeignKey('self', null=True, on_delete=models.CASCADE) def __str__(self): return self.content
再启动我们的Django项目前,我们需要激活我们的mysql。然后,启动项目,会报错:no module named MySQLdb 。这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb 对于py3有很大问题,所以我们需要的驱动是PyMySQL 所以,我们只需要找到项目名文件下的init,在里面写入:
import pymysql
pymysql.install_as_MySQLdb(
注意2:确保配置文件中的INSTALLED_APPS中写入我们创建的app名称
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', "book" ]
最后通过两条数据库迁移命令即可在指定的数据库中创建表 :
python manage.py makemigrations python manage.py migrate
注意3:如果报错如下:
django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.3 or newer is required; you have 0.7.11.None
MySQLclient目前只支持到python3.4,因此如果使用的更高版本的python,需要修改如下:
通过查找路径C:\Programs\Python\Python36-32\Lib\site-packages\Django-2.0-py3.6.egg\django\db\backends\mysql 这个路径里的文件把
if version < (1, 3, 3): raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__ver
生成图片验证码(代码比较多但写个模块引入)
utils文件夹下 validCode.py
from PIL import Image, ImageDraw, ImageFont from io import BytesIO import random def get_random_color(): return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) def get_valid_code_img(request): img = Image.new("RGB", (270, 40), color=get_random_color()) draw = ImageDraw.Draw(img) kumo_font = ImageFont.truetype("static/font/kumo.ttf", size=32) valid_code_str = "" for i in range(5): random_num = str(random.randint(0, 9)) random_low_alpha = chr(random.randint(95, 122)) random_upper_alpha = chr(random.randint(65, 90)) random_char = random.choice([random_num, random_low_alpha, random_upper_alpha]) draw.text((i * 50 + 20, 5), random_char, get_random_color(), font=kumo_font) # 保存验证码字符串 valid_code_str += random_char # width=270 # height=40 # for i in range(10): # x1=random.randint(0,width) # x2=random.randint(0,width) # y1=random.randint(0,height) # y2=random.randint(0,height) # draw.line((x1,y1,x2,y2),fill=get_random_color()) # # for i in range(100): # draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color()) # x = random.randint(0, width) # y = random.randint(0, height) # draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color()) request.session["valid_code_str"] = valid_code_str # 保存 f = BytesIO() img.save(f, "png") data = f.getvalue() return data
(1) 基于用户认证组件和Ajax实现登录验证(图片验证码)
(2) 基于forms组件和Ajax实现注册功能
(3) 设计系统首页(文章列表渲染)
(4) 设计个人站点页面
(5) 文章详情页
(6) 实现文章点赞功能
(7) 实现文章的评论(树型评论)
---文章的评论
---评论的评论
(8) 富文本编辑框和防止xss攻击
from django.shortcuts import render, HttpResponse, redirect from django.contrib import auth from blog.Myforms import UserForm from blog.models import UserInfo from blog.utils import validCode from blog import models from django.db.models import Count import json from django.http import JsonResponse from django.db.models import F from django.db import transaction from django.contrib.auth.decorators import login_required import os from cnblog import settings def login(request): """ 登录视图函数: get请求响应页面 post(Ajax)请求响应字典 :param request: :return: """ if request.method == "POST": response = {"user": None, "msg": None} user = request.POST.get("user") pwd = request.POST.get("pwd") valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str") if valid_code.upper() == valid_code_str.upper(): user = auth.authenticate(username=user, password=pwd) if user: auth.login(request, user) # request.user== 当前登录对象 response["user"] = user.username else: response["msg"] = "用户名或者密码错误!" else: response["msg"] = "验证码错误!" return JsonResponse(response) return render(request, "login.html") def index(request): """ 系统首页 :param request: :return: """ article_list = models.Article.objects.all() return render(request, "index.html", {"article_list": article_list}) def logout(request): """ 注销视图 :param request: :return: """ auth.logout(request) # request.session.flush() return redirect("/login/") def get_valid_code_img(request): """ 基于PIL模块动态生成响应状态码图片 :param request: :return: """ img_data = validCode.get_valid_code_img(request) return HttpResponse(img_data) def register(request): """ 注册视图函数: get请求响应注册页面 post(Ajax)请求,校验字段,响应字典 :param request: :return: """ if request.is_ajax(): print(request.POST) form = UserForm(request.POST) response = {"user": None, "msg": None} if form.is_valid(): response["user"] = form.cleaned_data.get("user") # 生成一条用户纪录 user = form.cleaned_data.get("user") print("user", user) pwd = form.cleaned_data.get("pwd") email = form.cleaned_data.get("email") avatar_obj = request.FILES.get("avatar") extra = {} if avatar_obj: extra["avatar"] = avatar_obj UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra) else: print(form.cleaned_data) print(form.errors) response["msg"] = form.errors return JsonResponse(response) form = UserForm() return render(request, "register.html", {"form": form}) def home_site(request, username, **kwargs): """ 个人站点视图函数 :param request: :return: """ print("kwargs", kwargs) # 区分访问是的站点页面还是站点下的跳转页面 print("username", username) user = UserInfo.objects.filter(username=username).first() # 判断用户是否存在! if not user: return render(request, "not_found.html") # 查询当前站点对象 blog = user.blog # 1 当前用户或者当前站点对应所有文章 # 基于对象查询 # article_list=user.article_set.all() # 基于 __ article_list = models.Article.objects.filter(user=user) if kwargs: condition = kwargs.get("condition") param = kwargs.get("param") # 2012-12 if condition == "category": article_list = article_list.filter(category__title=param) elif condition == "tag": article_list = article_list.filter(tags__title=param) else: year, month = param.split("/") article_list = article_list.filter(create_time__year=year, create_time__month=month) # 每一个后的表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段") # 查询每一个分类名称以及对应的文章数 # ret=models.Category.objects.values("pk").annotate(c=Count("article__title")).values("title","c") # print(ret) # 查询当前站点的每一个分类名称以及对应的文章数 # cate_list=models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list("title","c") # print(cate_list) # 查询当前站点的每一个标签名称以及对应的文章数 # tag_list=models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title","c") # print(tag_list) # 查询当前站点每一个年月的名称以及对应的文章数 # ret=models.Article.objects.extra(select={"is_recent":"create_time > '2018-09-05'"}).values("title","is_recent") # print(ret) # 方式1: # date_list=models.Article.objects.filter(user=user).extra(select={"y_m_date":"date_format(create_time,'%%Y/%%m')"}).values("y_m_date").annotate(c=Count("nid")).values_list("y_m_date","c") # print(date_list) # 方式2: # from django.db.models.functions import TruncMonth # # ret=models.Article.objects.filter(user=user).annotate(month=TruncMonth("create_time")).values("month").annotate(c=Count("nid")).values_list("month","c") # print("ret----->",ret) return render(request, "home_site.html", {"username": username, "blog": blog, "article_list": article_list,}) def get_classification_data(username): user = UserInfo.objects.filter(username=username).first() blog = user.blog cate_list = models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list( "title", "c") tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", "c") date_list = models.Article.objects.filter(user=user).extra( select={"y_m_date": "date_format(create_time,'%%Y/%%m')"}).values("y_m_date").annotate( c=Count("nid")).values_list("y_m_date", "c") return {"blog": blog, "cate_list": cate_list, "date_list": date_list, "tag_list": tag_list} def article_detail(request, username, article_id): """ 文章详情页 :param request: :param username: :param article_id: :return: """ user = UserInfo.objects.filter(username=username).first() blog = user.blog article_obj = models.Article.objects.filter(pk=article_id).first() comment_list = models.Comment.objects.filter(article_id=article_id) return render(request, "article_detail.html", locals()) def digg(request): """ 点赞功能 :param request: :return: """ print(request.POST) article_id = request.POST.get("article_id") is_up = json.loads(request.POST.get("is_up")) # "true" # 点赞人即当前登录人 user_id = request.user.pk obj = models.ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first() response = {"state": True} if not obj: ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up) queryset = models.Article.objects.filter(pk=article_id) if is_up: queryset.update(up_count=F("up_count") + 1) else: queryset.update(down_count=F("down_count") + 1) else: response["state"] = False response["handled"] = obj.is_up return JsonResponse(response) def comment(request): """ 提交评论视图函数 功能: 1 保存评论 2 创建事务 3 发送邮件 :param request: :return: """ print(request.POST) article_id = request.POST.get("article_id") pid = request.POST.get("pid") content = request.POST.get("content") user_id = request.user.pk article_obj = models.Article.objects.filter(pk=article_id).first() # 事务操作 with transaction.atomic(): comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid) models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1) response = {} response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X") response["username"] = request.user.username response["content"] = content # 发送邮件 from django.core.mail import send_mail from cnblog import settings # send_mail( # "您的文章%s新增了一条评论内容"%article_obj.title, # content, # settings.EMAIL_HOST_USER, # ["916852314@qq.com"] # ) import threading t = threading.Thread(target=send_mail, args=("您的文章%s新增了一条评论内容" % article_obj.title, content, settings.EMAIL_HOST_USER, ["916852314@qq.com"]) ) t.start() return JsonResponse(response) def get_comment_tree(request): article_id = request.GET.get("article_id") response = list(models.Comment.objects.filter(article_id=article_id).order_by("pk").values("pk", "content", "parent_comment_id")) return JsonResponse(response, safe=False) @login_required def cn_backend(request): """ 后台管理的首页 :param request: :return: """ article_list = models.Article.objects.filter(user=request.user) return render(request, "backend/backend.html", locals()) from bs4 import BeautifulSoup @login_required def add_article(request): """ 后台管理的添加书籍视图函数 :param request: :return: """ if request.method == "POST": title = request.POST.get("title") content = request.POST.get("content") # 防止xss攻击,过滤script标签 soup=BeautifulSoup(content,"html.parser") for tag in soup.find_all(): print(tag.name) if tag.name=="script": tag.decompose() # 构建摘要数据,获取标签字符串的文本前150个符号 desc=soup.text[0:150]+"..." models.Article.objects.create(title=title,desc=desc,content=str(soup), user=request.user) return redirect("/cn_backend/") return render(request, "backend/add_article.html") def upload(request): """ 编辑器上传文件接受视图函数 :param request: :return: """ print(request.FILES) img_obj=request.FILES.get("upload_img") print(img_obj.name) path=os.path.join(settings.MEDIA_ROOT,"add_article_img",img_obj.name) with open(path,"wb") as f: for line in img_obj: f.write(line) return HttpResponse("ok")
forms组件校验
from django import forms from django.forms import widgets from blog.models import UserInfo from django.core.exceptions import NON_FIELD_ERRORS, ValidationError class UserForm(forms.Form): user=forms.CharField(max_length=32, error_messages={"required":"该字段不能为空"}, label="用户名", widget=widgets.TextInput(attrs={"class":"form-control"},) ) pwd=forms.CharField(max_length=32, label="密码", widget=widgets.PasswordInput(attrs={"class":"form-control"},) ) re_pwd=forms.CharField(max_length=32, label="确认密码", widget=widgets.PasswordInput(attrs={"class":"form-control"},) ) email=forms.EmailField(max_length=32, label="邮箱", widget=widgets.EmailInput(attrs={"class":"form-control"},) ) def clean_user(self): val=self.cleaned_data.get("user") user=UserInfo.objects.filter(username=val).first() if not user: return val else: raise ValidationError("该用户已注册!") def clean(self): pwd=self.cleaned_data.get("pwd") re_pwd=self.cleaned_data.get("re_pwd") if pwd and re_pwd: if pwd==re_pwd: return self.cleaned_data else: raise ValidationError("两次密码不一致!") else: return self.cleaned_data
将表格注册到后台
from django.contrib import admin # Register your models here. from blog import models admin.site.register(models.UserInfo) admin.site.register(models.Blog) admin.site.register(models.Category) admin.site.register(models.Tag) admin.site.register(models.Article) admin.site.register(models.ArticleUpDown) admin.site.register(models.Article2Tag) admin.site.register(models.Comment)
自定义标签,将数据引入前端标签生成固定模板,供需要整体引用
from django import template from django.db.models import Count from blog import models register=template.Library() @register.simple_tag def multi_tag(x,y): return x*y @register.inclusion_tag("classification.html") def get_classification_style(username): user = models.UserInfo.objects.filter(username=username).first() blog = user.blog cate_list=models.Category.objects.filter(blog=blog).values("pk").annotate(c=Count("article__title")).values_list("title","c") tag_list=models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title","c") date_list=models.Article.objects.filter(user=user).extra(select={"y_m_date":"date_format(create_time,'%%Y/%%m')"}).values("y_m_date").annotate(c=Count("nid")).values_list("y_m_date","c") return {"blog":blog,"cate_list":cate_list,"date_list":date_list,"tag_list":tag_list}
"""cnblog URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.0/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path,re_path from django.views.static import serve from blog import views from cnblog import settings from django.urls import include urlpatterns = [ path('admin/', admin.site.urls), path('login/', views.login), path('index/', views.index), path('logout/', views.logout), re_path('^$', views.index), path('get_validCode_img/', views.get_valid_code_img), path('register/', views.register), # 文本编辑器上传图片url path('upload/', views.upload), # 后台管理url re_path("cn_backend/$",views.cn_backend), re_path("cn_backend/add_article/$",views.add_article), # 点赞 path("digg/",views.digg), # 评论 path("comment/",views.comment), # 获取评论树相关数据 path("get_comment_tree/",views.get_comment_tree), # media配置: re_path(r"media/(?P<path>.*)$",serve,{"document_root":settings.MEDIA_ROOT}), re_path('^(?P<username>\w+)/articles/(?P<article_id>\d+)$', views.article_detail), # article_detail(request,username="yuan","article_id":article_id) # 个人站点的跳转 re_path('^(?P<username>\w+)/(?P<condition>tag|category|archive)/(?P<param>.*)/$', views.home_site), # home_site(reqeust,username="yuan",condition="tag",param="python") # 个人站点url re_path('^(?P<username>\w+)/$', views.home_site), # home_site(reqeust,username="yuan") ]
相关的html标签
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css"> </head> <body> <h3>登录页面</h3> <div class="container"> <div class="row"> <div class="col-md-6 col-lg-offset-3"> <form> {% csrf_token %} <div class="form-group"> <label for="user">用户名</label> <input type="text" id="user" class="form-control"> </div> <div class="form-group"> <label for="pwd">密码</label> <input type="password" id="pwd" class="form-control"> </div> <div class="form-group"> <label for="pwd">验证码</label> <div class="row"> <div class="col-md-6"> <input type="text" class="form-control" id="valid_code"> </div> <div class="col-md-6"> <img width="270" height="36" id="valid_code_img" src="/get_validCode_img/" alt=""> </div> </div> </div> <input type="button" class="btn btn-default login_btn" value="submit"><span class="error"></span> <a href="/register/" class="btn btn-success pull-right">注册</a> </form> </div> </div> </div> <script src="/static/js/jquery-3.2.1.min.js"></script> <script> // 刷新验证码 $("#valid_code_img").click(function () { $(this)[0].src += "?" }); // 登录验证 $(".login_btn").click(function () { $.ajax({ url: "", type: "post", data: { user: $("#user").val(), pwd: $("#pwd").val(), valid_code: $("#valid_code").val(), csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), }, success: function (data) { console.log(data); if (data.user) { if (location.search){ location.href = location.search.slice(6) } else { location.href = "/index/" } } else { $(".error").text(data.msg).css({"color": "red", "margin-left": "10px"}); setTimeout(function(){ $(".error").text(""); },1000) } } }) }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css"> <script src="/static/js/jquery-3.2.1.min.js"></script> <script src="/static/blog/bs/js/bootstrap.min.js"></script> <style> #user_icon { font-size: 18px; margin-right: 10px; vertical-align: -3px; } .pub_info{ margin-top: 10px; } .pub_info .glyphicon-comment{ vertical-align: -1px; } </style> </head> <body> <nav class="tab navbar navbar-default"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">博客园</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">随笔 <span class="sr-only">(current)</span></a></li> <li><a href="#">新闻</a></li> <li><a href="#">博文</a></li> </ul> <ul class="nav navbar-nav navbar-right"> {% if request.user.is_authenticated %} <li><a href="#"><span id="user_icon" class="glyphicon glyphicon-user"></span>{{ request.user.username }}</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">修改密码</a></li> <li><a href="#">修改头像</a></li> <li><a href="/cn_backend/">管理</a></li> <li><a href="/logout/">注销</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> </ul> </li> {% else %} <li><a href="/login/">登录</a></li> <li><a href="/register/">注册</a></li> {% endif %} </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> <div class="container-fluid"> <div class="row"> <div class="col-md-3"> <div class="panel panel-warning"> <div class="panel-heading">Panel heading without title</div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-info"> <div class="panel-heading">Panel heading without title</div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-danger"> <div class="panel-heading">Panel heading without title</div> <div class="panel-body"> Panel content </div> </div> </div> <div class="col-md-6"> <div class="article_list"> {% for article in article_list %} <div class="article-item small"> <h5><a href="/{{ article.user.username }}/articles/{{ article.pk }}">{{ article.title }}</a></h5> <div class="article-desc"> <span class="media-left"> <a href="/{{ article.user.username }}/"><img width="56" height="56" src="media/{{ article.user.avatar }}" alt=""></a> </span> <span class="media-right"> {{ article.desc }} </span> </div> <div class="small pub_info"> <span><a href="/{{ article.user.username }}/">{{ article.user.username }}</a></span> <span>发布于 {{ article.create_time|date:"Y-m-d H:i" }}</span> <span class="glyphicon glyphicon-comment"></span>评论({{ article.comment_count }}) <span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ article.up_count }}) </div> </div> <hr> {% endfor %} </div> </div> <div class="col-md-3"> <div class="panel panel-primary"> <div class="panel-heading">Panel heading without title</div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-default"> <div class="panel-heading">Panel heading without title</div> <div class="panel-body"> Panel content </div> </div> </div> </div> </div> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css"> <script src="/static/js/jquery-3.2.1.min.js"></script> <style> #avatar_img { margin-left: 20px; } #avatar { display: none; } .error { color: red; } </style> </head> <body> <h3>注册页面</h3> <div class="container"> <div class="row"> <div class="col-md-6 col-lg-offset-3"> <form id="form"> {% csrf_token %} {% for field in form %} <div class="form-group"> <label for="{{ field.auto_id }}">{{ field.label }}</label> {{ field }} <span class="error pull-right"></span> </div> {% endfor %} <div class="form-group"> <label for="avatar"> 头像 <img id="avatar_img" width="60" height="60" src="/static/blog/img/default.png" alt=""> </label> <input type="file" id="avatar" name="avatar"> </div> <input type="button" class="btn btn-default reg_btn" value="submit"><span class="error"></span> </form> </div> </div> </div> <script> // 头像预览 $("#avatar").change(function () { // 获取用户选中的文件对象 var file_obj = $(this)[0].files[0]; // 获取文件对象的路径 var reader = new FileReader(); reader.readAsDataURL(file_obj); // 修改img的src属性 ,src=文件对象的路径 reader.onload = function () { $("#avatar_img").attr("src", reader.result) }; }); // 基于Ajax提交数据 $(".reg_btn").click(function () { //console.log($("#form").serializeArray()); var formdata = new FormData(); var request_data = $("#form").serializeArray(); $.each(request_data, function (index, data) { formdata.append(data.name, data.value) }); formdata.append("avatar", $("#avatar")[0].files[0]); $.ajax({ url: "", type: "post", contentType: false, processData: false, data: formdata, success: function (data) { //console.log(data); if (data.user) { // 注册成功 location.href="/login/" } else { // 注册失败 //console.log(data.msg) // 清空错误信息 $("span.error").html(""); $(".form-group").removeClass("has-error"); // 展此次提交的错误信息! $.each(data.msg, function (field, error_list) { console.log(field, error_list); if (field=="__all__"){ $("#id_re_pwd").next().html(error_list[0]).parent().addClass("has-error"); } $("#id_" + field).next().html(error_list[0]); $("#id_" + field).parent().addClass("has-error"); }) } } }) }) </script> </body> </html>
{% extends 'base.html' %} {% block content %} <div class="article_list"> {% for article in article_list %} <div class="article-item clearfix"> <h5><a href="/{{ article.user.username }}/articles/{{ article.pk }}">{{ article.title }}</a></h5> <div class="article-desc"> {{ article.desc }} </div> <div class="small pub_info pull-right"> <span>发布于 {{ article.create_time|date:"Y-m-d H:i" }}</span> <span class="glyphicon glyphicon-comment"></span>评论({{ article.comment_count }}) <span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ article.up_count }}) </div> </div> <hr> {% endfor %} </div> {% endblock %}
<div> <div class="panel panel-warning"> <div class="panel-heading">我的标签</div> <div class="panel-body"> {% for tag in tag_list %} <p><a href="/{{ username }}/tag/{{ tag.0 }}">{{ tag.0 }}({{ tag.1 }})</a></p> {% endfor %} </div> </div> <div class="panel panel-danger"> <div class="panel-heading">随笔分类</div> <div class="panel-body"> {% for cate in cate_list %} <p><a href="/{{ username }}/category/{{ cate.0 }}">{{ cate.0 }}({{ cate.1 }})</a></p> {% endfor %} </div> </div> <div class="panel panel-success"> <div class="panel-heading">随笔归档</div> <div class="panel-body"> {% for date in date_list %} <p><a href="/{{ username }}/archive/{{ date.0 }}">{{ date.0 }}({{ date.1 }})</a></p> {% endfor %} </div> </div> </div>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/blog/css/home_site.css"> <link rel="stylesheet" href="/static/theme/{{ blog.theme }}"> <link rel="stylesheet" href="/static/blog/css/article_detail.css"> <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css"> <script src="/static/js/jquery-3.2.1.min.js"></script> </head> <body> <div class="header"> <div class="content"> <p class="title"> <span>{{ blog.title }}</span> <a href="/cn_backend/" class="backend">管理</a> </p> </div> </div> <div class="container"> <div class="row"> <div class="col-md-3 menu"> {% load my_tags %} {% get_classification_style username %} </div> <div class="col-md-9"> {% block content %} {% endblock %} </div> </div> </div> </body> </html>
{% extends "base.html" %} {% block content %} {% csrf_token %} <div class="article_info"> <h3 class="text-center title">{{ article_obj.title }}</h3> <div class="cont"> {{ article_obj.content|safe }} </div> <div class="clearfix"> <div id="div_digg"> <div class="diggit action"> <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span> </div> <div class="buryit action"> <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span> </div> <div class="clear"></div> <div class="diggword" id="digg_tips" style="color: red;"></div> </div> </div> <div class="comments list-group"> <p class="tree_btn">评论树</p> <div class="comment_tree"> </div> <script> $.ajax({ url: "/get_comment_tree/", type: "get", data: { article_id: "{{ article_obj.pk }}" }, success: function (comment_list) { console.log(comment_list); $.each(comment_list, function (index, comment_object) { var pk = comment_object.pk; var content = comment_object.content; var parent_comment_id = comment_object.parent_comment_id; var s = '<div class="comment_item" comment_id=' + pk + '><span>' + content + '</span></div>'; if (!parent_comment_id) { $(".comment_tree").append(s); } else { $("[comment_id=" + parent_comment_id + "]").append(s); } }) } }) </script> <p>评论列表</p> <ul class="list-group comment_list"> {% for comment in comment_list %} <li class="list-group-item"> <div> <a href=""># {{ forloop.counter }}楼</a> <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span> <a href=""><span>{{ comment.user.username }}</span></a> <a class="pull-right reply_btn" username="{{ comment.user.username }}" comment_pk="{{ comment.pk }}">回复</a> </div> {% if comment.parent_comment_id %} <div class="pid_info well"> <p> {{ comment.parent_comment.user.username }}: {{ comment.parent_comment.content }} </p> </div> {% endif %} <div class="comment_con"> <p>{{ comment.content }}</p> </div> </li> {% endfor %} </ul> <p>发表评论</p> <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 class="btn btn-default comment_btn">提交评论</button> </p> </div> <script> // 点赞请求 $("#div_digg .action").click(function () { var is_up = $(this).hasClass("diggit"); $obj = $(this).children("span"); $.ajax({ url: "/digg/", type: "post", data: { "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(), "is_up": is_up, "article_id": "{{ article_obj.pk }}", }, success: function (data) { console.log(data); if (data.state) { var val = parseInt($obj.text()); $obj.text(val + 1); } else { var val = data.handled ? "您已经推荐过!" : "您已经反对过!"; $("#digg_tips").html(val); setTimeout(function () { $("#digg_tips").html("") }, 1000) } } }) }) // 评论请求 var pid = ""; $(".comment_btn").click(function () { var content = $("#comment_content").val(); if (pid) { var index = content.indexOf("\n"); content = content.slice(index + 1) } $.ajax({ url: "/comment/", type: "post", data: { "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(), "article_id": "{{ article_obj.pk }}", "content": content, pid: pid }, success: function (data) { console.log(data); var create_time = data.create_time; var username = data.username; var content = data.content; var s = ` <li class="list-group-item"> <div> <span>${create_time}</span> <a href=""><span>${username}</span></a> </div> <div class="comment_con"> <p>${content}</p> </div> </li>`; $("ul.comment_list").append(s); // 清空评论框 pid = "", $("#comment_content").val(""); } }) }); // 回复按钮事件 $(".reply_btn").click(function () { $('#comment_content').focus(); var val = "@" + $(this).attr("username") + "\n"; $('#comment_content').val(val); pid = $(this).attr("comment_pk"); }) </script> </div> {% endblock %}
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css"> </head> <body> <div class="container" style="margin-top: 100px"> <div class="text-center"> <a href="http://www.cnblogs.com/"><img src="/static/blog/img/logo_small.gif" alt="cnblogs"></a> <p><b>404.</b> 抱歉! 您访问的资源不存在!</p> <p class="d">请确认您输入的网址是否正确,如果问题持续存在,请发邮件至 404042726@qq.com 与 <strong style="font-size: 28px">老村长</strong> 联系。</p> <p><a href="/">返回网站首页</a></p> </div> </div> </body> </html>
浙公网安备 33010602011771号