草庐IT

Django(二)精美博客搭建(1)实现登录/注册功能

M1kasal 2023-04-10 原文

前言

之前我们用Django框架做了一个很简单的个人博客搭建,不论是页面还是功能都很粗糙
所以从这篇开始我打算做一个比较完整的【个人博客网站】,可能会分好几篇博客来讲述
等所有功能完善的差不多后,再考虑上传github


环境:

  • Pycharm
  • python3.6
  • mysql 5.7
  • django 2.0.13


一、整体框架介绍

1、创建Django项目

  • 直接在pycharm里新建一个Django项目即可


2、框架简要介绍


3、项目概览



二、准备工作

1、创建blog数据库

  • 数据库我这里用的是mysql5.7



2、将静态资源移入项目



三、MyBlog模块具体实现

1、setting全局配置

import os
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

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

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'hf&ss)e1pr49yngt1s9ql%7wgotm91vsvw&88$67@3p@hlm%^e'

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

ALLOWED_HOSTS = []

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # 自己的应用
    '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',

]

ROOT_URLCONF = 'MyBlog.urls'

# 如果用户继承了AbstractUser,修改原生auth_user的模型的话就需要加这个配置
AUTH_USER_MODEL = 'user.UserProfile'

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',
                # 如果要在页面里面进行引用图片的话,就必须在这里添加配置
                'django.template.context_processors.media' # 在模板中可以使用{{MEDIA_URL}}
            ],
        },
    },
]

WSGI_APPLICATION = 'MyBlog.wsgi.application'

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

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

# Password validation
# https://docs.djangoproject.com/en/3.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/3.1/topics/i18n/

# 配置语言,时区
LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False

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

STATIC_URL = '/static/'
# 配置静态文件夹路径
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]

# 配置媒体文件路径
MEDIA_URL = ''
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

2、urls.py


from django.contrib import admin
from django.urls import path, include

from user.views import index
"""
	全局路径
"""
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', index, name='index'),
    # 配置user路径
    path('user/', include(('user.urls', 'user'), namespace='user')),

    # path('user/', include('user.urls', namespace='user')),
    

]


四、user模块具体实现

1、在user文件下新建urls.py

  • 简单提及下:我们这里为什么要再单独在user应用下创建一个urls.py呢?

    主要是为了让代码更加规范,一般情况下,项目比较大的话,我们肯定是有多个应用的,那各个应用的路径我们就统一配置在全局的urls中

    单个应用底下的路由我们都配在一起,方便统一管理

    比如说:咱们这次做的登录注册功能,是与用户相关的,那我们就单独在user应用下新建一个urls.py,里面配置用户相关的操作,例:登录/注册/注销等…


"""
    用户相关的路径
"""
from django.urls import path

from user.views import user_register, user_login, user_logout

urlpatterns = [
    path('register', user_register, name='register'),
    path('login', user_login, name='login'),
    path('logout', user_logout, name='logout'),

]


2、views.py

  • 这里【用户注销】的操作我写了两种方法,大家详细可以看下注释,我写得比较清楚
  • 另外这里踩了个坑,找问题找了很久:启动项目后,页面表单显示不出来
    原因:判断 request.method = ‘GET’ 时,这个get一定要用全部大写,用‘Get’不行
from django.contrib.auth import logout
from django.contrib.auth.hashers import make_password, check_password
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render, redirect

# Create your views here.
from django.urls import reverse

from user.forms import RegisterForm, LoginForm
from user.models import UserProfile

"""
    视图函数
"""


def index(request):
    """
    返回首页
    :param request:
    :return:
    """
    return render(request, "index.html")


def user_register(request):
    """
    用户注册
    :param request:
    :return:
    """
    if request.method == 'GET':  # 注意get一定要大写,不然无法将表单渲染在页面上
        return render(request, 'user/register.html')
    else:
        rform = RegisterForm(request.POST)  # 使用form获取数据
        print('--------》', rform)
        print("errors", rform.errors)
        if rform.is_valid():  # 进行数据的校验
            # 从干净的数据中取值,即通过前端校验的数据
            username = rform.cleaned_data.get('username')
            email = rform.cleaned_data.get('email')
            mobile = rform.cleaned_data.get('mobile')
            password = rform.cleaned_data.get('password')
            # 如果用户名/手机号不存在的话,才进行添加数据操作
            if not UserProfile.objects.filter(Q(username=username) | Q(mobile=mobile)).exists():
                # 注册到数据库中
                password = make_password(password)  # 密码进行加密
                user = UserProfile.objects.create(username=username, password=password, email=email, mobile=mobile)
                if user:
                    # 如果用户创建成功,则提示注册成功
                    return HttpResponse('注册成功')
            else:
                # 否则用户名/手机号已存在
                return render(request, 'user/register.html', context={'msg': '用户名或者手机号已经存在!'})
        # 数据校验失败,就提示注册失败
        return render(request, 'user/register.html', context={'msg': '用户名或者手机号已经存在,请重新填写!'})


def user_login(request):
    """
    用户登陆
    :param request:
    :return:
    """
    if request.method == 'GET':
        return render(request, 'user/login.html')
    else:
        lform = LoginForm(request.POST)
        print('--------》', lform)
        print("errors", lform.errors)
        if lform.is_valid():
            username = lform.cleaned_data.get('username')
            password = lform.cleaned_data.get('password')
            # 查询数据库,如果加密后的两个密码一致的话登录成功
            user = UserProfile.objects.filter(username=username).first()
            flag = check_password(password, user.password)
            if flag:
                # 登陆成功后,保存session信息,并进入首页
                # session信息会保存到django_session表中,并进行base64加密
                request.session['username'] = username
                return redirect(reverse('index'))
        return render(request, 'user/login.html', context={'errors': lform.errors})


def user_logout(request):
    """
    用户注销
    :param request:
    :return:
    """
    # 方法一、可自行清空session,再重定向到首页
    # request.session.clear() # 仅删除字典
    # 用户注销后,把session给清空,并且重定向回首页
    # request.session.flush()  # 删除django_session +cookie + 字典
    # return redirect(reverse('index'))

    # 方法二、若model类继承了AbstractUser,可直接使用系统自带的退出登录,即logout;不需要自己去清空session
    logout(request)
    return redirect(reverse('index'))

3、models.py

from django.contrib.auth.models import AbstractUser
from django.db import models

# Create your models here.

"""
    数据库模型类
"""


#  继承AbstractUser,可以使用他本身的登录/退出登录方法;
#  同时也可以继承auth_user本身的所有数据库字段
class UserProfile(AbstractUser):
    # 这里我们设置mobile为唯一,之后方便用于登录校验
    mobile = models.CharField(max_length=11, verbose_name="手机号", unique=True)
    # icon的图片我们指定生成到media文件夹里,并且记得去settings里面进行配置
    # media创建在static同级目录下
    # upload_to表示文件上传的路径,uploads/%Y/%m/%d:他会在media文件底下依次创建2019--05--文件名
    icon = models.ImageField(upload_to='uploads/%Y/%m/%d')

    class Meta:
        db_table = 'userprofile'
        verbose_name = '用户表'
        verbose_name_plural = verbose_name
        

4、forms.py

  • 在user应用下新建forms.py,为了进行表单校验

    这里的表单校验我写了两种方法,详细可看代码注释

    • 方法一、继承Form
    • 方法二、继承ModelForm

import re

from django import forms
from django.core.exceptions import ValidationError
from django.forms import Form
from django.forms.models import ModelForm

from user.models import UserProfile


# 写法一、继承From的写法
# class UserRegisterForm(Form):
#     username = forms.CharField(max_length=50, min_length=6, error_messages={'min_lengh': "用户名至少6位"}, label="用户名")
#     email = forms.EmailField(required=True, error_messages={'required': "必须填写邮箱信息"}, label='邮箱')
#     mobile = forms.CharField(required=True, error_messages={'required': "必须填写手机号"}, label='手机号')
#     # widget=forms.widgets.PasswordInput 输入框为密码格式
#     password = forms.CharField(required=True, error_messages={'required': "必须填写密码"}, label='密码',
#                                widget=forms.widgets.PasswordInput)
#
#     def clean_username(self):
#         username = self.cleaned_data.get('username')
#         # username 正则匹配
#         result = re.match(r'[a-zA-Z]\w{5,}', username)
#         if not result:
#             raise ValidationError('用户名必须字母开头')
#         return username


# 写法二、这里是继承modelForm的写法,继承modelForm可以获取到model里面所有的值
class RegisterForm(ModelForm):
    """
        注册表单
    """

    class Meta:
        # 获取到模型里面的属性
        model = UserProfile
        # fields = '__all__’ 即获取所有属性
        # exclude = ['username','email'] 即排除这些字段
        fields = ['username', 'email', 'mobile', 'password']

    def clean_username(self):
        username = self.cleaned_data.get('username')
        # username 正则匹配
        result = re.match(r'[a-zA-Z]\w{5,}', username)
        if not result:
            raise ValidationError('用户名必须字母开头')
        return username


class LoginForm(Form):
    """
        登录表单
    """
    username = forms.CharField(max_length=50, min_length=6, error_messages={'min_lengh': "用户名至少6位"}, label="用户名")
    password = forms.CharField(required=True, error_messages={'required': "必须填写密码"}, label='密码',
                               widget=forms.widgets.PasswordInput)

    def clean_username(self):
        # 拿到表单里面的用户名
        username = self.cleaned_data.get('username')
        # 校验数据库里面这个用户名是否存在,不存在就抛出异常
        if not UserProfile.objects.filter(username=username).exists():
            raise ValidationError('用户名不存在')
        return username
        


五、templates模块具体实现

1、首先创建base.html

  • 为什么要创建base.html?
    base页面主要是从原静态页面index.html提取出来的公用代码,为了提高代码的复用性,其他页面之后直接继承该页面即可

{% load staticfiles %}
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}
        Mikasa个人博客
    {% endblock %}</title>
    <meta name="keywords" content="个人博客,Mikasa个人博客,个人博客模板,Mikasa"/>
    <meta name="description" content="Mikasa个人博客"/>
    <link href="{% static 'css/base.css' %}" rel="stylesheet">
    <link href="{% static 'css/index.css' %}" rel="stylesheet">
    <!--[if lt IE 9]>
    <script src="{% static 'js/modernizr.js' %}"></script>
    <![endif]-->
    <script src="{% static 'js/scrollReveal.js' %}"></script>
    {# 如果要使用jquery的话,记得把jquery引用加上,可以 直接去百度 一下搜索一个版本直接复制粘贴#}
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    {% block mycss %}

    {% endblock %}
</head>
<body>

<header>
    <br><br>
    <div style="text-align: right">
        {# 如果session里面有值的话就显示:欢迎!用户名,如果没有值的话就显示:登录/注册   #}
        {% if  request.session.username %}
            欢迎!<a href="#">{{ request.session.username }}</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <a href="{% url 'user:logout' %}">注销登录</a>
        {% else %}
            <a href="{% url 'user:login' %}">登录</a>
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="{% url 'user:register' %}">注册</a>
        {% endif %}
    </div>

    <div class="logo" data-scroll-reveal="enter right over 1s"><a href="/"><img
            src="{% static 'images/logo2.png' %}"></a></div>

    <nav class="topnav" data-scroll-reveal="enter bottom over 1s after 1s"><a
            href="{% url 'index' %}"><span>首页</span><span
            class="en">Home</span></a><a href="about.html"><span>关于我</span><span class="en">About</span></a><a
            href="manshenghuo.html"><span>慢生活</span><span class="en">Life</span></a><a
            href="learn.html"><span>碎言碎语</span><span class="en">Doing</span></a><a
            href="learn.html"><span>学无止境</span><span class="en">Learn</span></a><a
            href="gbook.html"><span>留言</span><span class="en">Saying</span></a></nav>
</header>

<article>
    <div class="container">
        {% block content %}

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

<footer>
    Design by DanceSmile <a href="/">粤ICP备11002373号-1</a>
</footer>

<script>
    if (!(/msie [6|7|8|9]/i.test(navigator.userAgent))) {
        (function () {
            window.scrollReveal = new scrollReveal({reset: true});
        })();
    }
    ;
</script>
{# js #}
{% block myjs %}

{% endblock %}

</body>
</html>

2、创建index.html

  • 该页面为首页,目前很多东西都是写死的,之后我们再优化
{% extends 'base.html' %}
{% load staticfiles %}
{% block content %}
    <div class="blog" data-scroll-reveal="enter top">
        <figure>
            <ul>
                <a href="/"><img src="{% static 'images/t01.jpg' %}" ><span>下载个人博客模板</span></a>
            </ul>
            <p><a href="/">灯具公司复古风格PSD设计稿</a></p>
            <figcaption>此模板为PSD设计稿,复古风格。首页主要突出产品,以及公司简介。手绘灯作为头部背景图片,这个比较特别。html可以做出灯一闪一闪的效果,或者说旁边有个按钮...</figcaption>
        </figure>
        <figure>
            <ul>
                <a href="/"><img src="{% static 'images/t02.jpg' %}" ><span>下载个人博客模板</span></a>
            </ul>
            <p><a href="/">个人博客模板古典系列之——江南墨..</a></p>
            <figcaption>一共是四个页面,首页,图文列表,图片列表,文字内容。此模板风格为中国古典风格,山水画墨迹成就一幅江南墨卷。页面首页设计较为简单,突出文章重点。图文列表显示...</figcaption>
        </figure>
        <figure>
            <ul>
                <a href="/"><img src="{% static 'images/t03.jpg' %}" ><span>下载个人博客模板</span></a>
            </ul>
            <p><a href="/">美丽的茧</a></p>
            <figcaption>让世界拥有它的脚步,让我保有我的茧。当溃烂已极的心灵再不想做一丝一毫的思索时,就让我静静回到我的茧内,以回忆为睡榻,以悲哀为覆被,这是我唯一的美丽。</figcaption>
        </figure>
    </div>
    <ul class="cbp_tmtimeline">
        <li>
            <time class="cbp_tmtime"><span>08-08</span> <span>2017</span></time>
            <div class="cbp_tmicon"></div>
            <div class="cbp_tmlabel" data-scroll-reveal="enter right over 1s">
                <h2>三步实现滚动条触动css动画效果</h2>
                <p><span class="blogpic"><a href="/"><img src="{% static 'images/t03.jpg' %}"></a></span>现在很多网站都有这种效果,我就整理了一下,分享出来。利用滚动条来实现动画效果,ScrollReveal.js
                    用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力...</p>
                <a href="/" target="_blank" class="readmore">阅读全文&gt;&gt;</a>
            </div>
        </li>
        <li>
            <time class="cbp_tmtime"><span>08-08</span> <span>2017</span></time>
            <div class="cbp_tmicon"></div>
            <div class="cbp_tmlabel" data-scroll-reveal="enter right over 1s">
                <h2>三步实现滚动条触动css动画效果</h2>
                <p><span class="blogpic"><a href="/"><img src="{% static 'images/t02.jpg' %}"></a></span>现在很多网站都有这种效果,我就整理了一下,分享出来。利用滚动条来实现动画效果,ScrollReveal.js
                    用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力...</p>
                <a href="/" target="_blank" class="readmore">阅读全文&gt;&gt;</a>
            </div>
        </li>
        <li>
            <time class="cbp_tmtime"><span>08-08</span> <span>2017</span></time>
            <div class="cbp_tmicon"></div>
            <div class="cbp_tmlabel" data-scroll-reveal="enter right over 1s">
                <h2>三步实现滚动条触动css动画效果</h2>
                <p><span class="blogpic"><a href="/"><img src="{% static 'images/t01.jpg' %}" ></a></span>现在很多网站都有这种效果,我就整理了一下,分享出来。利用滚动条来实现动画效果,ScrollReveal.js
                    用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力...</p>
                <a href="/" target="_blank" class="readmore">阅读全文&gt;&gt;</a>
            </div>
        </li>
        <li>
            <time class="cbp_tmtime"><span>08-08</span> <span>2017</span></time>
            <div class="cbp_tmicon"></div>
            <div class="cbp_tmlabel" data-scroll-reveal="enter right over 1s">
                <h2>三步实现滚动条触动css动画效果</h2>
                <p><span class="blogpic"><a href="/"><img src="{% static 'images/t03.jpg' %}" ></a></span>现在很多网站都有这种效果,我就整理了一下,分享出来。利用滚动条来实现动画效果,ScrollReveal.js
                    用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力...</p>
                <a href="/" target="_blank" class="readmore">阅读全文&gt;&gt;</a>
            </div>
        </li>
        <li>
            <time class="cbp_tmtime"><span>08-08</span> <span>2017</span></time>
            <div class="cbp_tmicon"></div>
            <div class="cbp_tmlabel" data-scroll-reveal="enter right over 1s">
                <h2>三步实现滚动条触动css动画效果</h2>
                <p><span class="blogpic"><a href="/"><img src="{% static 'images/t02.jpg' %}" ></a></span>现在很多网站都有这种效果,我就整理了一下,分享出来。利用滚动条来实现动画效果,ScrollReveal.js
                    用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力...</p>
                <a href="/" target="_blank" class="readmore">阅读全文&gt;&gt;</a>
            </div>
        </li>
        <li>
            <time class="cbp_tmtime"><span>08-08</span> <span>2017</span></time>
            <div class="cbp_tmicon"></div>
            <div class="cbp_tmlabel" data-scroll-reveal="enter right over 1s">
                <h2>三步实现滚动条触动css动画效果</h2>
                <p><span class="blogpic"><a href="/"><img src="{% static 'images/t01.jpg' %}" ></a></span>现在很多网站都有这种效果,我就整理了一下,分享出来。利用滚动条来实现动画效果,ScrollReveal.js
                    用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力...</p>
                <a href="/" target="_blank" class="readmore">阅读全文&gt;&gt;</a>
            </div>
        </li>
        <li>
            <time class="cbp_tmtime"><span>08-08</span> <span>2017</span></time>
            <div class="cbp_tmicon"></div>
            <div class="cbp_tmlabel" data-scroll-reveal="enter right over 1s">
                <h2>三步实现滚动条触动css动画效果</h2>
                <p><span class="blogpic"><a href="/"><img src="{% static 'images/t03.jpg' %}" ></a></span>现在很多网站都有这种效果,我就整理了一下,分享出来。利用滚动条来实现动画效果,ScrollReveal.js
                    用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力...</p>
                <a href="/" target="_blank" class="readmore">阅读全文&gt;&gt;</a>
            </div>
        </li>

    </ul>
{% endblock %}

3、在templates下创建user文件夹

  • 为了代码更规范,我们将不同功能的页面放在一个文件夹下;比如:用户相关的登陆/注册页面,统一建个user文件夹,放其下面

3.1、login.html

{% extends 'base.html' %}
{% load staticfiles %}

{% block title %}
    用户登录
{% endblock %}

{# css样式部分 #}
{% block mycss %}
    <link href="{% static 'css/register.css' %}" rel="stylesheet" type="text/css" media="all"/>
    <link href="//fonts.googleapis.com/css?family=Montserrat:100,100i,200,200i,300,300i,400,400i,500,500i,
    600,600i,700,700i,800,800i" rel="stylesheet"/>
{% endblock %}

{# 内容部分 #}
{% block content %}
    <div class="main">
        <div class="main-w31">
            <h1 class="logo-w3">个人博客用户登录</h1>
            <div class="w3layouts-main">
                <h2><span>请登录</span></h2>
                <p style="color: red">{{ msg }}{{ errors }}</p>
                <form action="{% url 'user:login' %}" method="post"> {% csrf_token %}
                    <input placeholder="用户名" name="username" type="text" required="">
                    <input placeholder="密码" name="password" type="password" id="password1" required="">
                    <input type="submit" value="登录" name="login">
                </form>
            </div>
        </div>
    </div>
{% endblock %}

3.2、register.html

{% extends 'base.html' %}
{% load staticfiles %}

{% block title %}
    用户注册
{% endblock %}
{# css样式部分 #}
{% block mycss %}
    <link href="{% static 'css/register.css' %}" rel="stylesheet" type="text/css" media="all"/>
    <link href="https://fonts.googleapis.com/css?family=Montserrat:100,100i,200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i"
          rel="stylesheet"/>
{% endblock %}
{# 内容部分 #}
{% block content %}
    <div class="main">
        <div class="main-w31">
            <h1 class="logo-w3">个人博客用户注册</h1>
            <div class="w3layouts-main">
                <h2><span>现在注册</span></h2>
                <p>{{ msg }}{{ errors }}</p>
                <form action="{% url 'user:register' %}" method="post"> {% csrf_token %}
                    <input placeholder="用户名" name="username" type="text" required=""><br>
                    <input placeholder="邮箱" name="email" type="email" required=""><br>
                    <input placeholder="手机号码" name="mobile" type="text" required=""><br>
                    <input placeholder="密码" name="password" type="password" id="password1" required=""><br>
                    <input placeholder="确认密码" name="password" type="password" id="password2" required=""><br>
                    <input type="submit" value="提交注册" name="login"><br>
                </form>
            </div>
        </div>
    </div>
{% endblock %}

{# js部分 #}
{% block myjs %}
    <script>
        addEventListener("load", function () {
            setTimeout(hideURLbar, 0);
        }, false);

        function hideURLbar() {
            window.scrollTo(0, 1);
        }
    </script>

    <script>
        {# 密码校验    #}
        window.onload = function () {
            document.getElementById("password1").onchange = validatePassword;
            document.getElementById("password2").onchange = validatePassword;
        }

        function validatePassword() {
            var pass1 = document.getElementById("password1").value;
            var pass2 = document.getElementById("password2").value;
            if (pass1 != pass2)
                document.getElementById("password2").setCustomValidity("密码不匹配")
            else
                document.getElementById("password2").setCustomValidity("")
        }
    </script>
{% endblock %}


六、登录/注册界面样式代码

1、register.css

  • 原静态模板下是没有登录/注册界面的,所以样式也需要自己调,放css下即可

@charset "UTF-8";

/*初始化所有默认样式*/
ol, ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

blockquote, q {
    quotes: none;
}

blockquote:before, blockquote:after, q:before, q:after {
    content: '';
    content: none;
}

table {
    border-collapse: collapse;
    border-spacing: 0;
}

/* start editing from here */
a {
    text-decoration: none;
}

.txt-rt {
    text-align: right;
}

/* text align right */
.txt-lt {
    text-align: left;
}

/* text align left */
.txt-center {
    text-align: center;
}

/* text align center */
.float-rt {
    float: right;
}

/* float right */
.float-lt {
    float: left;
}

/* float left */
.clear {
    clear: both;
}

/* clear float */
.pos-relative {
    position: relative;
}

/* Position Relative */
.pos-absolute {
    position: absolute;
}

/* Position Absolute */
.vertical-base {
    vertical-align: baseline;
}

/* vertical align baseline */
.vertical-top {
    vertical-align: top;
}

/* vertical align top */
/*.underline {*/
/*    padding-bottom: 5px;*/
/*    border-bottom: 1px solid #eee;*/
/*    margin: 0 0 20px 0;*/
/*}*/

/* Add 5px bottom padding and a underline */
nav.vertical ul li {
    display: block;
}

/* vertical menu */
nav.horizontal ul li {
    display: inline-block;
}

/* horizontal menu */
img {
    max-width: 100%;
}

/*end reset*/

body a {
    transition: 0.5s all;
    -webkit-transition: 0.5s all;
    -o-transition: 0.5s all;
    -moz-transition: 0.5s all;
    -ms-transition: 0.5s all;
}

ul {
    padding: 0;
    margin: 0;
}

h1, h2, h3, h4, h5, h6 {
    margin: 0;
    padding: 0;
}

p {
    padding: 0px;
    margin: 10px;
    color: #ff4444;
    font-size: 18px;
}

/* main */
.main {
    text-align: center;
}

h1.logo-w3 {
    font-size: 3em;
    text-transform: uppercase;
    letter-spacing: 4px;
    color: #ffffff;
    text-align: center;
    margin: 5% 0;
    width: 100%;
}


.w3layouts-main h2 {
    color: #fff;
    font-size: 29px;
    letter-spacing: 2px;
    text-transform: uppercase;
    margin-bottom: 30px;
    text-align: center;
}


.w3layouts-main {
    max-width: 420px;
    margin: 0 auto;
    background: rgba(0, 0, 0, 0.2);
    text-align: center;
    -webkit-box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.75);
    -moz-box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.75);
    box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.75);
}

.w3layouts-main {
    padding: 40px 30px;
}

input[type="text"], input[type="email"], input[type="password"] {
    width: 100%;
    padding: 15px;
    outline: none;
    font-size: 15px;
    font-weight: 100;
    color: #FFFFFF;
    margin-bottom: 20px;
    font-family: 'Montserrat', sans-serif;
    border: 1px solid #fff;
    background: transparent;
    background: rgba(0, 0, 0, 0.2);
    letter-spacing: 1px;
    border-radius: 3px;
    -webkit-border-radius: 3px;
    -moz-border-radius: 3px;
    -ms-border-radius: 3px;
    box-sizing: border-box;
}

input[type="submit"] {
    width: 100%;
    padding: 14px 30px;
    font-size: 14px;
    -webkit-border-radius: 3px;
    -moz-border-radius: 3px;
    -ms-border-radius: 3px;
    -o-border-radius: 3px;
    text-transform: uppercase;
    letter-spacing: 1px;
    background: #fff;
    color: #333;
    border: none;
    outline: none;
    cursor: pointer;
    font-family: 'Montserrat', sans-serif;
    transition: 0.5s all;
    -webkit-transition: 0.5s all;
    -o-transition: 0.5s all;
    -moz-transition: 0.5s all;
    -ms-transition: 0.5s all;
}

input[type="submit"]:hover {
    background: #ff4f81;
    color: #fff;
    transition: 0.5s all;
    -webkit-transition: 0.5s all;
    -o-transition: 0.5s all;
    -moz-transition: 0.5s all;
    -ms-transition: 0.5s all;
}

.w3layouts-main2 input[type="submit"] {
    margin: 27px auto 31px;
}

/* placeholder-color*/
::-webkit-input-placeholder {
    color: #fff;
}

:-moz-placeholder {
    color: #FFFFFF;
}

::-moz-placeholder {
    color: #FFFFFF;
}

:-ms-input-placeholder {
    color: #FFFFFF;
}

/* footer */
.footer-w31 p {
    margin: 3.5em 0em 0em;
    color: #FFFFFF;
    font-size: 15px;
    font-weight: 300;
    letter-spacing: 2px;
    line-height: 26px;
}

.footer-w31 a {
    color: #100e0e;
    font-family: 'Montserrat', sans-serif;
}

.footer-w31 a:hover {
    color: #FFFFFF;
    text-decoration: underline;
}

/* responsive */
@media (max-width: 1600px) {
    h1.logo-w3 {
        margin: 1em 0;
    }
}

@media (max-width: 1440px) {
    h1.logo-w3 {
        font-size: 2.8em;
    }
}

@media (max-width: 1366px) {
    h1.logo-w3 {
        font-size: 2.7em;
    }

    .footer-w31 p {
        margin: 3em 0em 2em;
    }
}

@media (max-width: 1280px) {
    h1.logo-w3 {
        font-size: 2.8em;
    }

    .footer-w31 p {
        margin: 4.5em 0em 0em;
    }
}

@media (max-width: 1080px) {
    h1.logo-w3 {
        font-size: 2.7em;
        letter-spacing: 3px;
    }

    .footer-w31 p {
        margin: 4.5em 0em 0em;
    }
}


七、创建数据库表及启动项目

1、创建数据库表

  • 正常情况下是直接创建成功,我这里遇到了问题,详细可以看下后面
python manage.py makemigrations

python manage.py migrate


1.1)在这儿是遇到了问题:ModuleNotFoundError: No module named ‘MySQLdb’

解决方法

import pymysql

pymysql.install_as_MySQLdb()


2、访问项目

有关Django(二)精美博客搭建(1)实现登录/注册功能的更多相关文章

  1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  2. ruby-on-rails - Cucumber 是否只是 rspec 的包装器以帮助将测试组织成功能? - 2

    只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您

  3. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  4. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  5. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  6. 阿里云国际版免费试用:如何注册以及注意事项 - 2

    作为新的阿里云用户,您可以50免费试用多种优惠,价值高达1,700美元(或8,500美元)。这将让您了解和体验阿里云平台上提供的一系列产品和服务。如果您以个人身份注册免费试用,您将获得价值1,700美元的优惠。但是,如果您是注册公司,您可以选择企业免费试用,提交基本信息通过企业实名注册验证,即可开始价值$8,500的免费试用!本教程介绍了如何设置您的帐户并使用您的免费试用版。​关于免费试用在我们开始此试用之前,您还必须遵守以下条款和条件才能访问您的免费试用:只有在一年内创建的账户才有资格获得阿里云免费试用。通过此免费试用优惠,用户可以免费试用免费试用活动页面上列出的每种产品一次。如果您有多个帐

  7. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  8. ruby - Arrays Sets 和 SortedSets 在 Ruby 中是如何实现的 - 2

    通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复

  9. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

  10. ruby - "public/protected/private"方法是如何实现的,我该如何模拟它? - 2

    在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定

随机推荐