Django開發註冊登錄系統筆記和系統運行流程(含避坑策略)

本文參考:實戰一:基於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提供解決方式,謝謝各位!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章