搭建自己的博客(二十五):实现简单的点赞功能
新创建一个app来单独实现点赞的功能。
1、变化的部分
2、上代码
ul.blog-types,ul.blog-dates { list-style-type: none; } div.blog:not(:last-child) { margin-bottom: 2em; padding-bottom: 1em; border-bottom: 1px solid #eee; } div.blog h3 { margin-top: 0.5em; } div.blog-info p { margin-bottom: 0; } div.blog-info p span{ margin-right: 10px; } div.blog-info-description { list-style-type: none; margin-bottom: 1em; } ul.blog-info-description li { display: inline-block; margin-right: 1em; } div.paginator { text-align: center; } div.container { max-width: 80%; } div.comment-area{ margin-top: 2em; } h3.comment-area-title{ border-bottom: 1px solid #ccc; padding-bottom: 0.4em; } div.django-ckeditor-widget { width: 100%; } div.comment{ border-bottom: 1px dashed #ccc; margin-bottom: 0.5em; padding-bottom: 0.5em; } div.reply{ margin-left: 2em; } div#reply-content-container{ display: block; border: 1px solid #d1d1d1; border-bottom: none; background-color: #f8f8f8; overflow: hidden; padding: 1em 1em 0.5em; } p#reply_title{ border-bottom: 1px dashed #ccc; padding-bottom: 0.5em; } div.like{ color: #337ab7; cursor: pointer; display: inline-block; padding: 0.5em 0.2em; } div.like .active{ color: #f22; }
{# 引用模板 #} {% extends 'base.html' %} {% load staticfiles %} {% load comment_tags %} {% load likes_tags %} {% block header_extends %} <link rel="stylesheet" href="{% static 'blog/blog.css' %}"> <link rel="stylesheet" href="{% static 'fontawesome-free-5.5.0-web/css/all.min.css' %}"> {# 处理公式 #} <script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML' async></script> <script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script> <script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script> {% endblock %} {# 标题 #} {% block title %} {{ blog.title }} {% endblock %} {# 内容#} {% block content %} <div class="container"> <div class="row"> <div class="col-10 offset-1"> <ul class="blog-info-description"> <h3>{{ blog.title }}</h3> <li>作者:{{ blog.author }}</li> {# 时间过滤器让时间按照自己需要的格式过滤 #} <li>发布日期:{{ blog.created_time|date:"Y-m-d H:i:s" }}</li> <li>分类: <a href="{% url 'blogs_with_type' blog.blog_type.pk %}"> {{ blog.blog_type }} </a> </li> <li>阅读({{ blog.get_read_num }})</li> <li>评论({% get_comment_count blog %})</li> </ul> <div class="blog-content">{{ blog.content|safe }}</div> <div class="like" onclick="likeChange(this,'{% get_content_type blog %}',{{ blog.pk }})"> <i class="far fa-thumbs-up {% get_like_status blog %}"></i> <span class="liked-num">{% get_like_count blog %}</span> <span>喜欢</span> </div> <p>上一篇: {% if previous_blog %} <a href="{% url 'blog_detail' previous_blog.pk %}">{{ previous_blog.title }}</a> {% else %} <span>没有了</span> {% endif %} </p> <p>下一篇: {% if next_blog %} <a href="{% url 'blog_detail' next_blog.pk %}">{{ next_blog.title }}</a> {% else %} <span>没有了</span> {% endif %} </p> </div> </div> <div class="row"> <div class="col-10 offset-1"> <div class="comment-area"> <h3 class="comment-area-title">提交评论</h3> {% if user.is_authenticated %} <form id="comment-form" action="{% url 'update_comment' %}" method="post" style="overflow: hidden"> {% csrf_token %} <label for="form-control">{{ user.username }},欢迎评论~</label> <div id="reply-content-container" style="display: none;"> <p id="reply_title">回复:</p> <div id="reply-content"> </div> </div> {% get_comment_form blog as comment_form %} {% for field in comment_form %} {{ field }} {% endfor %} <span id="comment-error" class="text-danger float-left"></span> <input type="submit" value="评论" class="btn btn-primary float-right"> </form> {% else %} 您尚未登录,登录之后方可评论 {# 提交登录的时候带上从哪里访问的路径 #} <a class="btn btn-primary" href="{% url 'login' %}?from={{ request.get_full_path }}">登录</a> <span> or </span> <a class="btn-danger btn" href="{% url 'register' %}?from={{ request.get_full_path }}">注册</a> {% endif %} </div> <div class="-comment-area"> <h3 class="comment-area-title">评论列表</h3> <div id="comment-list"> {% get_comment_list blog as comments %} {% for comment in comments %} <div id="root-{{ comment.pk }}" class="comment"> <span>{{ comment.user.username }}</span> <span>{{ comment.comment_time|date:"Y-m-d H:i:s" }}</span> <div id="comment-{{ comment.pk }}">{{ comment.text|safe }}</div> {# 点赞 #} <div class="like" onclick="likeChange(this,'{% get_content_type comment %}',{{ comment.pk }})"> <i class="far fa-thumbs-up {% get_like_status comment %}"></i> <span class="liked-num">{% get_like_count comment %}</span> </div> <a href="javascript:reply({{ comment.pk }})">回复</a> {% for reply in comment.root_comment.all %} <div class="reply"> <span>{{ reply.user.username }}</span> <span>{{ reply.comment_time|date:"Y-m-d H:i:s" }}</span> <span>回复:</span><span>{{ reply.reply_to.username }}</span> <div id="comment-{{ reply.pk }}">{{ reply.text|safe }}</div> {# 点赞 #} <div class="like" onclick="likeChange(this,'{% get_content_type reply %}',{{ reply.pk }})"> <i class="far fa-thumbs-up {% get_like_status reply %}"></i> <span class="liked-num">{% get_like_count reply %}</span> </div> <a href="javascript:reply({{ reply.pk }})">回复</a> </div> {% endfor %} </div> {% empty %} <span id="no-comment">暂无评论</span> {% endfor %} </div> </div> </div> </div> </div> {% endblock %} {% block js %} <script> // 处理点赞 function likeChange(obj, content_type, object_id) { let is_like = obj.getElementsByClassName('active').length === 0; $.ajax({ url: "{% url 'like_change' %}", type: 'GET', data: { content_type: content_type, object_id: object_id, is_like: is_like, }, cache: false, success: function (data) { console.log(data); if (data['status'] === 'SUCCESS') { // 更新点赞状态 let element = $(obj.getElementsByClassName('fa-thumbs-up')); if (is_like) { element.addClass('active'); } else { element.removeClass('active'); } // 更新点赞数量 let like_num = $(obj.getElementsByClassName('liked-num')); like_num.text(data['liked_num']); } else { alert(data['msg']); } }, error: function (xhr) { console.log(xhr); } }); } // 处理回复 function reply(reply_comment_id) { $('#reply_comment_id').val(reply_comment_id); let html = $('#comment-' + reply_comment_id).html(); $('#reply-content').html(html); $('#reply-content-container').show(); // 显示内容 // 滚动富文本编辑器 $('html').animate({scrollTop: $('#comment-form').offset().top - 60}, 300, function () { // 动画执行完毕后执行的方法 // 让富文本编辑器获得焦点 CKEDITOR.instances['id_text'].focus(); }); } function numFormat(num) { return ('00' + num).substr(-2); } function timeFormat(timestamp) { let datetime = new Date(timestamp * 1000); let year = datetime.getFullYear(); let month = numFormat(datetime.getMonth() + 1); let day = numFormat(datetime.getDate()); let hour = numFormat(datetime.getHours()); let minute = numFormat(datetime.getMinutes()); let second = numFormat(datetime.getSeconds()); return `${year}-${month}-${day} ${hour}:${minute}:${second}` } // 提交评论 $('#comment-form').submit(function () { // 获取错误框 let comment_error = $('#comment-error'); comment_error.text(''); // 更新数据到textarea CKEDITOR.instances['id_text'].updateElement(); let comment_text = CKEDITOR.instances['id_text'].document.getBody().getText().trim(); // 判断是否为空 if (!(CKEDITOR.instances['id_text'].document.getBody().find('img')['$'].length !== 0 || comment_text !== '')) { // 显示错误信息 comment_error.text('评论内容不能为空'); return false; } //异步提交 $.ajax({ url: "{% url 'update_comment' %}", type: 'POST', data: $(this).serialize(),// 序列化表单值 cache: false, // 关闭缓存 success: function (data) { let reply_comment = $('#reply_comment_id'); if (data['status'] === 'SUCCESS') { console.log(data); // 插入数据 // es6写法 if (reply_comment.val() === '0') { // 插入评论 let comment_html = `<div id="root-${data["pk"]}" class="comment"> <span>${data["username"]}</span> <span>${timeFormat(data["comment_time"])}</span> <div id="comment-${data["pk"]}">${data["text"]}</div> <a href="javascript:reply(${data["pk"]})">回复</a> </div>`; $('#comment-list').prepend(comment_html); } else { // 插入回复 let reply_html = `<div class="reply"> <span>${data["username"]}</span> <span>${timeFormat(data["comment_time"])}</span> <span>回复:</span><span>${data["reply_to"]}</span> <div id="comment-${data["pk"]}">${data["text"]}</div> <a href="javascript:reply(${data["pk"]})">回复</a> </div>`; $('#root-' + data['root_pk']).append(reply_html); } // 清空编辑框的内容 CKEDITOR.instances['id_text'].setData(''); $('#reply-content-container').hide(); // 回复完隐藏掉要回复的内容 reply_comment.val('0'); // 将回复标志重置0 $('#no-comment').remove(); // 如果有没回复标志,清除掉5 comment_error.text('评论成功'); } else { // 显示错误信息 comment_error.text(data['message']) } }, error: function (xhr) { console.log(xhr); } }); return false; }); </script> <script> $(".nav-blog").addClass("active").siblings().removeClass("active"); </script> {% endblock %}
{% extends 'base.html' %} {% load staticfiles %} {% load comment_tags %} {% load likes_tags %} {# 标题 #} {% block title %} felix Blog {% endblock %} {% block header_extends %} <link rel="stylesheet" href="{% static 'blog/blog.css' %}"> <link rel="stylesheet" href="{% static 'fontawesome-free-5.5.0-web/css/all.min.css' %}"> {% endblock %} {# 内容#} {% block content %} <div class="container"> <div class="row"> <div class="col-md-8"> <div class="card" style=""> <div class="card-header"><h5 class="card-title">{% block blog_type_title %}博客列表{% endblock %}</h5> </div> <div class="card-body"> {% for blog in blogs %} <div class="blog"> <h3><a href="{% url 'blog_detail' blog.pk %}">{{ blog.title }}</a></h3> <div class="blog-info"> <p> {# 添加图标 #} <span> <i class="fas fa-tag"></i> <a href="{% url 'blogs_with_type' blog.blog_type.pk %}"> {{ blog.blog_type }} </a> </span> <span> <i class="far fa-clock "></i> {{ blog.created_time|date:"Y-m-d" }} </span> <span>阅读({{ blog.get_read_num }})</span> <span>评论({% get_comment_count blog %})</span> <span>点赞({% get_like_count blog %})</span> <p> </div> <p>{{ blog.content|safe|truncatechars:30 }}</p> </div> {% empty %} <div class="blog"> <h3>--暂无博客,敬请期待--</h3> </div> {% endfor %} </div> </div> {# 分页 #} <div class="paginator"> <ul class="pagination justify-content-center"> {# 首页 #} <li class="page-item"><a class="page-link" href="?page=1">首页</a></li> {# 上一页 #} {% if page_of_blogs.has_previous %} <li class="page-item"><a class="page-link" href="?page={{ page_of_blogs.previous_page_number }}">上一页</a> </li> {% else %} <li class="page-item disabled"><span class="page-link">上一页</span></li> {% endif %} {# 全部页码 #} {% for page_num in page_range %} {# 添加当前页高亮显示 #} {% if page_num == page_of_blogs.number %} <li class="page-item active"><span class="page-link">{{ page_num }}</span></li> {% else %} <li class="page-item"><a class="page-link" href="?page={{ page_num }}">{{ page_num }}</a></li> {% endif %} {% endfor %} {# 下一页 #} {% if page_of_blogs.has_next %} <li class="page-item"><a class="page-link" href="?page={{ page_of_blogs.next_page_number }}">下一页</a></li> {% else %} <li class="page-item disabled"><span class="page-link">下一页</span></li> {% endif %} {# 尾页 #} <li class="page-item"><a class="page-link" href="?page={{ page_of_blogs.paginator.num_pages }}">尾页</a></li> </ul> <p> 一共有 {{ page_of_blogs.paginator.count }}篇博客,当前{{ page_of_blogs.number }}页,共{{ page_of_blogs.paginator.num_pages }}页 </p> </div> </div> <div class="col-md-4"> <div class="card"> <div class="card-header"><h5 class="card-title">博客分类</h5></div> <div class="card-body"> <ul class="blog-types"> {% for blog_type in blog_types %} <li> <a href="{% url 'blogs_with_type' blog_type.pk %}">{{ blog_type.type_name }}({{ blog_type.blog_count }})</a> </li> {% empty %} <li>暂无分类</li> {% endfor %} </ul> </div> </div> <div class="card"> <div class="card-header"><h5 class="card-title">日期归档</h5></div> <div class="card-body"> <ul class="blog-dates"> {% for blog_date,blog_count in blog_dates.items %} <li> <a href="{% url 'blogs_with_date' blog_date.year blog_date.month %}"> {{ blog_date|date:'Y年m月' }}({{ blog_count }}) </a> </li> {% empty %} <li>暂无分类</li> {% endfor %} </ul> </div> </div> </div> </div> </div> {% endblock %} {% block js %} <script> $(".nav-blog").addClass("active").siblings().removeClass("active"); </script> {% endblock %}
# -*- coding: utf-8 -*- # @Time : 18-11-23 下午5:12 # @Author : Felix Wang from django import template from django.contrib.contenttypes.models import ContentType from ..models import LikeCount, LikeRecord # 注册自定义模板标签 register = template.Library() # 获取评论计数 @register.simple_tag def get_like_count(obj): content_type = ContentType.objects.get_for_model(obj) like_count, created = LikeCount.objects.get_or_create(content_type=content_type, object_id=obj.pk) return like_count.liked_num # 获取评论计数 @register.simple_tag(takes_context=True) def get_like_status(context, obj): content_type = ContentType.objects.get_for_model(obj) user = context['user'] if not user.is_authenticated: return '' if LikeRecord.objects.filter(content_type=content_type, object_id=obj.pk, user=user).exists(): return 'active' else: return '' @register.simple_tag def get_content_type(obj): content_type = ContentType.objects.get_for_model(obj) return content_type.model
from django.db import models from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.contrib.auth.models import User # Create your models here. class LikeCount(models.Model): content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') liked_num = models.IntegerField(default=0) class LikeRecord(models.Model): content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') user = models.ForeignKey(User, on_delete=models.CASCADE) liked_time = models.DateTimeField(auto_now_add=True)
# -*- coding: utf-8 -*- # @Time : 18-11-23 下午4:25 # @Author : Felix Wang from django.urls import path from . import views urlpatterns = [ path('like_change', views.like_change, name='like_change'), ]
from django.shortcuts import render from .models import LikeCount, LikeRecord from django.contrib.contenttypes.models import ContentType from django.http import JsonResponse from django.db.models import ObjectDoesNotExist def errorResponse(code, msg): data = { 'status': 'ERROR', 'code': code, 'msg': msg, } return JsonResponse(data) def successResponse(liked_num): data = { 'status': 'SUCCESS', 'liked_num': liked_num, } return JsonResponse(data) def like_change(requests): # 获取数据 user = requests.user if not user.is_authenticated: return errorResponse(400, '你没有登录') content_type = requests.GET.get('content_type') object_id = int(requests.GET.get('object_id')) try: content_type = ContentType.objects.get(model=content_type) model_class = content_type.model_class() model_obj = model_class.objects.get(pk=object_id) except ObjectDoesNotExist: return errorResponse(401, '对象不存在') # 处理数据 if requests.GET.get('is_like') == 'true': # 用户点赞 like_record, created = LikeRecord.objects.get_or_create(content_type=content_type, object_id=object_id, user=user) if created: # 没有点赞过 like_count, created = LikeCount.objects.get_or_create(content_type=content_type, object_id=object_id) like_count.liked_num += 1 like_count.save() return successResponse(like_count.liked_num) else: # 点赞过,不能重复点赞 return errorResponse(402, '你已经点赞过了') else: # 用户取消点赞 if LikeRecord.objects.filter(content_type=content_type, object_id=object_id, user=user).exists(): # 有点赞过,取消点赞 like_record = LikeRecord.objects.get(content_type=content_type, object_id=object_id, user=user) like_record.delete() # 点赞总数减1 like_count, created = LikeCount.objects.get_or_create(content_type=content_type, object_id=object_id) if not created: like_count.liked_num -= 1 like_count.save() return successResponse(like_count.liked_num) else: return errorResponse(404, '数据错误') else: # 没有点赞过,不能取消 return errorResponse(403, '你没有点赞过')
""" Django settings for myblog project. Generated by 'django-admin startproject' using Django 2.1.3. For more information on this file, see https://docs.djangoproject.com/en/2.1/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/2.1/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/2.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'ea+kzo_5k^6r7micfg@lar1(rfdc08@b4*+w5d11=0mp1p5ngr' # SECURITY WARNING: don't run with debug turned on in production!2. 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', 'ckeditor', 'ckeditor_uploader', 'blog.apps.BlogConfig', # 将自己创建的app添加到设置中 'read_statistics.apps.ReadStatisticsConfig', # 注册阅读统计app 'comment.apps.CommentConfig', # 注册评论 'likes.apps.LikesConfig', # 注册点赞 ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'blog.middleware.mymiddleware.My404', # 添加自己的中间件 ] ROOT_URLCONF = 'myblog.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 = 'myblog.wsgi.application' # Database # https://docs.djangoproject.com/en/2.1/ref/settings/#databases DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), # } 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'myblogs', # 要连接的数据库,连接前需要创建好 'USER': 'root', # 连接数据库的用户名 'PASSWORD': 'felixwang', # 连接数据库的密码 'HOST': '127.0.0.1', # 连接主机,默认本级 'PORT': 3306 # 端口 默认3306 } } # Password validation # https://docs.djangoproject.com/en/2.1/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/2.1/topics/i18n/ # LANGUAGE_CODE = 'en-us' # 语言 LANGUAGE_CODE = 'zh-hans' # TIME_ZONE = 'UTC' # 时区 TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True # 不考虑时区 USE_TZ = False # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.1/howto/static-files/ STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static") ] # media MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # 配置ckeditor CKEDITOR_UPLOAD_PATH = 'upload/' # 自定义参数 EACH_PAGE_BLOGS_NUMBER = 7 # 设置数据库缓存 CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'my_read_num_cache_table', } } # ckeditor 代码高亮,以及公式 CKEDITOR_CONFIGS = { 'default': { 'skin': 'moono', 'tabSpaces': 4, 'mathJaxLib': 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML', 'toolbar': ( ['div', 'Source', '-', 'Save', 'NewPage', 'Preview', '-', 'Templates'], ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Print', 'SpellChecker', 'Scayt'], ['Undo', 'Redo', '-', 'Find', 'Replace', '-', 'SelectAll', 'RemoveFormat', '-', 'Maximize', 'ShowBlocks', '-', "CodeSnippet", 'Mathjax', 'Subscript', 'Superscript'], ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'], ['Bold', 'Italic', 'Underline', 'Strike', '-'], ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', 'Blockquote'], ['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'], ['Link', 'Unlink', 'Anchor'], ['Image', 'Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak'], ['Styles', 'Format', 'Font', 'FontSize'], ['TextColor', 'BGColor'],), 'extraPlugins': ','.join([ 'codesnippet', 'mathjax', 'dialog', 'dialogui', 'lineutils', ]), }, 'comment_ckeditor': { 'toolbar': 'custom', 'toolbar_custom': [ ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript'], ['TextColor', 'BGColor', 'RemoveFormat'], ['NumberedList', 'BulletedList'], ['Link', 'Unlink'], ['Smiley', 'SpecialChar', 'Blockquote'], ], 'width': 'auto', 'height': '180', 'tabSpace': 4, 'removePlugins': 'elementspath', 'resize_enabled': False, } }
"""myblog URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.1/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, include from django.conf import settings from django.conf.urls.static import static from . import views urlpatterns = [ path('', views.home, name='home'), # 主页路径 path('admin/', admin.site.urls), path('ckeditor', include('ckeditor_uploader.urls')), # 配置上传url path('blog/', include('blog.urls')), # 博客app路径 path('comment/', include('comment.urls')), # 博客app路径 path('likes/', include('likes.urls')), # 博客app路径 path('login/', views.login, name='login'), # 登录 path('register/', views.register, name='register'), # 登录 ] # 设置ckeditor的上传 urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# -*- coding: utf-8 -*- # @Time : 18-11-7 下午4:12 # @Author : Felix Wang from django.shortcuts import render, redirect from django.contrib.contenttypes.models import ContentType from django.contrib import auth from django.contrib.auth.models import User from django.urls import reverse from read_statistics.utils import get_seven_days_read_data, get_x_days_hot_data from blog.models import Blog from .forms import LoginForm, RegisterForm def home(requests): blog_content_type = ContentType.objects.get_for_model(Blog) dates, read_nums = get_seven_days_read_data(blog_content_type) context = { 'read_nums': read_nums, 'dates': dates, 'today_hot_data': get_x_days_hot_data(0), # 获取今日热门 'yesterday_hot_data': get_x_days_hot_data(1), # 获取昨日热门 'seven_days_hot_data': get_x_days_hot_data(7), # 获取周热门 'one_month_hot_data': get_x_days_hot_data(30), # 获取月热门 } return render(requests, 'home.html', context) def login(requests): print(requests.POST) print(requests.GET) # 如果是form表单提交验证登录 if requests.method == 'POST': login_form = LoginForm(requests.POST) if login_form.is_valid(): # 验证是否通过 # 因为在form表单验证过了,所以不用自己再验证 user = login_form.cleaned_data.get('user') auth.login(requests, user) return redirect(requests.GET.get('from', reverse('home'))) else: login_form.add_error(None, '用户名或密码不正确') else: login_form = LoginForm() context = { 'login_form': login_form, } return render(requests, 'login.html', context) def register(requests): if requests.method == 'POST': reg_form = RegisterForm(requests.POST) if reg_form.is_valid(): username = reg_form.cleaned_data['username'] email = reg_form.cleaned_data['email'] password = reg_form.cleaned_data['password'] # 创建用户 user = User.objects.create_user(username=username, email=email, password=password) user.save() # 登录用户 user = auth.authenticate(username=username, password=password) auth.login(requests, user) # 登录之后跳转 return redirect(requests.GET.get('from', reverse('home'))) else: reg_form = RegisterForm() context = { 'reg_form': reg_form, } return render(requests, 'register.html', context)