三【用django2.0來開發】會員註冊登錄
本章主要講如何實現會員的前臺註冊登錄, 會涉及到以下模塊
- 簡單的路由設置
- 簡單的模板操作
- 視圖以及session的操作
- 比較復雜的表單
- ajax請求以及json返回資源
使用的還是上一節的model即可
實現註冊功能
# account/forms.py class AccountForm(forms.ModelForm): # ... 忽略代碼 def get_first_error(self): # 獲取所有錯誤轉換成的json格式 errors = self.errors.get_json_data() if errors: for key, message_dicts in errors.items(): # 存在錯誤則返回第一個錯誤信息, 返回string return message_dicts[0].get(‘message‘) return None
創建註冊表單
RegisterForm 會繼承AccountForm
# account/forms.py class RegisterForm(AccountForm): # 設置場景是新增用戶 # 這個不是django默認的, 是我自己加的, 作用是用來不同場景下的實現不同的自定義規則 scene = ‘insert‘ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 因為前端框架使用的是bootstrap, 所以需要給所有的表單元素添加class名稱 # 同樣的, 添加別的屬性也可以使用 self.字段名.widget.attrs.update({‘html屬性名‘, ‘html屬性值‘})實現 for _field in self.fields: self.fields[_field].widget.attrs.update({‘class‘: ‘form-control‘}) # 因為在基類中設置的password字段是非必填項, 而在註冊的時候是必填的, 所以設置password的required屬性為True # 同時我們也不需要status字段, 所以設置為False self.fields[‘password‘].required = True self.fields[‘status‘].required = False class Meta(AccountForm.Meta): # 使用自定義的Form, 就必須指定fields or exclude屬性, 否則報錯 # 只指定表單中藥用到的字段 fields = (‘account‘, ‘password‘, ‘email‘, ‘phone‘) # 新增一個rep_password字段, 讓用戶輸入兩次密碼, 防止出錯 rep_password = forms.CharField( label=‘重復密碼‘, required=True, error_messages={‘required‘: ‘請再次輸入密碼‘}, widget=forms.PasswordInput()) def clean_rep_password(self): # 驗證兩次輸入的密碼是否一致 # 因為在clean_password方法中, 已經加密了cleaned_data[‘password‘], 所以這裏只能取data[‘password‘] if self.data[‘password‘] != self.cleaned_data[‘rep_password‘]: raise ValidationError(‘兩次輸入的密碼不一致‘) return self.cleaned_data[‘rep_password‘]
創建用戶模塊註冊的路由
先在account目錄中創建一個urls.py文件
然後在cms.urls中加載account中的urls.py文件
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path(‘admin/‘, admin.site.urls),
# include方法參數是urls文件的加載地址, 名字可以自己指定
path(‘account/‘, include(‘account.urls‘))
]
然後再account.urls.py中創建登錄路由
# account/urls.py
from django.urls import path
from . import views
urlpatterns = [
# 訪問的url路徑是http://xxx/account/register
# 把請求轉發給register方法
# 這個路由的名稱是account-register
path(‘register/‘, views.register, name=‘account-register‘),
]
註冊的具體邏輯
基本流程:
- 把數據放入form表單中
- 驗證表單
- 將表單中的數據保存到數據庫
- 成功/失敗都返回指定資源
# account/views.py
from django.views.decorators.http import require_http_methods
from .forms import RegisterForm
from cms.utils import return_json
# 指定可以請求的方式
@require_http_methods([‘GET‘, ‘POST‘])
def register(request):
if request.method == ‘POST‘:
# 把所有POST的數據都放入表單中
form = RegisterForm(request.POST)
# 驗證表單中的數據是否正確
if form.is_valid():
# 將數據保存到數據庫
form.save()
# 操作成功, 將數據返回給瀏覽器
return return_json(url=reverse(‘account-index‘))
else:
# 驗證失敗, 從form中獲取到第一個錯誤信息, 返回給瀏覽器
return return_json(code=1, message=form.get_first_error())
else:
form = RegisterForm()
return render(request, ‘account/register.html‘, {‘form‘: form})
在這個方法中加載了一個return_json的方法, 具體代碼:
# cms/utils.py
from django.http import JsonResponse
def return_json(code = 0, message = ‘success‘, data = [], url=‘‘):
return JsonResponse({
‘code‘: code,
‘url‘: url,
‘message‘: message,
})
這個代碼的意思是返回一個Json的資源給瀏覽器解析
插播一個關於模板的配置
django默認的模板目錄在模塊/templates中
比如account模塊的模板目錄就是在account/templates
而在一般的開發過程中會把所有模板放在一起, 所以需要修改配置文件, 指定模板目錄的路徑到根目錄下。
# cms/settings.py
TEMPLATES = [
{
‘BACKEND‘: ‘django.template.backends.django.DjangoTemplates‘,
‘DIRS‘: [
# 將templates目錄放在根目錄, 也就是cms/templates中
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‘,
],
},
},
]
而且靜態資源(css,js,fonts,img)文件一般也放在根目錄下
還是在cms/settings.py中增加配置
STATIC_URL = ‘/static/‘ # 指定靜態文件的訪問url前綴
STATICFILES_DIRS = (‘static‘, ) # 指定靜態文件的目錄地址
這樣模板和靜態資源的新路徑就成了cms/templates, cms/static
樣式文件使用的是bootstrap. 大家可以提前下載, 並且提前放入static文件中
然後目錄就變為以下這樣
cms/
templates/
static/
css/
fonts/
js/
創建註冊表單模板
在templates中創建layout.html 框架文件
layout.html中包含的是所有模板共用的地方, 並且通過block標簽占位符來占位, 別的模板可以繼承layout.html, 然後修改block標簽的占位符中的內容, 可以大幅度減少代碼量
# templates/layout.html
<!-- 加載static模板, 作用是為了使用 {% static ‘filepath‘%} 來加載靜態資源-->
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!-- block名稱為title的占位符-->
<title>{% block title %} {% endblock %}</title>
<!-- 完全的文件地址是static/css/bootstrap.min.css-->
<link rel="stylesheet" href="{% static ‘css/bootstrap.min.css‘%}">
</head>
<body>
{% block body %} {% endblock %}
</body>
<script src="{% static ‘js/jquery.min.js‘ %}"></script>
<script src="{% static ‘js/bootstrap.min.js‘ %}"></script>
<script src="{% static ‘js/layer/layer.js‘ %}"></script>
<script src="{% static ‘js/utils.js‘ %}"></script>
</html>
註冊模板文件,templates/account/register.html
<!-- 繼承layout.html-->
{% extends ‘layout.html‘ %}
<!-- 將占位符名稱為title中的內容改為【註冊】-->
{% block title %} 註冊 {% endblock %}
{% block body %}
<div class="container">
<div class="row" style="width:500px">
<!-- url ‘路由的名稱, 就是urls中的name‘-->
<form action="{% url ‘account-register‘%}" method="post" onsubmit="return post(this)">
{% csrf_token %}
<div class="form-group">
<label for="{{ form.account.id_for_label}}">{{ form.account.label}}</label> {{ form.account}}
</div>
<div class="form-group">
<label for="{{ form.password.id_for_label}}">{{ form.password.label}}</label> {{ form.password}}
</div>
<div class="form-group">
<label for="{{ form.rep_password.id_for_label}}">{{ form.rep_password.label}}</label> {{ form.rep_password}}
</div>
<div class="form-group">
<label for="{{ form.email.id_for_label}}">{{ form.email.label}}</label> {{ form.email}}
</div>
<div class="form-group">
<label for="{{ form.phone.id_for_label}}">{{ form.phone.label}}</label> {{ form.phone}}
</div>
<input type="submit" value="提交" class="btn btn-success">
</form>
</div>
</div>
{% endblock %}
關於form的重點說明
form.字段名是訪問到字段
form.字段名.id_for_label, 是返回字段的id
form.字段名.label, 是label名稱
form.字段名 會直接返回表單元素的html
比如form.account就會變成<input type="password" name="password" maxlength="12" minlength="6" class="form-control" required id="id_password" />
代碼中return post(this)
的js function代碼如下
# static/js/utils.js
function post(form) {
$.ajax({
url: $(form).attr(‘action‘),
method: ‘POST‘,
dataType: ‘json‘,
data: $(form).serialize(),
success: function(data) {
if (data.code == 0) {
layer.msg(‘操作成功‘);
window.location.href = data.url;
} else {
layer.msg(data.message);
}
}
})
return false;
}
啟動開發服務器並測試
python manager.py runserver
實現登錄方法
實現登錄的方法和註冊的流程是一樣的, 可以自己嘗試實現以下, 如果有難度可以對照我的代碼
# account/forms.py
class LoginForm(AccountForm):
scene = ‘login‘
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for _field in self.fields:
self.fields[_field].widget.attrs.update({‘class‘: ‘form-control‘})
# 設置密碼為必須輸入項
self.fields[‘password‘].required = True
self.fields[‘email‘].required = False
self.fields[‘phone‘].required = False
self.fields[‘status‘].required = False
class Meta(AccountForm.Meta):
# 使用自定義的Form, 就必須指定fields or exclude屬性, 否則報錯
fields = (‘account‘, ‘password‘)
# account/views.py
from django.shortcuts import render
from django.urls import reverse
from django.views.decorators.http import require_http_methods
from django.shortcuts import redirect
from .forms import RegisterForm, LoginForm
from .utils import authenticate, login_required
from cms.utils import return_json
from functools import wraps
@login_required(login_url=‘/account/login/‘)
def index(request):
return render(request, template_name=‘account/index.html‘)
@require_http_methods([‘GET‘, ‘POST‘])
def login(request):
if request.method == ‘POST‘:
form = LoginForm(request.POST)
if form.is_valid():
user = authenticate(
request,
account=form.cleaned_data[‘account‘],
password=form.cleaned_data[‘password‘])
if user is not None:
return return_json(url=reverse(‘account-index‘))
else:
return return_json(code=1, message=‘賬號或密碼不正確‘)
else:
return return_json(code=1, message=form.get_first_error())
else:
form = LoginForm()
return render(request, ‘account/login.html‘, {‘form‘: form})
# account/utils.py
from .models import Account
from django.contrib.auth.hashers import check_password
from django.shortcuts import redirect
from functools import wraps
def authenticate(request, account, password):
try:
user = Account.objects.get(account=account)
except Account.DoesNotExist:
return None
if not check_password(password, user.password):
return None
request.session[‘user_id‘] = user.id
request.session[‘account‘] = user.password
return user
def login_required(func=None, login_url=‘‘):
def wrapper(func):
@wraps(func)
def _func(request, *arg, **kwargs):
if request.session.get(‘user_id‘):
return func(request, *arg, **kwargs)
else:
return redirect(login_url)
return _func
return wrapper
request.session就是一個session對象, 可以用來保存用戶登錄後的信息, 使用方法和dict完全一樣。
三【用django2.0來開發】會員註冊登錄