目录结构如下:
cnblog


1 # 修改部分 2 STATIC_URL = '/static/' 3 STATICFILES_DIRS = [ 4 os.path.join(BASE_DIR, 'app01', "static") 5 ] 6 7 AUTH_USER_MODEL = 'app01.UserInfo' 8 9 MEDIA_URL = "/media/" 10 MEDIA_ROOT = os.path.join(BASE_DIR, "app01", "media")


1 urlpatterns = [ 2 url(r'^admin/', admin.site.urls), 3 4 url(r'^$', views.index), 5 url(r'^login/', views.log_in), 6 url(r'^logout/', views.logout), 7 url(r'^get_valid_img/', views.get_valid_img), 8 url(r'^index/', views.index), 9 10 url(r'^register/', views.register), 11 url(r'^change_pwd/', views.change_pwd), # 修改密码 未写 12 # blog url的分发 13 url(r'^blog/', include("app01.url")), 14 # media 配置 15 url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}), 16 17 # 配置添加文章中的文件上传路径 文本编辑器的指定路径 18 url(r'^upload_file/',views.upload_file), 19 ]


import os from django.core.wsgi import get_wsgi_application os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cnbolg.settings") application = get_wsgi_application()
app01


1 from django import template 2 from app01.models import * 3 from django.db.models import Count 4 # from django.utils.safestring import mark_safe 5 6 register = template.Library() # register的名字是固定的,不能改变 7 8 9 @register.inclusion_tag("blog/archive.html") 10 def get_archive_style(username): 11 user = UserInfo.objects.filter(username=username).first() 12 # 当前站点 13 blog = user.blog 14 # 查询当前站点的所有分类 15 # category_list=Category.objects.filter(blog_id=blog.pk) 16 # 查询每一个分类名称以及对应的文章数 17 cate_list = Category.objects.filter(blog=blog).annotate(c=Count("article")).values_list("title", "c") 18 19 # 查询但当前站点每一个标签名称以及对应的文章数 20 tag_list = Tag.objects.filter(blog=blog).annotate(c=Count("article")).values_list("title", "c") 21 22 # 日期归档 [["2018-02",5],["2010-9",6]] 23 date_list = Article.objects.filter(user=user) \ 24 .extra(select={"create_time_ym": "strftime('%%Y/%%m',create_time)"}) \ 25 .values("create_time_ym") \ 26 .annotate(c=Count("nid")).values_list("create_time_ym", "c") 27 28 return {"cate_list": cate_list, "tag_list": tag_list, "date_list": date_list, "username": username}


1 import random 2 3 4 def get_valid_img(request): 5 def get_random_color(): 6 return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) 7 8 from io import BytesIO 9 from PIL import Image, ImageDraw, ImageFont 10 f = BytesIO() 11 12 image = Image.new(mode='RGB', size=(120, 80), color=get_random_color()) 13 draw = ImageDraw.Draw(image) 14 font = ImageFont.truetype('app01/static/fonts/kumo.ttf', size=36) 15 16 temp = [] 17 for i in range(5): 18 random_char = random.choice([str(random.randint(0, 9)), 19 chr(random.randint(65, 90)), 20 chr(random.randint(97, 122)) 21 ]) 22 draw.text((i * 24, 26), random_char, get_random_color(), font=font) 23 temp.append(random_char) 24 25 # width = 120 26 # height = 80 27 # for i in range(80): 28 # draw.point((random.randint(0,width),random.randint(0,height)),fill=get_random_color()) 29 # 30 # for i in range(10): 31 # x1=random.randint(0,width) 32 # x2=random.randint(0,width) 33 # y1=random.randint(0,height) 34 # y2=random.randint(0,height) 35 # draw.line((x1,y1,x2,y2),fill=get_random_color()) 36 # for i in range(40): 37 # draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color()) 38 # x = random.randint(0, width) 39 # y = random.randint(0, height) 40 # draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color()) 41 image.save(f, "png") 42 data = f.getvalue() 43 44 return data, temp


1 from django.contrib import admin 2 3 # Register your models here. 4 from .models import * 5 6 admin.site.register(Article) 7 admin.site.register(UserInfo) 8 admin.site.register(Blog) 9 admin.site.register(Tag) 10 admin.site.register(Category) 11 admin.site.register(Comment) 12 admin.site.register(ArticleUpDown) 13 admin.site.register(ArticleDetail) 14 admin.site.register(Article2Tag)


1 from django import forms 2 from django.core.exceptions import ValidationError 3 from django.forms import widgets 4 from app01.models import * 5 6 7 class RegisterForm(forms.Form): 8 user = forms.CharField(max_length=18, 9 min_length=3, 10 error_messages={ 11 'required': '不能为空', 12 'max_length': '用户名太长', 13 'min_length': '用户名太短' 14 }, 15 widget=widgets.TextInput(attrs={"class": "form-control", "placeholder": "用户名"})) 16 pwd = forms.CharField(max_length=18, 17 min_length=3, 18 error_messages={ 19 'required': '不能为空', 20 'max_length': '密码太长', 21 'min_length': '密码太短' 22 }, 23 widget=widgets.PasswordInput(attrs={"class": "form-control", "placeholder": "密码"})) 24 repeat_pwd = forms.CharField(min_length=5, error_messages={"required": "该字段不能为空"}, 25 widget=widgets.PasswordInput(attrs={"class": "form-control", "placeholder": "确认密码"}) 26 ) 27 email = forms.EmailField(error_messages={"required": "该字段不能为空", "invalid": "格式错误"}, 28 widget=widgets.EmailInput(attrs={"class": "form-control", "placeholder": "邮箱"}) 29 ) 30 31 def clean_user(self): 32 val = self.cleaned_data.get('user') 33 ret = UserInfo.objects.filter(username=val) 34 if not ret: 35 return val 36 else: 37 raise ValidationError('该用户已经被注册!') 38 39 def clean_tel(self): 40 val = self.cleaned_data.get('tel') 41 import re 42 ret = re.search('1[356789]\d{9}$', val) 43 if ret: 44 return val 45 else: 46 raise ValidationError('手机号格式错误') 47 48 def clean(self): 49 pwd = self.cleaned_data.get('pwd') 50 repeat_pwd = self.cleaned_data.get('repeat_pwd') 51 if pwd and repeat_pwd: 52 if pwd == repeat_pwd: 53 return self.cleaned_data 54 else: 55 raise ValidationError('两次密码不一致') 56 else: 57 return self.cleaned_data


1 from django.db import models 2 3 # Create your models here. 4 from django.contrib.auth.models import AbstractUser 5 6 7 class UserInfo(AbstractUser): # 用户信息 8 nid = models.AutoField(primary_key=True) 9 nickname = models.CharField(verbose_name='昵称', max_length=32) 10 telephone = models.CharField(max_length=11, null=True, unique=True) 11 avatar = models.FileField(upload_to='avatar_dir/', default='/avatar/default.png') 12 create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) 13 blog = models.OneToOneField(to='Blog', to_field='nid', null=True) 14 15 def __str__(self): 16 return self.username 17 18 19 class Blog(models.Model): # 博客信息 20 nid = models.AutoField(primary_key=True) 21 title = models.CharField(verbose_name="个人博客标题", max_length=64) 22 site = models.CharField(verbose_name="个人博客后缀", max_length=32, unique=True) 23 theme = models.CharField(verbose_name="博客主题", max_length=32) 24 25 def __str__(self): 26 return self.title 27 28 29 class Category(models.Model): # 博主个人文章分类表 30 nid = models.AutoField(primary_key=True) 31 title = models.CharField(verbose_name='分类标题', max_length=32) 32 blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field="nid") 33 34 def __str__(self): 35 return self.title 36 37 38 class Tag(models.Model): 39 nid = models.AutoField(primary_key=True) 40 title = models.CharField(verbose_name="标签名称", max_length=32) 41 blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid') 42 43 def __str__(self): 44 return self.title 45 46 47 class Article(models.Model): 48 nid = models.AutoField(primary_key=True) 49 title = models.CharField(max_length=50, verbose_name='文章标题') 50 desc = models.CharField(max_length=255, verbose_name='文章描述') 51 create_time = models.DateTimeField(verbose_name='创建时间') 52 53 comment_count = models.IntegerField(default=0) 54 up_count = models.IntegerField(default=0) 55 down_count = models.IntegerField(default=0) 56 57 Category = models.ForeignKey(to= 'Category', to_field='nid', null=True) 58 tags = models.ManyToManyField( 59 to='Tag', 60 through='Article2Tag', 61 through_fields=('article', 'tag') 62 ) 63 user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid') 64 65 def __str__(self): 66 return self.title 67 68 69 class ArticleDetail(models.Model): 70 nid = models.AutoField(primary_key=True) 71 content = models.TextField() 72 73 article = models.OneToOneField(to='Article', to_field='nid') 74 75 76 class ArticleUpDown(models.Model): 77 nid = models.AutoField(primary_key=True) 78 user = models.ForeignKey('UserInfo', null=True) 79 article = models.ForeignKey('Article', null=True) 80 is_up = models.BooleanField(default=True) 81 82 class Meta: 83 unique_together = [('article', 'user'), ] 84 85 86 class Article2Tag(models.Model): 87 nid = models.AutoField(primary_key=True) 88 article = models.ForeignKey(verbose_name='文章', to='Article', to_field='nid') 89 tag = models.ForeignKey(verbose_name='标签', to='Tag', to_field='nid') 90 91 class Meta: 92 unique_together = [('article', 'tag'), ] 93 94 def __str__(self): 95 v = self.article.title + "-----" + self.tag.title 96 return v 97 98 99 class Comment(models.Model): 100 nid = models.AutoField(primary_key=True) 101 article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid') 102 user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid') 103 content = models.CharField(verbose_name='评论内容', max_length=255) 104 create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) 105 106 parent_comment = models.ForeignKey('self', null=True) 107 108 def __str__(self): 109 return self.content


1 from django.conf.urls import url, include 2 from app01 import views 3 4 urlpatterns = [ 5 # 文章详细页的点赞和评论 6 url(r'^digg/$', views.digg), 7 url(r'^comment/$',views.comment), 8 url(r'^get_comment_tree/(\d+)/$',views.get_comment_tree), 9 10 11 url(r'^(?P<username>\w+)/backend/$',views.backend), 12 url(r'^(?P<username>\w+)/backend_add_article/$',views.backend_add_article), 13 14 # 个人站点的url配置 15 url(r'^(?P<username>\w+)/articles/(?P<article_id>\d+)\.html/$', views.article_detail), 16 url(r'^(?P<username>\w+)/$', views.home_site), 17 url(r'^(?P<username>\w+)/(?P<condition>cate|tag|date)/(?P<params>(\w+/?\w+))$', views.home_site), 18 ]


1 from django.shortcuts import render, HttpResponse, redirect 2 from .models import * 3 from .models import UserInfo 4 from app01.forms import RegisterForm 5 from django.contrib import auth 6 from django.db.models import F 7 from django.db import transaction 8 from django.http import JsonResponse 9 import json 10 11 12 # 登录页 13 def log_in(request): 14 if request.is_ajax(): 15 user = request.POST.get("user") 16 pwd = request.POST.get("pwd") 17 valid_code = request.POST.get("valid_code") 18 code_str = request.session.get("random_code_str") 19 # 构造返回给前端的字典 20 login_response = {"user": None, "error_msg": ""} 21 22 if valid_code.upper() == code_str.upper(): 23 user = auth.authenticate(username=user, password=pwd) 24 if user: 25 login_response["user"] = user.username 26 auth.login(request, user) 27 else: 28 login_response["error_msg"] = "username or password error!" 29 else: 30 login_response["error_msg"] = "valid code error!" 31 return HttpResponse(json.dumps(login_response)) 32 33 return render(request, "login.html") 34 35 36 # 主页 37 def index(request): 38 username = request.user 39 article_list = Article.objects.all() 40 return render(request, 'index.html', locals()) 41 42 43 # 退出登录 44 def logout(request): 45 auth.logout(request) 46 # return HttpResponse('OK') 47 return redirect('/login/') 48 49 50 # 注册页 51 def register(request): 52 # if request.method == "POST": 53 if request.is_ajax(): 54 register_form = RegisterForm(request.POST) # 接收前端ajax传过来的对象 55 reg_response = {"user": None, "error_msg": None} # 定义返回给前端的数据格式 56 if register_form.is_valid(): 57 user = register_form.cleaned_data.get("user") 58 pwd = register_form.cleaned_data.get("pwd") 59 email = register_form.cleaned_data.get("email") 60 avatar_obj = request.FILES.get('avatar') # 图片对象 61 if avatar_obj: 62 user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar_obj) 63 else: 64 user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email) 65 reg_response["user"] = user_obj.username 66 else: 67 reg_response["error_msg"] = register_form.errors 68 return HttpResponse(json.dumps(reg_response)) 69 70 register_form = RegisterForm() 71 return render(request, "register.html", {"register_form": register_form}) 72 73 74 # 验证码 75 def get_valid_img(request): 76 ''' 77 :param request: 78 :get_valid_img: 生成验证码,返回图片数据 79 :return: 80 ''' 81 82 from app01.utils.valid_code import get_valid_img 83 info = get_valid_img(request) 84 85 request.session["random_code_str"] = "".join(info[1]) 86 ''' 87 if cookie.get(sesssion):更新 88 89 1 生成随机字符串 90 2 响应set_cookie {"sessionId":"123456abc"} 91 3 在django-session表中插入一条记录 92 session-key session-data 93 123456abc {"random_code_str":"abc12"} 94 ''' 95 96 return HttpResponse(info[0]) 97 98 99 # 修改密码 100 def change_pwd(request): 101 if request.method == 'POST': 102 user = request.user.username 103 old_pwd = request.POST.get('old_pwd') 104 new_pwd = request.POST.get('new_pwd') 105 106 # 构造返回给前端的字典 107 change_response = {"flag": False} 108 user_obj = auth.authenticate(username=user, password=old_pwd) 109 if user_obj: 110 if new_pwd == "": 111 change_response["error_msg"] = "新密码不能为空" 112 else: 113 user_obj.set_password(new_pwd) 114 user_obj.save() 115 logout(request) 116 change_response['flag'] = True 117 else: 118 change_response["error_msg"] = "旧密码错误" 119 return HttpResponse(json.dumps(change_response)) 120 return render(request, "change_pwd.html") 121 122 123 # 个人主页 124 def home_site(request, username, **kwargs): 125 # 判断用户是否存在 126 user = UserInfo.objects.filter(username=username).first() 127 if not user: 128 return render(request, "blog/not_found.html") 129 blog = user.blog # 当前站点 130 131 if not kwargs: 132 # 筛选当前站点的所有文章 133 article_list = Article.objects.filter(user=user) 134 else: 135 # 归档跳转标签的请求,对article_list过滤 136 condition = kwargs.get("condition") 137 params = kwargs.get("params") 138 if condition == "cate": 139 article_list = Article.objects.filter(user=user).filter(Category__title=params) 140 elif condition == "tag": 141 article_list = Article.objects.filter(user=user).filter(tags__title=params) 142 else: 143 year, month = params.split("/") 144 article_list = Article.objects.filter(user=user).filter(create_time__year=year, create_time__month=month) 145 return render(request, "blog/home_site.html", locals()) 146 147 148 # 文章详细页 149 def article_detail(request, username, article_id): 150 article_obj = Article.objects.filter(pk=article_id).first() 151 comment_list = Comment.objects.filter(article_id=article_id) 152 return render(request, "blog/article_detail.html", 153 {"username": username, "article_obj": article_obj, "comment_list": comment_list}) 154 155 156 # 点赞 157 def digg(request): 158 article_id = request.POST.get("article_id") 159 is_up = json.loads(request.POST.get("is_up")) 160 user_id = request.user.pk 161 response = {"state": True} 162 try: 163 with transaction.atomic(): # 事务 164 obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up) 165 if is_up: 166 Article.objects.filter(pk=article_id).update(up_count=F("up_count") + 1) 167 else: 168 Article.objects.filter(pk=article_id).update(down_count=F("down_count") + 1) 169 except Exception as e: 170 first_updown = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).values("is_up").first().get( 171 "is_up") 172 response["state"] = False 173 response["first_updown"] = first_updown 174 175 return JsonResponse(response) 176 177 178 # 评论 179 def comment(request): 180 content = request.POST.get("content") 181 article_id = request.POST.get("article_id") 182 user_id = request.user.pk 183 pid = request.POST.get("pid") 184 185 comment_response = {} 186 with transaction.atomic(): 187 if pid: # 子评论 188 comment = Comment.objects.create(content=content, article_id=article_id, user_id=user_id, 189 parent_comment_id=pid) 190 else: 191 comment = Comment.objects.create(content=content, article_id=article_id, user_id=user_id) 192 Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1) 193 194 comment_response["create_time"] = comment.create_time.strftime("%Y-%m-%d %H:%M") 195 comment_response["content"] = comment.content 196 return HttpResponse(json.dumps(comment_response)) 197 198 199 # 管理 200 def backend(request, username): 201 article_list = Article.objects.filter(user__username=username) 202 return render(request, "blog/backend_index.html", {"username": username, "article_list": article_list}) 203 204 205 # 管理后台添加文章 206 def backend_add_article(request, username): 207 if request.method == "POST": 208 content = request.POST.get("content") 209 210 # 用BeautifulSoup过滤content 211 valid_tags_attrs_list = { 212 "div": ["id", "class", "style"], 213 "img": ["src", "width", "height"], 214 "a": ["href"], 215 "p": [], 216 } 217 from bs4 import BeautifulSoup 218 soup = BeautifulSoup(content, "html.parser") 219 tags_list = soup.find_all() 220 221 for tag in tags_list: 222 if tag.name not in valid_tags_attrs_list: 223 tag.decompose() 224 print("count:", soup) 225 226 return render(request, "blog/backend_add_article.html") 227 228 229 # 编辑器上传文件 230 def upload_file(request): 231 print(request.FILES) 232 # 保存上传到指定路径 233 obj = request.FILES.get("upload_img") 234 235 from django.core.files.uploadedfile import InMemoryUploadedFile 236 from cnbolg import settings 237 import os 238 path = os.path.join(settings.MEDIA_ROOT, "article_imgs", obj.name) 239 with open(path, "wb") as f_write: 240 for chunk in obj.chunks(): 241 f_write.write(chunk) 242 243 # 给本文编辑器返回json字符串 244 upload_response = { 245 "error": 0, 246 "url": "/media/article_imgs/%s" % obj.name 247 } 248 249 return HttpResponse(json.dumps(upload_response)) 250 251 252 # 评论树 253 def get_comment_tree(request, article_id): 254 comment_list = list( 255 Comment.objects.filter(article_id=article_id).values("nid", "content", "parent_comment_id", "user__username")) 256 return JsonResponse(comment_list, safe=False)
templates


1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>YueNet</title> 8 <link rel="stylesheet" href="/static/bs/css/bootstrap.css"> 9 <link rel="stylesheet" href="/static/css/index.css"> 10 <script src="/static/js/jquery-3.2.1.min.js"></script> 11 <script src="/static/bs/js/bootstrap.js"></script> 12 </head> 13 <body> 14 <!--顶部导航条--> 15 <nav class="navbar navbar-inverse"> 16 <div class="container"> 17 <!-- Brand and toggle get grouped for better mobile display --> 18 <div class="navbar-header"> 19 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" 20 data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> 21 <span class="sr-only"></span> 22 <span class="icon-bar"></span> 23 <span class="icon-bar"></span> 24 <span class="icon-bar"></span> 25 </button> 26 <a class="navbar-brand" href="#">YueNet</a> 27 </div> 28 29 <!-- Collect the nav links, forms, and other content for toggling --> 30 <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> 31 <ul class="nav navbar-nav"> 32 </ul> 33 34 {% if request.user.is_authenticated %} 35 <ul class="nav navbar-nav navbar-right"> 36 <li><a href="#"> 37 <span class="glyphicon glyphicon-user"></span> {{ request.user.username }}</a></li> 38 <li><a href="/logout/">退出</a></li> 39 <li class="dropdown"> 40 <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" 41 aria-expanded="false">我的博客 <span class="caret"></span></a> 42 <ul class="dropdown-menu"> 43 <li><a href="/change_pwd/">修改密码</a></li> 44 <li><a href="#">我的设置</a></li> 45 <li><a href="#">我的收藏</a></li> 46 <li role="separator" class="divider"></li> 47 <li><a href="#">注销账号</a></li> 48 </ul> 49 </li> 50 </ul> 51 {% else %} 52 <ul class="nav navbar-nav navbar-right"> 53 <li><a href="/login/">登录</a></li> 54 <li><a href="/register/">注册</a></li> 55 </ul> 56 {% endif %} 57 </div><!-- /.navbar-collapse --> 58 </div><!-- /.container-fluid --> 59 </nav> 60 61 <!--页面主体开始--> 62 <div class="container-fluid"> 63 <div class="row"> 64 <!-- 左侧菜单 --> 65 <div class="col-md-3"> 66 <div class="panel panel-primary"> 67 <!-- Default panel contents --> 68 <div class="panel-heading">文章分类</div> 69 <div class="panel-body"> 70 <p>...</p> 71 </div> 72 </div> 73 <div class="panel panel-success"> 74 <!-- Default panel contents --> 75 <div class="panel-heading">推荐博客</div> 76 <div class="panel-body"> 77 <p>...</p> 78 </div> 79 </div> 80 <div class="panel panel-danger"> 81 <!-- Default panel contents --> 82 <div class="panel-heading">统计信息</div> 83 <div class="panel-body"> 84 <p>...</p> 85 </div> 86 </div> 87 </div> 88 <!-- 文章列表 --> 89 <div class="col-md-6"> 90 <div class="article_list"> 91 {% for article_obj in article_list %} 92 <div class="article_item"> 93 <h4><a href="/blog/{{ article_obj.user.username }}/articles/{{ article_obj.pk }}.html">{{ article_obj.title }}</a></h4> 94 <div class="row"> 95 <div class="col-md-2"> 96 <a href="/blog/{{ article_obj.user.username }}"> 97 <img src="{{ article_obj.user.avatar.url }}" alt="" width="70" height="70"> 98 </a> 99 </div> 100 <div class="desc"> 101 <p>{{ article_obj.desc }}</p> 102 </div> 103 </div> 104 <div class="small"> 105 <a href="/blog/{{ article_obj.user.username }}">{{ article_obj.user.username }}</a> 106 <span>发布于</span> 107 <span>{{ article_obj.create_time|date:'Y-m-d H:i' }}</span> 108 <span class="glyphicon glyphicon-comment"></span>评论({{ article_obj.comment_count }}) 109 <span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ article_obj.up_count }}) 110 </div> 111 </div> 112 {% endfor %} 113 </div> 114 <!-- 广告 --> 115 <div class="col-md-3"></div> 116 </div> 117 </div> 118 119 120 </body> 121 </html>


1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <link rel="stylesheet" href="/static/bs/css/bootstrap.css"> 8 <script src="/static/js/jquery-3.2.1.min.js"></script> 9 <title>登录页</title> 10 </head> 11 12 <body> 13 <h3>登录页面</h3> 14 <div class="container"> 15 <div class="row"> 16 <div class="col-md-6 col-md-offset-3"> 17 <form> 18 {% csrf_token %} 19 <div class="form-group"> 20 <label for="user">用户名</label> 21 <input type="text" class="form-control" id="user" placeholder="Username"> 22 </div> 23 <div class="form-group"> 24 <label for="pwd">密码</label> 25 <input type="password" class="form-control" id="pwd" placeholder="Password"> 26 </div> 27 <div class="row"> 28 <div class="col-md-6"> 29 <div class="form-group"> 30 <label for="valid_code">验证码</label> 31 <input type="text" class="form-control" id="valid_code" placeholder="验证码"> 32 </div> 33 </div> 34 <div class="col-md-6"> 35 <img id="valid_img" width="260" height="60" src="/get_valid_img/" alt=""> 36 </div> 37 </div> 38 <input type="button" value="submit" class="btn btn-primary pull-right login_btn"> 39 <span class="error"> </span> 40 </form> 41 </div> 42 </div> 43 </div> 44 45 <script> 46 $('.login_btn').click(function () { 47 48 $.ajax({ 49 url: '', 50 type: 'post', 51 data: { 52 "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(), 53 'user': $('#user').val(), 54 'pwd': $('#pwd').val(), 55 'valid_code': $('#valid_code').val() 56 }, 57 58 success: function (data) { 59 var login_response = JSON.parse(data); 60 console.log(login_response); 61 if (login_response.user) { // 登录成功 62 location.href = '/index/' 63 } 64 else { // 登录失败 65 $('.error').html(login_response.error_msg).css('color', 'red'); 66 67 setTimeout(function () { 68 $('.error').html('') 69 }, 1000) 70 } 71 } 72 }) 73 }); 74 // 验证码点击刷新 75 $("#valid_img").click(function () { 76 $(this)[0].src += "?" 77 }) 78 79 80 </script> 81 </body> 82 </html>


1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>注册页</title> 8 <link rel="stylesheet" href="/static/bs/css/bootstrap.css"> 9 <script src="/static/js/jquery-3.2.1.min.js"></script> 10 </head> 11 <body> 12 <h3>注册页</h3> 13 14 <div class="container"> 15 <div class="row"> 16 <div class="col-md-6 col-md-offset-3"> 17 <form> 18 {% csrf_token %} 19 <div> 20 <label for="id_user">用户名</label> 21 {{ register_form.user }} 22 <span class="pull-right"></span> 23 </div> 24 <div class="form-group"> 25 <label for="id_pwd">密码</label> 26 {{ register_form.pwd }} 27 <span class="pull-right"></span> 28 </div> 29 <div class="form-group"> 30 <label for="id_repeat_pwd">确认密码</label> 31 {{ register_form.repeat_pwd }} 32 <span class="pull-right"></span> 33 </div> 34 <div class="form-group"> 35 <label for="id_email">邮箱</label> 36 {{ register_form.email }} 37 <span class="pull-right"></span> 38 </div> 39 <div class="form-group"> 40 <label for="avatar"> 41 头像 42 <img id="avater_img" src='/static/img/default.png' alt="" width="60" height="60"> 43 </label> 44 <input type="file" style="display: none;" id="avatar"> 45 <input type="button" value="submit" class="btn btn-primary pull-right reg_btn"> <span 46 class="error"></span> 47 </div> 48 </form> 49 </div> 50 </div> 51 </div> 52 53 <script> 54 // 头像预览功能 55 $("#avatar").change(function () { 56 var choose_file = $(this)[0].files[0]; 57 var reader = new FileReader(); 58 reader.readAsDataURL(choose_file); 59 reader.onload = function () { 60 $("#avater_img").attr("src", this.result) 61 } 62 }); 63 64 // ajax提交数据 65 $(".reg_btn").click(function () { 66 var formdata = new FormData(); 67 formdata.append("user", $("#id_user").val()); 68 formdata.append("pwd", $("#id_pwd").val()); 69 formdata.append("repeat_pwd", $("#id_repeat_pwd").val()); 70 formdata.append("email", $("#id_email").val()); 71 formdata.append("avatar", $("#avatar")[0].files[0]); 72 formdata.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val()); 73 74 $.ajax({ 75 url: "", 76 type: "post", 77 data: formdata, 78 contentType: false, 79 processData: false, 80 success: function (data) { 81 console.log(data); 82 data = JSON.parse(data); 83 if (data.user) { 84 location.href = "/login/" 85 } 86 else { 87 // 清空上次错误信息 88 $("form span").html(""); 89 $(".form-group").removeClass("has-error"); 90 91 // 显示当前错误信息 92 $.each(data.error_msg, function (field, error_info) { 93 console.log(field, error_info[0]); 94 $("#id_" + field).parent().addClass("has-error"); 95 $("#id_" + field).next().html(error_info[0]).css("color", "red"); 96 // 判断全局错误 97 if (field == "__all__") { 98 $("#id_repeat_pwd").next().html(error_info[0]).css("color", "red"); 99 $("#id_repeat_pwd").parent().addClass("has-error"); 100 } 101 }); 102 } 103 } 104 }) 105 }); 106 107 </script> 108 109 </body> 110 </html>


1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>修改密码</title> 8 <link rel="stylesheet" href="/static/bs/css/bootstrap.css"> 9 <script src="/static/js/jquery-3.2.1.min.js"></script> 10 </head> 11 <body> 12 <h3>修改密码</h3> 13 <div class="container"> 14 <div class="row"> 15 <div class="col-md-6 col-md-offset-3"> 16 <form> 17 {% csrf_token %} 18 <div class="form-group"> 19 <label for="user">当前用户</label> 20 <input type="text" class="form-control" id="user" placeholder={{ request.user }} disabled> 21 </div> 22 <div class="form-group"> 23 <label for="pwd">当前密码</label> 24 <input type="password" class="form-control" id="old_password" placeholder="Old_Password"> 25 </div> 26 <div class="form-group"> 27 <label for="pwd">新设密码</label> 28 <input type="password" class="form-control" id="new_password" placeholder="New_Password"> 29 </div> 30 <input type="button" value="submit" class="btn btn-primary pull-right change_btn"> 31 <span class="error"> </span> 32 </form> 33 </div> 34 </div> 35 </div> 36 37 <script> 38 $('.change_btn').click(function () { 39 40 $.ajax({ 41 url: '', 42 type: 'post', 43 data: { 44 "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(), 45 'old_pwd': $('#old_password').val(), 46 'new_pwd': $('#new_password').val() 47 }, 48 success:function (data) { 49 var change_response=JSON.parse(data); 50 console.log(change_response); 51 console.log(change_response.flag); 52 if (change_response.flag){ // 修改密码成功 53 location.href = '/index/' 54 } 55 else{ // 修改密码失败 56 $('.error').html(change_response.error_msg).css("color","red"); 57 setTimeout(function () { 58 $('.error').html('') 59 },1000) 60 } 61 } 62 }) 63 }); 64 </script> 65 </body> 66 </html>
blog


1 <div class="left_region"> 2 <div class="panel panel-info"> 3 <!-- Default panel contents --> 4 <div class="panel-heading">我的分类</div> 5 <div class="panel-body"> 6 <p> 7 {% for category in cate_list %} 8 <p><a href="/blog/{{ username }}/cate/{{ category.0 }}">{{ category.0 }}({{ category.1 }})</a></p> 9 {% endfor %} 10 </p> 11 </div> 12 </div> 13 <div class="panel panel-success"> 14 <!-- Default panel contents --> 15 <div class="panel-heading">标签</div> 16 <div class="panel-body"> 17 <p> 18 {% for tag in tag_list %} 19 <p><a href="/blog/{{ username }}/tag/{{ tag.0 }}">{{ tag.0 }}({{ tag.1 }})</a></p> 20 {% endfor %} 21 22 </p> 23 </div> 24 </div> 25 <div class="panel panel-danger"> 26 <!-- Default panel contents --> 27 <div class="panel-heading">日期归档</div> 28 <div class="panel-body"> 29 {% for date in date_list %} 30 <p><a href="/blog/{{ username }}/date/{{ date.0 }}">{{ date.0 }}({{ date.1 }})</a></p> 31 {% endfor %} 32 33 </div> 34 </div> 35 </div>


1 {% load my_tags %} 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 7 <meta name="viewport" content="width=device-width, initial-scale=1"> 8 <link rel="stylesheet" href="/static/bs/css/bootstrap.css"> 9 <link rel="stylesheet" href="/static/css/article_detail.css"> 10 <script src="/static/js/jquery-3.2.1.min.js"></script> 11 <script src="/static/bs/js/bootstrap.js"></script> 12 <!--引入访问用户的博客样式--> 13 <link rel="stylesheet" href="/static/css/theme/{{ username }}.css"> 14 <title>Title</title> 15 <style> 16 * { 17 margin: 0; 18 padding: 0; 19 } 20 21 body { 22 margin-bottom: 50px; 23 } 24 25 .header p.title { 26 color: #ffffff; 27 font-family: 微软雅黑, 华文细黑, 黑体, Arial; 28 font-size: 24px; 29 float: left; 30 margin-left: 15px; 31 } 32 33 .action { 34 float: right; 35 margin-right: 40px; 36 } 37 38 .action a { 39 padding: 5px 8px; 40 color: white; 41 background-color: #5ab2ce; 42 font-size: 12px; 43 text-decoration: none; 44 margin-left: 5px; 45 } 46 47 48 </style> 49 </head> 50 <body> 51 <div class="header"> 52 <p class="title"><a href="/index/">{{ username }}</a></p> 53 <div class="action"> 54 <a href="/index/">首页</a> 55 <a href="">文章</a> 56 <a href="">随笔</a> 57 <a href="/blog/{{ request.user.username }}/backend">管理</a> 58 </div> 59 </div> 60 61 <div class="container"> 62 <div class="row"> 63 <div class="col-md-3"> 64 {% get_archive_style username %} 65 </div> 66 <div class="col-md-8"> 67 {% block content %} 68 <div class="articles_region"> 69 <div class="article_list"> 70 {% for article_obj in article_list %} 71 <div class="article_item"> 72 <h4> 73 <a href="/blog/{{ username }}/articles/{{ article_obj.pk }}.html">{{ article_obj.title }}</a> 74 </h4> 75 <div class="row"> 76 <div class="desc"> 77 <p>{{ article_obj.desc }}</p> 78 </div> 79 </div> 80 <div class="small pull-right"> 81 <a href="">{{ article_obj.user.username }}</a> 82 <span>发布于</span> 83 <span>{{ article_obj.create_time|date:'Y-m-d H:i' }}</span> 84 <span class="glyphicon glyphicon-comment"></span>评论({{ article_obj.comment_count }}) 85 <span class="glyphicon glyphicon-thumbs-up"></span>点赞({{ article_obj.up_count }}) 86 </div> 87 <hr> 88 </div> 89 {% endfor %} 90 </div> 91 </div> 92 {% endblock %} 93 </div> 94 95 </div> 96 </div> 97 98 </body> 99 </html>


1 {% extends "blog/home_base.html" %}


1 <!--引入font-awesome图标--> 2 <link rel="stylesheet" href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.css"> 3 4 {% extends "blog/home_base.html" %} 5 6 {% block content %} 7 <div class="article_info"> 8 <h4 class="text-center"><a>{{ article_obj.title }}</a></h4> 9 {{ article_obj.articledetail.content|safe }} 10 </div> 11 12 <div id="green_channel"> 13 <a>好文要顶</a> 14 <a>关注我</a> 15 <a>收藏该文</a> 16 <a> 17 <i class="fa fa-weibo"></i> 18 </a> 19 <a> 20 <i class="fa fa-weixin"></i> 21 </a> 22 </div> 23 <!--点赞功能--> 24 <div id="div_digg"> 25 <div class="diggit digg"> 26 <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span> 27 </div> 28 <div class="buryit digg"> 29 <span class="burynum" id="bury_count">{{ article_obj.down_count }}</span> 30 </div> 31 <div class="diggword" id="digg_tips"></div> 32 </div> 33 34 <p> </p> 35 <p> </p> 36 <p> </p> 37 <!--评论树功能--> 38 <p class="show_tree">评论树:</p> 39 <div class="comment_tree"> 40 </div> 41 42 43 <hr> 44 <!--评论楼功能--> 45 <div class="comment_show"> 46 <p>评论楼:</p> 47 <ul class="comment_list list-group"> 48 {% for comment in comment_list %} 49 <li class="comment_item list-group-item"> 50 <!--评论内容顶部--> 51 <div class="row"> 52 <div style="margin-left:20px;"> 53 <a>#{{ forloop.counter }}楼</a> 54 <span>{{ comment.create_time|date:'Y-m-d H:i' }}</span> 55 <span>{{ comment.user.username }}</span> 56 <div class="pull-right"> 57 <a class="reply_btn" comment_id="{{ comment.pk }}" 58 comment_user="{{ comment.user.username }}">回复</a> 59 </div> 60 </div> 61 </div> 62 <!--若为子评论时显示的内容--> 63 {% if comment.parent_comment_id %} 64 <div class="row"> 65 <div class="parent_comment_info col-md-offset-1 well"> 66 <a href="">@{{ comment.parent_comment.user.username }}</a> 67 <span>{{ comment.parent_comment.content }}</span> 68 </div> 69 </div> 70 {% endif %} 71 72 <!--评论内容--> 73 <div class="row"> 74 <div class="col-md-offset-1"> 75 <p>{{ comment.content }}</p> 76 </div> 77 </div> 78 </li> 79 {% endfor %} 80 </ul> 81 </div> 82 83 <!--评论框--> 84 <div class="comment"> 85 <p> 86 昵称:<input type="text" id="tbCommentAuthor" class="author" disabled size="50" 87 value="{{ request.user.username }}"> 88 </p> 89 <label for="">评论内容:</label> 90 <p> 91 <textarea name="" id="comment_area" cols="60" rows="10"></textarea> 92 </p> 93 <input type="button" class="btn btn-default" value="提交" id="comment_submit_btn"> 94 </div> 95 <div class="login_user_info" username="{{ request.user.usernaame }}"></div> 96 97 98 99 100 {% csrf_token %} 101 <script> 102 // 绑定提交点赞事件 103 $(".digg").click(function () { 104 if ("{{ request.user.username }}") { 105 var is_up = $(this).hasClass("diggit"); 106 $.ajax({ 107 url: "/blog/digg/", 108 type: "post", 109 data: { 110 article_id:{{ article_obj.pk }}, 111 csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), 112 is_up: is_up 113 }, 114 success: function (data) { 115 var error_info; 116 if (data.state) { 117 if (is_up) { 118 var val = parseInt($("#digg_count").text()) + 1; 119 $("#digg_count").text(val); 120 } 121 else { 122 var val = parseInt($("#bury_count").text()) + 1; 123 $("#bury_count").text(val); 124 } 125 } 126 else { 127 if (data.first_updown) { 128 error_info = "已经点赞过" 129 } else { 130 error_info = "已经反对过" 131 } 132 $("#digg_tips").html(error_info).css('color', "red"); 133 setTimeout(function () { 134 $("#digg_tips").html("") 135 }, 1000) 136 } 137 } 138 }) 139 } else { 140 alert("请登录") 141 } 142 }); 143 144 // 基于ajax绑定 提交评论 事件 145 $("#comment_submit_btn").click(function () { 146 // parent_comment_pk 区分 根评论和子评论 147 var content = $("#comment_area").val(); 148 149 if (parent_comment_pk) { 150 var index = content.indexOf("\n"); 151 content = content.slice(index + 1); 152 } else { 153 content = $("#comment_area").val(); 154 } 155 // 清空输入框的内容 156 $("#comment_area").val(""); 157 158 $.ajax({ 159 url: "/blog/comment/", 160 type: "post", 161 data: { 162 content: content, 163 article_id: "{{ article_obj.pk }}", 164 csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), 165 pid: parent_comment_pk 166 }, 167 success: function (data) { 168 // Ajax生成评论内容并显示 169 var data = JSON.parse(data); 170 171 var floor_count = $(".comment_list .comment_item").length + 1; 172 var create_time = data.create_time; 173 var username = $(".login_user_info").attr("username"); 174 var content = data.content; 175 176 177 s = '<li class="comment_item list-group-item"><div class="row"> <div class="col-md-offset-1"> <a href="">#' + floor_count + '楼</a> <span>' + create_time + '</span> <span>' + username + '</span> <div class="pull-right"></div></div></div> <div class="row"> <div class="col-md-offset-1"> <p>' + content + '</p> </div> </div> </li>'; 178 $(".comment_list").append(s) 179 } 180 }) 181 }); 182 183 184 // 绑定回复事件 185 var parent_comment_pk = ""; 186 $(".comment_item .reply_btn").click(function () { 187 // 获取焦点 188 $("#comment_area").focus(); 189 // 设置:@用户名 190 var val = "@" + $(this).attr("comment_user") + "\n"; 191 $("#comment_area").val(val); 192 // 获取回复评论的主键值 以便插入数据库 193 parent_comment_pk = $(this).attr("comment_id") 194 }); 195 196 // 评论树功能请求comment_list 197 $(".show_tree").click(function () { 198 $.ajax({ 199 url: "/blog/get_comment_tree/" +{{ article_obj.pk }}, 200 success: function (data) { 201 var comment_list = data; 202 var comment_html = ""; 203 $.each(comment_list, function (index, comment) { 204 var nid = comment.nid; 205 var content = comment.content; 206 var pid = comment.parent_comment_id; 207 var username = comment.user__username; 208 209 comment_html = '<div class="comment_item"><span>' + username + '</span>: <span class="content" tree_comment_id=' + nid + '>' + content + '</span></div>'; 210 if (pid) { 211 $("[tree_comment_id=" + pid + "]").parent().append(comment_html); 212 } 213 else { 214 $(".comment_tree").append(comment_html); 215 } 216 }) 217 } 218 }) 219 }) 220 </script> 221 222 {% endblock %}


1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <link rel="stylesheet" href="/static/bs/css/bootstrap.css"> 8 <link rel="stylesheet" href="/static/css/backend_index.css"> 9 <title>后台管理</title> 10 </head> 11 12 <body> 13 <div class="header"> 14 <div class="title">{{ username }}</div> 15 </div> 16 17 <div class="container-fluid"> 18 19 <div class="row"> 20 <!--管理页面左侧--> 21 <div class="col-md-3"> 22 <ul class="list-group"> 23 <li class="list-group-item"> 24 <a href="/blog/{{ request.user.username }}/backend_add_article">添加文章</a> 25 </li> 26 <li class="list-group-item">分类管理</li> 27 <li class="list-group-item">标签操作</li> 28 </ul> 29 </div> 30 <!--管理页面主体--> 31 <div class="col-md-9"> 32 <div class="con"> 33 <table class="table table-strippen table-hover"> 34 <thead> 35 <tr> 36 <th>文章标题</th> 37 <th>操作</th> 38 <th>操作</th> 39 </tr> 40 </thead> 41 <tbody> 42 {% for article in article_list %} 43 <tr> 44 <td>{{ article.title }}</td> 45 <td><a href="">编辑</a></td> 46 <td><a href="">删除</a></td> 47 </tr> 48 {% endfor %} 49 </tbody> 50 </table> 51 </div> 52 </div> 53 </div> 54 </div> 55 56 </body> 57 </html>


1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <link rel="stylesheet" href="/static/bs/css/bootstrap.css"> 8 <link rel="stylesheet" href="/static/css/backend_index.css"> 9 <title>后台管理</title> 10 </head> 11 12 <body> 13 <div class="header"> 14 <div class="title">{{ username }}</div> 15 </div> 16 17 <div class="container-fluid"> 18 19 <div class="row"> 20 <!--管理页面左侧--> 21 <div class="col-md-3"> 22 <ul class="list-group"> 23 <li class="list-group-item"> 24 <a href="/blog/{{ request.user.username }}/backend_add_article">添加文章</a> 25 </li> 26 <li class="list-group-item">分类管理</li> 27 <li class="list-group-item">标签操作</li> 28 </ul> 29 </div> 30 <!--管理页面主体--> 31 <div class="col-md-9"> 32 <div class="con"> 33 <table class="table table-strippen table-hover"> 34 <thead> 35 <tr> 36 <th>文章标题</th> 37 <th>操作</th> 38 <th>操作</th> 39 </tr> 40 </thead> 41 <tbody> 42 {% for article in article_list %} 43 <tr> 44 <td>{{ article.title }}</td> 45 <td><a href="">编辑</a></td> 46 <td><a href="">删除</a></td> 47 </tr> 48 {% endfor %} 49 </tbody> 50 </table> 51 </div> 52 </div> 53 </div> 54 </div> 55 56 </body> 57 </html>


1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>Title</title> 8 </head> 9 <body> 10 11 <p><b>404.</b> 抱歉! 您访问的资源不存在!</p> 12 <p class="d">请确认您输入的网址是否正确,如果问题持续存在,请发邮件至contact@cnblogs.com与我们联系。</p> 13 <p><a href="/">返回网站首页</a></p> 14 15 </body> 16 </html>