一、變化的部分javascript
二、上代碼:css
{# 引用模板 #} {% 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 { if (data['code'] === 400) { $('#login_model').modal('show'); } 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寫法 let like_html = `<div class="like" onclick="likeChange(this,'${data['content_type']}',${data["pk"]})"> <i class="far fa-thumbs-up"></i> <span class="liked-num">0</span> </div>`; 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> ${like_html} <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> ${like_html} <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 %}
from django.shortcuts import render, get_object_or_404 from django.core.paginator import Paginator from django.conf import settings from django.db.models import Count from read_statistics.utils import read_statistics_once_read from .models import Blog, BlogType # 分頁部分公共代碼 def blog_list_common_data(requests, blogs_all_list): paginator = Paginator(blogs_all_list, settings.EACH_PAGE_BLOGS_NUMBER) # 第一個參數是所有內容,第二個是每頁多少 page_num = requests.GET.get('page', 1) # 獲取url的頁面參數(get請求) page_of_blogs = paginator.get_page(page_num) # 從分頁器中獲取指定頁碼的內容 current_page_num = page_of_blogs.number # 獲取當前頁 all_pages = paginator.num_pages if all_pages < 5: page_range = list( range(max(current_page_num - 2, 1), min(all_pages + 1, current_page_num + 3))) # 獲取須要顯示的頁碼 而且剔除不符合條件的頁碼 else: if current_page_num <= 2: page_range = range(1, 5 + 1) elif current_page_num >= all_pages - 2: page_range = range(all_pages - 4, paginator.num_pages + 1) else: page_range = list( range(max(current_page_num - 2, 1), min(all_pages + 1, current_page_num + 3))) # 獲取須要顯示的頁碼 而且剔除不符合條件的頁碼 blog_dates = Blog.objects.dates('created_time', 'month', order='DESC') blog_dates_dict = {} for blog_date in blog_dates: blog_count = Blog.objects.filter(created_time__year=blog_date.year, created_time__month=blog_date.month).count() blog_dates_dict = { blog_date: blog_count } return { 'blogs': page_of_blogs.object_list, 'page_of_blogs': page_of_blogs, 'blog_types': BlogType.objects.annotate(blog_count=Count('blog')), # 添加查詢並添加字段 'page_range': page_range, 'blog_dates': blog_dates_dict } # 博客列表 def blog_list(requests): blogs_all_list = Blog.objects.all() # 獲取所有博客 context = blog_list_common_data(requests, blogs_all_list) return render(requests, 'blog/blog_list.html', context) # 根據類型篩選 def blogs_with_type(requests, blog_type_pk): blog_type = get_object_or_404(BlogType, pk=blog_type_pk) blogs_all_list = Blog.objects.filter(blog_type=blog_type) # 獲取所有博客 context = blog_list_common_data(requests, blogs_all_list) context['blog_type'] = blog_type return render(requests, 'blog/blog_with_type.html', context) # 根據日期篩選 def blogs_with_date(requests, year, month): blogs_all_list = Blog.objects.filter(created_time__year=year, created_time__month=month) # 獲取所有博客 context = blog_list_common_data(requests, blogs_all_list) context['blogs_with_date'] = '{}年{}日'.format(year, month) return render(requests, 'blog/blog_with_date.html', context) # 博客詳情 def blog_detail(requests, blog_pk): blog = get_object_or_404(Blog, pk=blog_pk) obj_key = read_statistics_once_read(requests, blog) context = { 'blog': blog, 'previous_blog': Blog.objects.filter(created_time__gt=blog.created_time).last(), 'next_blog': Blog.objects.filter(created_time__lt=blog.created_time).first(), } response = render(requests, 'blog/blog_detail.html', context) response.set_cookie(obj_key, 'true') return response
""" 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', # 註冊點贊 'user.apps.UserConfig', # 用戶相關 ] 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', 'user.context_processors.login_model_form', # 自定義模板變量,直接用模板語言調用 ], }, }, ] 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('user/', include('user.urls')), # 博客app路徑 ] # 設置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 from django.contrib.contenttypes.models import ContentType from read_statistics.utils import get_seven_days_read_data, get_x_days_hot_data from blog.models import Blog 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)
{% load staticfiles %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <!-- 根據屏幕自動響應佈局 --> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title> {# 用來放標題 #} {% block title %} {% endblock %} </title> {# 加載css代碼 #} <link rel="stylesheet" href="{% static 'bootstrap4.1/bootstrap.min.css' %}"> <link rel="stylesheet" href="{% static 'css/base.css' %}"> {% block header_extends %} {# 用來作頭部擴展,如加載某些靜態文件 #} {% endblock %} </head> <body> {# 導航欄 #} <nav class="navbar navbar-expand-lg navbar-light bg-light sticky-top"> <a class="navbar-brand" href="{% url 'home' %}">Felix Blog</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav mr-auto"> <li class="nav-item nav-home"><a href="{% url 'home' %}" class="nav-link">首頁</a></li> <li class="nav-item nav-blog"><a href="{% url 'blog_list' %}" class="nav-link">博客</a></li> </ul> <ul class="navbar-nav"> {% if not user.is_authenticated %} <li class="nav-item"> <a class="nav-link" href="{% url 'login' %}?from={{ request.get_full_path }}">登陸</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'register' %}?from={{ request.get_full_path }}">註冊</a> </li> {% else %} <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> {{ user.username }} </a> <div class="dropdown-menu" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="{% url 'user_info' %}">我的資料</a> <div class="dropdown-divider"></div> <a class="dropdown-item" href="{% url 'logout' %}?from={{ request.get_full_path }}">登出</a> </div> </li> {% endif %} </ul> </div> </nav> {# 用來放內容 #} {% block content %} {% endblock %} <!-- Modal 登陸模態框 --> <div class="modal fade" id="login_model" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered" role="document"> <div class="modal-content"> <form id="login_model_form" action="" method="post"> <div class="modal-header"> <h5 class="modal-title">登陸</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> {% csrf_token %} {% for field in login_model_form %} <label for="{{ field.id_for_label }}">{{ field.label }}</label> {{ field }} {% endfor %} <span id="login_model_tip" class="text-danger"></span> </div> <div class="modal-footer"> <button type="submit" class="btn btn-primary">登陸</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">關閉</button> </div> </form> </div> </div> </div> <script> $('#login_model_form').submit(function (event) { $('#login_model_tip').text(''); event.preventDefault(); // 阻止原事件提交 $.ajax({ url: '{% url 'login_for_model' %}', type: 'POST', data: $(this).serialize(), cache: false, success: function (data) { if (data['status'] === 'SUCCESS') { window.location.reload(); } else { $('#login_model_tip').text('用戶名或密碼不正確') } } }); }) </script> {# js代碼放在後面能夠增長性能 #} <!-- jQuery first, then Popper.js, then Bootstrap JS --> <script src="{% static 'js/jquery-3.3.1.min.js' %}"></script> <script src="{% static 'bootstrap4.1/popper.min.js' %}"></script> <script src="{% static 'bootstrap4.1/bootstrap.min.js' %}"></script> {# 導入資源建議放在js代碼前 #} {# 用來放js代碼 #} {% block js %} {% endblock %} </body> </html>
# -*- coding: utf-8 -*- # @Time : 18-11-25 下午2:55 # @Author : Felix Wang from .forms import LoginForm def login_model_form(requests): return {'login_model_form': LoginForm()}
# -*- coding: utf-8 -*- # @Time : 18-11-20 下午8:10 # @Author : Felix Wang from django import forms from django.contrib import auth from django.contrib.auth.models import User class LoginForm(forms.Form): username = forms.CharField(label='用戶名', required=True, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '請輸入用戶名'})) # widget指定input標籤類型 password = forms.CharField(label='密碼', widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '請輸入密碼'})) def clean(self): # 驗證數據 username = self.cleaned_data['username'] password = self.cleaned_data['password'] user = auth.authenticate(username=username, password=password) if user is None: raise forms.ValidationError('用戶名或密碼錯誤') self.cleaned_data['user'] = user # 將驗證過的user放入clean_data return self.cleaned_data class RegisterForm(forms.Form): # 用戶名字段 username = forms.CharField(label='用戶名', max_length=30, min_length=3, required=True, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '請輸入用戶名'})) # 郵箱字段 email = forms.EmailField(label='郵箱', min_length=3, required=True, widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': '請輸入郵箱'})) # 密碼字段 password = forms.CharField(label='密碼', min_length=6, required=True, widget=forms.PasswordInput( attrs={'class': 'form-control', 'placeholder': '請輸入密碼'})) # 再次輸入密碼 password_again = forms.CharField(label='確認密碼', min_length=6, required=True, widget=forms.PasswordInput( attrs={'class': 'form-control', 'placeholder': '請再輸入一次密碼'})) def clean_username(self): username = self.cleaned_data['username'] if User.objects.filter(username=username).exists(): raise forms.ValidationError('用戶名已存在') return username def clean_email(self): email = self.cleaned_data['email'] if User.objects.filter(email=email).exists(): raise forms.ValidationError('郵箱已存在') return email def clean_password_again(self): password = self.cleaned_data['password'] password_again = self.cleaned_data['password_again'] if password != password_again: raise forms.ValidationError('兩次輸入的密碼不一致') return password_again
# -*- coding: utf-8 -*- # @Time : 18-11-4 下午5:22 # @Author : Felix Wang from django.urls import path from . import views urlpatterns = [ path('login/', views.login, name='login'), # 登陸 path('logout/', views.logout, name='logout'), # 登陸 path('login_for_model/', views.login_for_model, name='login_for_model'), # 登陸 path('register/', views.register, name='register'), # 登陸 path('user_info/', views.user_info, name='user_info'), # 登陸 ]
# -*- coding: utf-8 -*- # @Time : 18-11-7 下午4:12 # @Author : Felix Wang from django.shortcuts import render, redirect from django.http import JsonResponse from django.contrib import auth from django.contrib.auth.models import User from django.urls import reverse from .forms import LoginForm, RegisterForm def login(requests): # 若是是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, 'user/login.html', context) def login_for_model(requests): login_form = LoginForm(requests.POST) # 若是是form表單提交驗證登陸 if login_form.is_valid(): # 驗證是否經過 # 由於在form表單驗證過了,因此不用本身再驗證 user = login_form.cleaned_data.get('user') auth.login(requests, user) data = { 'status': 'SUCCESS', } else: data = { 'status': 'ERROR', } return JsonResponse(data) 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, 'user/register.html', context) def logout(requests): auth.logout(requests) return redirect(requests.GET.get('from', reverse('home'))) def user_info(requests): context = {} return render(requests, 'user/user_info.html', context)
三、解釋html
使用到了模板變量java
若是須要在模板裏直接使用模板變量,在app中新建context_processors.py,而後定義一個方法,返回一個字典。mysql
而後在settings中配置。jquery
實例:es6
# -*- coding: utf-8 -*- # @Time : 18-11-25 下午2:55 # @Author : Felix Wang from .forms import LoginForm def login_model_form(requests): return {'login_model_form': LoginForm()}
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', 'user.context_processors.login_model_form', # 自定義模板變量,直接用模板語言調用 ], }, }, ]
{% login_model_form %}