本文參考:實戰一:基於Django2.2可重用登錄與註冊系統
文章不做具體的操作,給大家一些分享一些我遇到的坑和解決方式。
一、系統結構
mysite工程文件根目錄下:
1、my_app是我創建的app。
2、mysite和根目錄名稱一樣,合法的,請忽略。
3、manage.py是我們開啓服務器的基礎,只需要在mysite工程文件根目錄下按住shift,右鍵,打開cmd,輸入python manage.py runserver即可。每次更改都需要重新啓動,不然可能會報錯,這種事情我遇到過無數次。
manage.py:
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
4、請忽略send_mail.py、urls.py,這是我的測試文件。
my_app文件根目錄下
1、請忽略_pycache_,這個文件夾是自動生成的。
2、migrations文件夾是執行“python manage.py makemigrations”時用來存儲0001_initial.py等數據遷移數據的,保存了我們的數據遷移工作,不用管,沒什麼大坑,按時做“python manage.py migrate”就行了。
3、statics文件夾是一個自定義的文件夾,它用來存儲一些前端所需的樣式文件,如css、js、jpg等。如圖所示,本案例主要用到了login文件中的css、image文件。這裏的問題是要注意路徑,路徑不對,仔細檢查。
4、templates文件夾也是一個自定義文件夾,裏面是my_app文件夾和base.html,app文件夾用來存儲一些主要的前端.html文件。前端的代碼請自行編寫,這裏就不展示了。不過要注意保存html文件時,編碼格式保存成UTF-8,不然會無法編碼。
5、admin.py代碼(全)
from django.contrib import admin
# Register your models here.
from . import models
admin.site.register(models.User)
admin.site.register(models.ConfirmString)
6、forms.py代碼(全)
from django import forms
from captcha.fields import CaptchaField
class UserForm(forms.Form):
username = forms.CharField(label="用戶名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': "Username",'autofocus': ''}))
password = forms.CharField(label="密碼", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control','placeholder': "Password"}))
captcha = CaptchaField(label='驗證碼')
class RegisterForm(forms.Form):
gender = (
('male', "男"),
('female', "女"),
)
username = forms.CharField(label="用戶名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
password1 = forms.CharField(label="密碼", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
password2 = forms.CharField(label="確認密碼", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
email = forms.EmailField(label="郵箱地址", widget=forms.EmailInput(attrs={'class': 'form-control'}))
sex = forms.ChoiceField(label='性別', choices=gender)
captcha = CaptchaField(label='驗證碼')
7、models.py代碼(全)
from django.db import models
# Create your models here.
class User(models.Model):
gender = (
('male', "男"),
('female', "女"),
)
name = models.CharField(max_length=128, unique=True)
password = models.CharField(max_length=256)
email = models.EmailField(unique=True)
sex = models.CharField(max_length=32, choices=gender, default="男")
c_time = models.DateTimeField(auto_now_add=True)
has_confirmed = models.BooleanField(default=False)
def __str__(self):
return self.name
class Meta:
ordering = ["-c_time"]
verbose_name = "用戶"
verbose_name_plural = "用戶"
class ConfirmString(models.Model):
code = models.CharField(max_length=256)
user = models.OneToOneField('User', on_delete=models.CASCADE)
c_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user.name + ": " + self.code
class Meta:
ordering = ["-c_time"]
verbose_name = "你認真的嗎?"
verbose_name_plural = "你認真的嗎?"
8、views代碼(全),在視圖中定義了很多函數,一定要注意html文件的地址跟自己保存的地址是不是一致,如my_app/my_app.html、my_app/index.html、my_app/logout.html、my_app/register.html。代碼很長,慎入。
from django.shortcuts import render
# Create your views here.
from django.shortcuts import redirect
from . import models
from . import forms
import hashlib
import datetime
from django.conf import settings
def hash_code(s, salt='mysite'):# 加點鹽
h = hashlib.sha256()
s += salt
h.update(s.encode()) # update方法只接收bytes類型
return h.hexdigest()
def make_confirm_string(user):
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
code = hash_code(user.name, now)
models.ConfirmString.objects.create(code=code, user=user,)
return code
def send_email(email, code):
from django.core.mail import EmailMultiAlternatives
subject = '來自127.0.0.1:8000/register/的註冊確認郵件'
text_content = '''感謝註冊127.0.0.1:8000/register/,這裏是Nick的博客站點,專注於Python爬蟲和Django網站設計的分享!\
如果你看到這條消息,說明你的郵箱服務器不提供HTML鏈接功能,請聯繫管理員!'''
html_content = '''
<p>感謝註冊<a href="http://{}/confirm/?code={}" target=blank>WWW.Nickblog.com</a>,\
這裏是Nick的博客站點,專注於Python和Django網站設計的分享!</p>
<p>請點擊站點鏈接完成註冊確認!</p>
<p>此鏈接有效期爲{}天!</p>
'''.format('127.0.0.1:8000', code, settings.CONFIRM_DAYS)
msg = EmailMultiAlternatives(subject, text_content, settings.EMAIL_HOST_USER, [email])
msg.attach_alternative(html_content, "text/html")
msg.send()
def user_confirm(request):
code = request.GET.get('code', None)
message = ''
try:
confirm = models.ConfirmString.objects.get(code=code)
except:
message = '無效的確認請求!'
return render(request, 'my_app/confirm.html', locals())
c_time = confirm.c_time
now = datetime.datetime.now()
if now > c_time + datetime.timedelta(settings.CONFIRM_DAYS):
confirm.user.delete()
message = '您的郵件已經過期!請重新註冊!'
return render(request, 'my_app/confirm.html', locals())
else:
confirm.user.has_confirmed = True
confirm.user.save()
confirm.delete()
message = '感謝確認,請使用賬戶登錄!'
return render(request, 'my_app/confirm.html', locals())
def index(request):
if not request.session.get('is_login', None):
return redirect('/login/')
return render(request, 'my_app/index.html')
def my_app(request):
if request.session.get('is_login', None): # 不允許重複登錄
return redirect('/index/')
if request.method == 'POST':
login_form = forms.UserForm(request.POST)
message = '請檢查填寫的內容!'
if login_form.is_valid():
username = login_form.cleaned_data.get('username')
password = login_form.cleaned_data.get('password')
try:
user = models.User.objects.get(name=username)
except :
message = '用戶不存在!'
return render(request, 'my_app/my_app.html', locals())
if not user.has_confirmed:
message ='該用戶還未進行郵箱確認!'
return render(request, 'my_app/my_app.html', locals())
if user.password == hash_code(password):
request.session['is_login'] = True
request.session['user_id'] = user.id
request.session['user_name'] = user.name
return redirect('/index/')
else:
message = '密碼不正確!'
return render(request, 'my_app/my_app.html', locals())
else:
return render(request, 'my_app/my_app.html', locals())
login_form = forms.UserForm()
return render(request, 'my_app/my_app.html', locals())
def register(request):
if request.session.get('is_login', None):
return redirect('/index/')
if request.method == 'POST':
register_form = forms.RegisterForm(request.POST)
message = "請檢查填寫的內容!"
if register_form.is_valid():
username = register_form.cleaned_data.get('username')
password1 = register_form.cleaned_data.get('password1')
password2 = register_form.cleaned_data.get('password2')
email = register_form.cleaned_data.get('email')
sex = register_form.cleaned_data.get('sex')
if password1 != password2:
message = '兩次輸入的密碼不同!'
return render(request, 'my_app/register.html', locals())
else:
same_name_user = models.User.objects.filter(name=username)
if same_name_user:
message = '用戶名已經存在'
return render(request, 'my_app/register.html', locals())
same_email_user = models.User.objects.filter(email=email)
if same_email_user:
message = '該郵箱已經被註冊了!'
return render(request, 'my_app/register.html', locals())
new_user = models.User()
new_user.name = username
new_user.password = hash_code(password1)
new_user.email = email
new_user.sex = sex
new_user.save()
code = make_confirm_string(new_user)
send_email(email, code)
message = '請前往郵箱進行確認!'
return render(request, 'my_app/confirm.html', locals())
else:
return render(request, 'my_app/register.html', locals())
register_form = forms.RegisterForm()
return render(request, 'my_app/register.html', locals())
def logout(request):
if not request.session.get('is_login', None):
# 如果本來就未登錄,也就沒有登出一說
return redirect("/login/")
request.session.flush()
# 或者使用下面的方法
# del request.session['is_login']
# del request.session['user_id']
# del request.session['user_name']
return redirect("/login/")
mysite工程文件根目錄下的mysite文件夾
1、settings.py代碼(全)
"""
Django settings for mysite project.
Generated by 'django-admin startproject' using Django 2.2.13.
For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/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.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'v1h*i85dyk4cz2!uqtf_pfjk%i@aqa%(pt%#!)#br@gw0!e6qu'
# 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',
'my_app',#添加
'captcha'#添加
]
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 = 'mysite.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',
],
},
},
]
WSGI_APPLICATION = 'mysite.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/2.2/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.2/topics/i18n/
LANGUAGE_CODE = 'zh-hans'#修改一下
TIME_ZONE = 'Asia/Shanghai' #修改一下
USE_I18N = True
USE_L10N = True
USE_TZ = False #默認true,請修改
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
STATIC_URL = '/static/'#一定要加上
staticfiles_dirs=(
os.path.join(BASE_DIR,'Static'),
)
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.126.com'#請填寫郵箱服務器地址,自行百度,網易126郵箱爲smtp.126.com
EMAIL_PORT = 25
EMAIL_HOST_USER = '[email protected]'#請填自己的郵箱
EMAIL_HOST_PASSWORD = 'DEOYPADHARLDYKEA'#請去自己的郵箱申請服務授權碼,不會請百度
CONFIRM_DAYS = 7
2、urls代碼(全)
'index/'匹配路由,例如http://127.0.0.1:8000/index/,這樣就能執行views.index,index是views.py裏定義的一個函數,從而打開index.html,具體見view.py,下同。
from django.contrib import admin
from django.urls import re_path,path
from django.conf.urls import url,include
from my_app import views #views代表前面提到的views.py
urlpatterns = [
path('admin/',admin.site.urls),
path('index/',views.index),
path('login/',views.my_app),
path('register/',views.register),
path('logout/',views.logout),
path('captcha/', include('captcha.urls')),
path('confirm/', views.user_confirm),
]
二、系統運行
管理員系統
在mysite工程文件根目錄下,執行python manage.py runserver,啓動服務,切記不要關閉cmd。
打開瀏覽器,輸入127.0.0.1:8000/admin/
輸入之前創建的超級管理員的用戶名和密碼,就能進去了。
在這個系統中可以增、刪、改用戶。
註冊
打開瀏覽器,輸入http://127.0.0.1:8000/register/
郵箱驗證(確認後纔可實現登錄)
用戶生成
進入Django管理(管理員系統),輸入127.0.0.1:8000/admin/,輸入帳號和密碼,查看註冊用戶,注意has confirmed前的那個複選框,沒確認是沒有勾選的,用戶也就沒法登錄;用戶確認後,就會自動勾選,用戶就可以登錄了;後臺雖然可以更改,但是不要胡亂修改,這些都是自動生成。
登錄
打開瀏覽器,輸入http://127.0.0.1:8000/login/,輸入帳號和密碼
登錄之後,就會跳轉到首頁
我這個index.html是空的,還沒想好要做什麼內容的網站,所以暫時是空的,但不影響測試。
好了,就分享到這,如果大家在編寫這個教程時有什麼問題,歡迎大傢俬信交流,很願意爲大家解決bug。不能及時回覆的,我會及時更新這個Blog提供解決方式,謝謝各位!