专题:Django2.0.8+xadmin2实现在线学习网站
Django2.0.8+xadmin2实现在线学习网站,课程、讲师、机构、用户收藏功能。GitHub地址:https://github.com/xyliurui/OnlineLearningPlatform ;Django版本:2.0.8
更多内容请点击 我的博客 查看,欢迎来访。
模板和资源位置
在项目中创建templates目录,用于放置模板文件
在项目中创建static目录,用来存放css, js等静态文件
配置templates目录路径
在项目下新建templates目录用于存放模板文件,在settings.py中添加templates的目录,让它能被搜索到。
修改settings.py配置,添加模板文件的目录
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',
],
},
},
]
配置static目录路径
用于放置静态文件。修改settings.py配置
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
创建临时的首页index.html
在templates目录下,创建index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
暂无内容
</body>
</html>
然后配置首页url,在项目主url中,使用Django提供的TemplateView
用于处理静态文件
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('xadmin/', xadmin.site.urls),
path('', TemplateView.as_view(template_name='index.html'), name='index'),
]
访问 http://127.0.0.1:8000/ 即可看到首页的内容
创建登录
登录login.html
创建登录模板文件
url配置跳转登录页面
urlpatterns = [
path('admin/', admin.site.urls),
path('xadmin/', xadmin.site.urls),
path('', TemplateView.as_view(template_name='index.html'), name='index'),
path('login/', TemplateView.as_view(template_name='login.html'), name='login'),
]
访问 http://127.0.0.1:8000/login/ 即可访问登录页面
使用函数实现用户登录user_login(request)
配置url之前我们要书写好对应处理的view
Django的view实际就是一个函数,接收request请求对象,处理后返回response对象。
用户登录视图
users/views.py
from django.shortcuts import render
# 当我们配置url被这个view处理时,自动传入request对象
def user_login(request):
if request.method == 'POST':
pass
elif request.method == 'GET':
return render(request, 'login.html', {})
用户登录url
在项目的主url中添加
from users.views import user_login
urlpatterns = [
path('admin/', admin.site.urls),
path('xadmin/', xadmin.site.urls),
path('', TemplateView.as_view(template_name='index.html'), name='index'),
# path('login/', TemplateView.as_view(template_name='login.html'), name='login'),
path('login/', user_login, name='login'),
]
现在访问 http://127.0.0.1:8000/login/ 仍会跳转到同一个页面
用户登录表单
templates/login.html
可以看到form表单中有input。点击提交会把值提交到后台。我们需要修改action让它指向我们的后台相应地址。input中的name值会被传递到后台。回组成键值对形式。
<h3 class="no-margins">登录</h3>
<p class="m-t-md">登录到学习平台</p>
<form method="post" action="/login/">
<input name='username' type="text" class="form-control uname" placeholder="用户名" />
<input name='password' type="password" class="form-control pword m-b" placeholder="密码" />
{% csrf_token %}
<a href="">忘记密码了?</a>
<button type="submit" class="btn btn-success btn-block">登录</button>
</form>
BLOG_20190604_135101_16
html页面内必须加上crsf token
才能传值到后台。
系统会随机的给前端发一串符号,必须把这串符号带回来,才允许post
用户登录post逻辑
from django.shortcuts import render
from django.contrib.auth import login, authenticate
# 当我们配置url被这个view处理时,自动传入request对象
def user_login(request):
if request.method == 'POST':
user_name = request.POST.get('username', '')
pass_word = request.POST.get('password', '')
user = authenticate(username=user_name, password=pass_word)
# 认证成功返回user对象,失败返回null
if user:
login(request, user)
return render(request, 'index.html')
else:
return render(request, 'login.html', {})
elif request.method == 'GET':
return render(request, 'login.html', {})
支持邮箱登录
自定义authenticate方法
users/views/py
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
from .models import UserProfile
# 自定义登录,可使用邮箱和账号
class CustomBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
# 不希望用户存在两个,get只能有一个。两个是get失败的一种原因 Q为使用并集查询
user = UserProfile.objects.get(Q(username=username) | Q(email=username))
# django的后台中密码加密:所以不能password==password
# UserProfile继承的AbstractUser中有def check_password(self, raw_password)
if user.check_password(password):
return user
except Exception as e:
return None
添加登录后台设置
修改settings.py,添加
# 设置邮箱和用户名均可登录
AUTHENTICATION_BACKENDS = (
'users.views.CustomBackend',
)
添加登录错误提示
模板添加错误信息提示
<form method="post" action="/login/">
<input name='username' type="text" class="form-control uname" placeholder="用户名" />
<input name='password' type="password" class="form-control pword m-b" placeholder="密码" />
{% if msg %}
<div class="alert alert-danger" style="padding: 5px;">
{{ msg }}
</div>
{% endif %}
{% csrf_token %}
<a href="">忘记密码了?</a>
<button type="submit" class="btn btn-success btn-block">登录</button>
</form>
视图添加错误信息返回
def user_login(request):
if request.method == 'POST':
user_name = request.POST.get('username', '')
pass_word = request.POST.get('password', '')
user = authenticate(username=user_name, password=pass_word)
# 认证成功返回user对象,失败返回null
if user:
login(request, user)
return render(request, 'index.html')
else:
return render(request, 'login.html',
{
'msg': '用户名或密码错误!'
})
elif request.method == 'GET':
return render(request, 'login.html', {})
基于类的视图实现用户登录LoginView(View)
基础教程中基本上都是基于函数来做的,其实更推荐基于类来做。基于类可以带来不少好处
基于类的登录视图LoginView(View)
from django.views.generic import View
# 基于类的视图实现登录
class LoginView(View):
def get(self, request):
return render(request, 'login.html', {})
def post(self, request):
user_name = request.POST.get('username', '')
pass_word = request.POST.get('password', '')
user = authenticate(username=user_name, password=pass_word)
# 认证成功返回user对象,失败返回null
if user:
login(request, user)
return render(request, 'index.html')
else:
return render(request, 'login.html',
{
'msg': '用户名或密码错误!'
})
基于类的登录url
from users.views import user_login, LoginView
urlpatterns = [
path('admin/', admin.site.urls),
path('xadmin/', xadmin.site.urls),
path('', TemplateView.as_view(template_name='index.html'), name='index'),
# path('login/', TemplateView.as_view(template_name='login.html'), name='login'),
# path('login/', user_login, name='login'),
path('login/', LoginView.as_view(), name='login'), # 基于类方法实现登录,这里是调用它的方法
]
form字段验证
验证最大长度,是否为空等一系列。
users下新建forms文件。编辑
from django import forms
class LoginForm(forms.Form):
# 用户名和密码不能为空
username = forms.CharField(required=True)
password = forms.CharField(required=True, min_length=5)
form加入到登录逻辑LoginView(View)
定义好forms之后我们来使用它做验证。
from .forms import LoginForm
# 基于类的视图实现登录
class LoginView(View):
def get(self, request):
return render(request, 'login.html', {})
def post(self, request):
login_form = LoginForm(request.POST)
if login_form.is_valid():
user_name = request.POST.get('username', '')
pass_word = request.POST.get('password', '')
user = authenticate(username=user_name, password=pass_word)
# 认证成功返回user对象,失败返回null
if user:
login(request, user)
return render(request, 'index.html')
else:
return render(request, 'login.html',
{
'msg': '用户名或密码错误!',
'login_form': login_form,
})
else:
return render(request, 'login.html',
{
'login_form': login_form,
})
完善错误提示login.html
<h3 class="no-margins">登录</h3>
<p class="m-t-md">登录到学习平台</p>
<form method="post" action="/login/">
<input name='username' value="{% if login_form.username.value %}{{ login_form.username.value }}{% endif %}" type="text" class="form-control uname" placeholder="用户名" />
{% if login_form.errors.username %}
<span id="cname-error" class="help-block m-b-none"> {{ login_form.errors.username.as_text }}</span>
{% endif %}
<input name='password' type="password" class="form-control pword m-b" placeholder="密码" />
{% if login_form.errors.password %}
<span id="cname-error" class="help-block m-b-none"> {{ login_form.errors.password.as_text }}</span>
{% endif %}
{% if msg %}
<div class="alert alert-danger" style="padding: 5px;">
{{ msg }}
</div>
{% endif %}
{% csrf_token %}
<a href="">忘记密码了?</a>
<button type="submit" class="btn btn-success btn-block">登录</button>
</form>
- 写了一个类继承Django的view,然后写了
get
post
方法(get/post的if是Django替我们完成的) - 在url中调用
Loginview
的as_view
方法需要加上括号,进行调用。 - Django的form进行表单验证并把
error
值传到前台。 -
is_valid
方法,验证表单
session和cookie自动登录机制
cookie的存储
cookie是浏览器支持的一种本地存储方式。以dict,键值对方式存储。
{"sessionkey": "123"}
浏览器会自动对于它进行解析。
http请求是一种无状态的请求
用户向服务器发起的两次请求之间是没有状态的。也就是服务器并不知道这是同一个用户发的。
a浏览一个新闻,b浏览一个新闻,服务器只需要把新闻返回给客户端就可以,但在没有登录的情况下浏览某些网站,例如淘宝,也是给我们记住的,a浏览了哪些商品,b浏览了哪些商品。
有状态请求
做到记住用户:
浏览器a在向服务器发起请求,服务器会自动给浏览器a回复一个id,浏览器a把id放到cookie当中,在下一次请求时带上这个cookie里的id值向浏览器请求,服务器就知道你是哪个浏览器发过来的了。
服务器a发回来的id会放到服务器a的域之下。不能跨域访问cookie。
使用浏览器随便打开一个网页,然后f12打开。会找到存储在浏览器本地的cookie值。点击clear all清空所有的cookie f5刷新页面,会发现又把这些cookie值进来。
如果将用户名和密码直接保存在cookie,可以实现最垃圾最简略版本的自动登录。
解决cookie放在本地不安全的问题(session)
用户在第一次请求后,浏览器回复的id既可以是用户的user id, 也可以一段任意的字符串,我们把它叫做session id
根据用户名和密码,服务器会采用自己的规则生成session id。这个session id保存在本地cookie。浏览器请求服务器会携带。
- 输入用户名 & 密码
- 调用
login()
, 后端程序会根据用户名密码生成session id。保存在数据库中。 - 用户登录之后,需要通过这个session id取出这些基本信息。
Django的默认表中的session表就记录了用户登录时,后端我们Django为用户生成的sessionid。
可以看到session key value 和过期时间。
我们可以清空这张表的数据。运行项目进行登录。
可以看到我们刚刚生成的session id。
此时通过f12查看浏览器在本地存储的session id。可以看到如下图和我们数据库中的一致。
session_key 发到浏览器叫做session id
通过session id 用户访问任何一个页面都会携带,服务器就会认识。
settings.py中,
'django.contrib.sessions',
这个app会拦截我们每次的request请求,在request中找到session id,然后去数据表中进行查询。
然后通过session key 去找到session data。此时直接为我们取出了user。
在服务器返回浏览器的response中也会直接加上session id
cookie是浏览器本地存储机制,存在域名之下,存储不安全。
服务器在返回id时通过规则生成一串字符,并设置了过期时间。存储在服务器端(数据库)
网友评论