之前我们用Django框架做了一个很简单的个人博客搭建,不论是页面还是功能都很粗糙
所以从这篇开始我打算做一个比较完整的【个人博客网站】,可能会分好几篇博客来讲述
等所有功能完善的差不多后,再考虑上传github
-
那本篇呢,我们主要实现博客的基础框架搭建,以及【登陆/注册/注销】功能的实现
我会顺便详细介绍一下小知识点,以及我踩的坑 -
简易版blog搭建:https://blog.csdn.net/Makasa/article/details/124982130?spm=1001.2014.3001.5502
环境:
- Pycharm
- python3.6
- mysql 5.7
- django 2.0.13
一、整体框架介绍 1、创建Django项目
- 直接在pycharm里新建一个Django项目即可
2、框架简要介绍
3、项目概览
二、准备工作 1、创建blog数据库
- 数据库我这里用的是mysql5.7
2、将静态资源移入项目
- 在应用同级目录下,创建static文件夹;且将静态资源复制在其下
静态资源下载地址:https://www.ip3q.com/free/1243.html
三、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 %}
{% block title %}
Mikasa个人博客
{% endblock %}
{# 如果要使用jquery的话,记得把jquery引用加上,可以 直接去百度 一下搜索一个版本直接复制粘贴#}
{% block mycss %}
{% endblock %}
{# 如果session里面有值的话就显示:欢迎!用户名,如果没有值的话就显示:登录/注册 #}
{% if request.session.username %}
欢迎!{{ request.session.username }}
注销登录
{% else %}
登录
注册
{% endif %}
{% block content %}
{% endblock %}
{# js #}
{% block myjs %}
{% endblock %}
2、创建index.html
- 该页面为首页,目前很多东西都是写死的,之后我们再优化
{% extends 'base.html' %}
{% load staticfiles %}
{% block content %}
此模板为PSD设计稿,复古风格。首页主要突出产品,以及公司简介。手绘灯作为头部背景图片,这个比较特别。html可以做出灯一闪一闪的效果,或者说旁边有个按钮...
一共是四个页面,首页,图文列表,图片列表,文字内容。此模板风格为中国古典风格,山水画墨迹成就一幅江南墨卷。页面首页设计较为简单,突出文章重点。图文列表显示...
让世界拥有它的脚步,让我保有我的茧。当溃烂已极的心灵再不想做一丝一毫的思索时,就让我静静回到我的茧内,以回忆为睡榻,以悲哀为覆被,这是我唯一的美丽。
-
三步实现滚动条触动css动画效果
阅读全文>>
现在很多网站都有这种效果,我就整理了一下,分享出来。利用滚动条来实现动画效果,ScrollReveal.js 用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力... -
三步实现滚动条触动css动画效果
阅读全文>>
现在很多网站都有这种效果,我就整理了一下,分享出来。利用滚动条来实现动画效果,ScrollReveal.js 用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力... -
三步实现滚动条触动css动画效果
阅读全文>>
现在很多网站都有这种效果,我就整理了一下,分享出来。利用滚动条来实现动画效果,ScrollReveal.js 用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力... -
三步实现滚动条触动css动画效果
阅读全文>>
现在很多网站都有这种效果,我就整理了一下,分享出来。利用滚动条来实现动画效果,ScrollReveal.js 用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力... -
三步实现滚动条触动css动画效果
阅读全文>>
现在很多网站都有这种效果,我就整理了一下,分享出来。利用滚动条来实现动画效果,ScrollReveal.js 用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力... -
三步实现滚动条触动css动画效果
阅读全文>>
现在很多网站都有这种效果,我就整理了一下,分享出来。利用滚动条来实现动画效果,ScrollReveal.js 用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力... -
三步实现滚动条触动css动画效果
阅读全文>>
现在很多网站都有这种效果,我就整理了一下,分享出来。利用滚动条来实现动画效果,ScrollReveal.js 用于创建和管理元素进入可视区域时的动画效果,帮助你的网站增加吸引力...
3、在templates下创建user文件夹
- 为了代码更规范,我们将不同功能的页面放在一个文件夹下;比如:用户相关的登陆/注册页面,统一建个user文件夹,放其下面
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}
用户登录
{% endblock %}
{# css样式部分 #}
{% block mycss %}
{% endblock %}
{# 内容部分 #}
{% block content %}
个人博客用户登录
请登录
{{ msg }}{{ errors }}
{% endblock %}
3.2、register.html
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}
用户注册
{% endblock %}
{# css样式部分 #}
{% block mycss %}
{% endblock %}
{# 内容部分 #}
{% block content %}
个人博客用户注册
现在注册
{{ msg }}{{ errors }}
{% endblock %}
{# js部分 #}
{% block myjs %}
{% 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;
}
a {
text-decoration: none;
}
.txt-rt {
text-align: right;
}
.txt-lt {
text-align: left;
}
.txt-center {
text-align: center;
}
.float-rt {
float: right;
}
.float-lt {
float: left;
}
.clear {
clear: both;
}
.pos-relative {
position: relative;
}
.pos-absolute {
position: absolute;
}
.vertical-base {
vertical-align: baseline;
}
.vertical-top {
vertical-align: top;
}
nav.vertical ul li {
display: block;
}
nav.horizontal ul li {
display: inline-block;
}
img {
max-width: 100%;
}
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 {
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;
}
::-webkit-input-placeholder {
color: #fff;
}
:-moz-placeholder {
color: #FFFFFF;
}
::-moz-placeholder {
color: #FFFFFF;
}
:-ms-input-placeholder {
color: #FFFFFF;
}
.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;
}
@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、访问项目




