程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python入門自學進階-Web框架——16、Django登錄/注冊

編輯:Python

以抽屜為原型,實現用戶的注冊和登錄。

基本的界面:

 第一個知識點:自動發送驗證碼到郵箱,也就是實現自動發送郵件的功能:

 

 要自動給別人發送郵件,首先要有自己的郵箱,msg["From"]保存發送郵件的發送人名稱、發件人郵箱地址,實際就是你自己的郵箱地址,發送人名稱可以隨意寫。msg["Subject"]保存發送郵件的標題。

然後是配置郵件發送服務器,就是你自己的郵箱服務器,在你的郵箱中進行設置,啟動POP3/SMTP服務,我使用的是搜狐的郵箱,啟動後,會給你一個單獨的登錄密碼,配置server.login時會用到。

批量發送郵件時,只要將郵件列表寫在emailsend的第一個參數中就可以了。

第二個知識點,生成驗證碼圖片:

主要是pillow庫的使用,生成圖片,圖片中帶有擾動過的隨機字符串。

import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter
# pip3 install Pillow
_letter_cases = "abcdefghjkmnpqrstuvwxy" # 小寫字母,去除可能干擾的i,l,o,z
_upper_cases = _letter_cases.upper() # 大寫字母
_numbers = ''.join(map(str, range(3, 10))) # 數字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))
def create_validate_code(size=(120, 30),
chars=init_chars,
img_type="GIF",
mode="RGB",
bg_color=(255, 255, 255),
fg_color=(0, 0, 255),
font_size=18,
font_type="Monaco.ttf",
length=4,
draw_lines=True,
n_line=(1, 2),
draw_points=True,
point_chance = 2):
'''
@todo: 生成驗證碼圖片
@param size: 圖片的大小,格式(寬,高),默認為(120, 30)
@param chars: 允許的字符集合,格式字符串
@param img_type: 圖片保存的格式,默認為GIF,可選的為GIF,JPEG,TIFF,PNG
@param mode: 圖片模式,默認為RGB
@param bg_color: 背景顏色,默認為白色
@param fg_color: 前景色,驗證碼字符顏色,默認為藍色#0000FF
@param font_size: 驗證碼字體大小
@param font_type: 驗證碼字體,默認為 ae_AlArabiya.ttf
@param length: 驗證碼字符個數
@param draw_lines: 是否劃干擾線
@param n_lines: 干擾線的條數范圍,格式元組,默認為(1, 2),只有draw_lines為True時有效
@param draw_points: 是否畫干擾點
@param point_chance: 干擾點出現的概率,大小范圍[0, 100]
@return: [0]: PIL Image實例
@return: [1]: 驗證碼圖片中的字符串
'''
width, height = size # 寬, 高
img = Image.new(mode, size, bg_color) # 創建圖形
draw = ImageDraw.Draw(img) # 創建畫筆
def get_chars():
'''生成給定長度的字符串,返回列表格式'''
return random.sample(chars, length)
def create_lines():
'''繪制干擾線'''
line_num = random.randint(*n_line) # 干擾線條數
for i in range(line_num):
# 起始點
begin = (random.randint(0, size[0]), random.randint(0, size[1]))
#結束點
end = (random.randint(0, size[0]), random.randint(0, size[1]))
draw.line([begin, end], fill=(0, 0, 0))
def create_points():
'''繪制干擾點'''
chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]
for w in range(width):
for h in range(height):
tmp = random.randint(0, 100)
if tmp > 100 - chance:
draw.point((w, h), fill=(0, 0, 0))
def create_strs():
'''繪制驗證碼字符'''
c_chars = get_chars()
strs = ' %s ' % ' '.join(c_chars) # 每個字符前後以空格隔開
font = ImageFont.truetype(r'd:\Monaco.ttf', font_size)
font_width, font_height = font.getsize(strs)
draw.text(((width - font_width) / 3, (height - font_height) / 3),
strs, font=font, fill=fg_color)
return ''.join(c_chars)
if draw_lines:
create_lines()
if draw_points:
create_points()
strs = create_strs()
# 圖形扭曲參數
params = [1 - float(random.randint(1, 2)) / 100,
0,
0,
0,
1 - float(random.randint(1, 10)) / 100,
float(random.randint(1, 2)) / 500,
0.001,
float(random.randint(1, 2)) / 500
]
img = img.transform(size, Image.PERSPECTIVE, params) # 創建扭曲
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 濾鏡,邊界加強(阈值更大)
return img, strs

在views中函數:

from myutils import create_code
import io
def check_code(req):
stream = io.BytesIO()
img,code = create_code.create_validate_code()
print(code)
img.save(stream,'PNG')
req.session['CheckCode'] = code
return HttpResponse(stream.getvalue())

返回給前端的是圖片的字節流,在前端,使用的是img標簽:
<img class="check-img" src="check_code" alt="驗證碼" οnclick="ChangeCode(this);">,就顯示圖片了。而隨機字符串保存在session中。

整個驗證的前端:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/css/mycommon.css">
</head>
<body>
<div class="head-box">
<div class="head-content">
<a href="#" class="logo"></a>
<div class="action-menu">
<a href="#" class="tb active">全部</a>
<a href="#" class="tb">42區</a>
<a href="#" class="tb">段子</a>
<a href="#" class="tb">圖片</a>
<a href="#" class="tb">挨踢1024</a>
<a href="#" class="tb">你問我答</a>
</div>
<div class="key-search">
<form action="/" method="post">
<input type="text" class="search-txt" autocomplete="off">
<a href="#" class="i" >
<span class="ico"></span>
</a>
</form>
</div>
{% if request.session.is_login %}
<div class="action-nav">
<a>動態</a>
<a>通知</a>
<a href="#" class="login-user">{
{ request.session.user_info.username }}</a>
<a id="loginout" href="loginout.html">退出</a>
</div>
{% else %}
<div class="action-nav">
<a href="#" class="register-login-btn">注冊/登錄</a>
</div>
{% endif %}
</div>
</div>
<div class="shadow hide"></div>
<div class="accountDialog hide">
<div id="model_login" class="login left">
<div class="header">登陸</div>
<div class="content">
<div >
<div class="tips">
<span>用戶名登陸</span>
<span >|</span>
<span>郵箱登陸</span>
</div>
<div id="login_error_summary" class="error-msg">
</div>
<div class="inp">
<input name="user" type="text" placeholder="請輸入用戶名或郵箱" />
</div>
<div class="inp">
<input name="pwd" type="password" placeholder="請輸入密碼" />
</div>
<div class="inp clearfix">
<input name="code" class="check-code" type="text" placeholder="請輸入驗證碼" />
<span>
<img class="check-img" src="check_code" alt="驗證碼" onclick="ChangeCode(this);">
</span>
</div>
<div class="extra" >
<input type="checkbox" name="autoLogin" checked="checked" /> <span>一個月內自動登錄</span>
<a class="right" href="javascript:void(0);">忘記密碼?</a>
</div>
<div class="inp">
<div class="submit" onclick="SubmitLogin(this);">
<span>登陸</span>
<span class="hide">
<img src="/static/images/loader.gif" >
<span>正在登陸</span>
</span>
</div>
</div>
</div>
<script>
function ChangeCode(ths) {
ths.src += '?';
}
</script>
</div>
</div>
<div id="model_register" class="register right">
<div class="header">
<span>注冊</span>
<div class="dialog-close" onclick="CloseDialog('.accountDialog');">X</div>
</div>
<div class="content">
<div >
<div class="tips">
<span>輸入注冊信息</span>
</div>
<div id="register_error_summary" class="error-msg"></div>
<div class="inp">
<input name="username" type="text" placeholder="請輸入用戶名" />
</div>
<div class="inp">
<input name="email" id="email" type="text" placeholder="請輸入郵箱" />
</div>
<div class="inp">
<input name="email_code" class="email-code" type="text" placeholder="請輸入驗證碼" />
<a id="fetch_code" class="fetch-code" href="javascript:void(0);">獲取驗證碼</a>
<!-- a標簽中的href屬性,如果設為“#”,鼠標放在上面是一個豎槓,且點擊時地址欄會增加#,如上面寫法,會變成小手,且點擊時不會增加地址欄內容-->
</div>
<div class="inp">
<input name="pwd" type="password" placeholder="請輸入密碼" />
</div>
<div class="inp">
<div class="submit" onclick="SubmitRegister(this);">
<span>注冊</span>
<span class="hide">
<img src="/static/images/loader.gif" >
<span>正在注冊</span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="main-content-box">
<div class="main-content">
<div class="content-L">
<div class="top-area">
<div class="child-nav">
<a href="#" class="hotbtn active" >最熱</a>
<a href="#" class="newbtn" >最新</a>
<a href="#" class="personbtn" >人類發布</a>
</div>
<div class="sort-nav">
<a href="#" class="sortbtn active" >即時排序</a>
<a href="#" class="newbtn" >24小時</a>
<a href="#" class="newbtn" >3天</a>
</div>
{% if userinfo.is_login %}
<a href="javascript:void(0);" class="publish-btn">
<span class="n2">+發布</span>
</a>
{% else %}
<a href="javascript:void(0);" class="publish-btn">
<span class="n2">+發布</span>
</a>
{% endif %}
</div>
<div class="content-list">
{% for item in news_list %}
<div class="item">
<div class="news-pic">
<img src="{
{ item.image_url }}" alt="抽屜新熱榜">
</div>
<div class="news-content">
<div class="part1">
<a href="#" class="show-content" target="_blank">
{
{ item.title }}
</a>
<span class="content-source">-{
{ item.url_laiyuan }}</span>
<a href="#" class="n2">
<span class="content-kind">42區</span>
</a>
</div>
<div class="part2">
<a href="#" class="recommend" title="推薦">
<span class="hand-icon icon-recommend"></span>
<b>{
{ item.counts_acc }}</b>
</a>
<a href="javascript:;" class="discuss">
<span class="hand-icon icon-discuss"></span>
<b>5</b>
</a>
<a href="javascript:;" class="collect" title="加入私藏">
<span class="hand-icon icon-collect"></span>
<b>私藏</b>
</a>
<a href="#" class="user-a">
<span>
<img src="/static/images/13.png">
</span>
<b>亂太郎</b>
</a>
<span class="left time-into">
<a class="time-a" href="#" target="_blank">
<b>4分鐘前</b>
</a>
<i>入熱榜</i>
</span>
<!-- 分享各微博的按鈕 -->
<span class="share-site-to">
<i>分享到</i>
<span class="share-icon">
<a class="icon-sina" title="分享到新浪微博" href="#" ></a>
<a class="icon-douban" title="分享到豆瓣" href="#" ></a>
<a class="icon-qqzone" title="分享到QQ空間" href="#" ></a>
<a class="icon-tenxun" title="分享到騰訊微博" href="#" ></a>
<a class="icon-renren" title="分享到人人網" href="#" ></a>
</span>
</span>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="page-area">
<ul>
<li><span class="ct_pagepw">1</span></li>
<li><a href="#" class="ct_pagepa">2</a></li>
<li><a href="#" class="ct_pagepa">3</a></li>
<li><a href="#" class="ct_pagepa">4</a></li>
<li><a href="#" class="ct_pagepa">5</a></li>
<li><a href="#" class="ct_pagepa">6</a></li>
<li><a href="#" class="ct_pagepa">7</a></li>
<li><a href="#" class="ct_pagepa">8</a></li>
<li><a href="#" class="ct_pagepa">9</a></li>
<li><a href="#" class="ct_pagepa">10</a></li>
<li class="next"><a href="#" class="ct_page_edge">下一頁</a></li>
</ul>
</div>
</div>
<div class="content-R">
</div>
</div>
</div>
<div class="footer-box">
<div class="foot-nav">
<a href="#" target="_blank">關於我們</a>
<span>|</span>
<a href="#" target="_blank">聯系我們</a>
<span>|</span>
<a href="#" target="_blank">服務條款</a>
<span>|</span>
<a href="#" target="_blank">隱私政策</a>
<span>|</span>
<a href="#" target="_blank">抽屜新熱榜工具</a>
<span>|</span>
<a href="#" target="_blank">下載客戶端</a>
<span>|</span>
<a href="#" target="_blank">意見與反饋</a>
<span>|</span>
<a href="#" target="_blank">友情鏈接</a>
<span>|</span>
<a href="#" target="_blank">公告</a>
<a href="#" target="_blank" >
<img src="/static/images/ct_rss.gif" width="36" height="14">
</a>
</div>
<div class="foot-nav2">
<a target="_blank" href="#">
<img class="foot_e" src="/static/images/footer1.gif" width="36" height="14">
</a>
<span class="foot_d">旗下站點</span>
<span class="foot_a">2016chouti.com</span>
<a target="_blank" href="#" class="foot_b">京ICP備09053974號-3 京公網安備 110102004562</a>
<div >版權所有:北京格致璞科技有限公司</div>
</div>
</div>
<script src="/static/jquery-3.6.0.js"></script>
<script>
$(function () {
bindLoginRegister();
bindSendMsg();
});
function bindLoginRegister() {
$('.action-nav a.register-login-btn').click(function () {
$('.shadow,.accountDialog').removeClass('hide');
});
}
function bindSendMsg() {
$('#fetch_code').click(function () {
$('#register_error_summary').empty();
var email = $('#email').val();
if(email.trim().length == 0){
$('#register_error_summary').text('請輸入郵箱');
return;
}
if($(this).hasClass('sending')){
return;
}
var ths = $(this);
var time = 60;
$.ajax({
url: "send_msg/",
type: "POST",
data: {email:email,csrfmiddlewaretoken:'{
{ csrf_token }}'},
dataType: "json",
success: function (arg) {
if(!arg.status){
$('#register_error_summary').text(arg.summary);
}else {
ths.addClass('sending');
var interval = setInterval(function () {
ths.text("已發送(" + time +")");
time -= 1;
if(time<=0){
clearInterval(interval);
ths.removeClass('sending');
ths.text("獲取驗證碼");
}
},1000);
}
}
});
});
}
function SubmitRegister(self) {
$('#register_error_summary').empty();
$('#model_register .inp .error').remove();
$(self).children(':eq(0)').addClass('hide');
$(self).addClass('not-allow').children(':eq(1)').removeClass('hide');
var post_dict = {};
$('#model_register input').each(function () {
post_dict[$(this).attr("name")] = $(this).val();
});
post_dict['csrfmiddlewaretoken'] = '{
{ csrf_token }}';
$.ajax({
url: 'register.html',
type: 'POST',
data: post_dict,
dataType: 'json',
success: function (arg) {
if(arg.status){
window.location.href = 'chouti-index.html';
}else {
$.each(arg.message,function (k,v) {
var tag = document.createElement('span');
tag.className = 'error';
tag.innerText = v;
$('#model_register input[name="' + k + '"]').after(tag);
})
}
}
});
$(self).removeClass('not-allow').children(':eq(1)').addClass('hide');
$(self).children(':eq(0)').removeClass('hide');
}
function CloseDialog(dialog) {
$(dialog).addClass('hide');
$('.shadow').addClass('hide');
}
function ChangeCode(self) {
self.src += '?';
}
function SubmitLogin(self) {
$(self).children(':eq(0)').addClass('hide');
$(self).addClass('not-allow').children(':eq(1)').removeClass('hide');
$('#model_login .inp .error').remove();
var post_dict ={};
$('#model_login input').each(function () {
post_dict[$(this).attr('name')] = $(this).val();
});
post_dict['csrfmiddlewaretoken'] = '{
{ csrf_token }}';
$.ajax({
url: 'login.html',
type: 'POST',
data: post_dict,
dataType: 'json',
success: function (arg) {
if(arg.status){
window.location.href = 'chouti-index.html'
}else {
$.each(arg.message,function (k,v) {
alert(v);
var tag = document.createElement('span');
tag.className = 'error';
tag.innerText = v[0]['message'];
$('#model_login input[name="' + k + '"]').after(tag);
});
}
}
});
$(self).removeClass('not-allow').children(':eq(1)').addClass('hide');
$(self).children(':eq(0)').removeClass('hide');
}
</script>
</body>
</html>

樣式文件:

*{
margin: 0;
padding: 0;
}
a{
text-decoration: none;
}
body{
font-size: 14px;
}
.head-box{
background-color: #2459a2;
height: 44px;
width: 100%;
position: fixed;
top: 0;
left: 0;
/* position的fixed定位,要加上位置top,left等,使人明白是固定在哪個位置,現在是屏幕的0,0處 */
/* 是一直在屏幕的0,0位置 ,隨著鼠標向下滾動,位置一直不變 */
}
.head-content{
margin: 0 auto; /* 內容左右居中,0代表上下,auto代表左右 */
width: 1016px;
height: 44px;
background-color: #2459a2;
line-height: 44px;
position: relative;
}
.logo{
background : url("/static/images/logo.png") no-repeat 0 0;
height: 23px;
width: 121px;
float: left;
margin-top: 11px;
}
.action-menu{
float: left;
margin-left: 20px;
}
.action-menu a.tb{
color: #c0cddf;
margin-left: -6px; /* 兩個菜單項之間margin是0,使用padding來分隔,因為默認margin有個值,使用負值使之為0*/
padding: 0 13px 0 13px;
display: inline-block;
}
.action-menu a.tb:hover{
color: #fff;
background-color: #c0cddf;
}
.action-menu a.active,.action-menu a.active:hover{
color: #fff;
background-color: #204982;
}
.key-search{
float: right;
margin-top: 7px;
}
.key-search .search-txt,.key-search a.i{
float: left;
}
.key-search .search-txt{
width: 91px;
height: 25px;
padding: 2px 2px 2px 5px;
color: #333;
}
.key-search .ico{ /* span標簽中背景圖片,進行調整,使放大鏡圖像顯示在小窗口中 */
background: url("/static/images/icon.png") no-repeat 0 -197px;
height: 12px;
width: 12px;
display: inline-block;
/* span本身是內聯標簽,需要改成inline-block*/
margin-bottom: 5px;
margin-left: 8px;
/* margin用於調整下面的span標簽的位置*/
}
.key-search a.i{ /* a標簽中容納了上面的span標簽*/
margin-top: 1px;
height: 31px;
width: 30px;
background-color: #f4f4f4;
display: inline-block;
/*border: 1px yellow solid;*/
border-left: none;
}
.action-nav{
position: absolute;
right: 131px;
}
.action-nav a{
color: white;
padding: 0 20px;
display: inline-block;
}
.action-nav a:hover{
background-color: #c0cddf;
}
.shadow{
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0,0,0,0.4);
z-index: 1000;
}
.hide{
display: none;
}
.main-content-box{
background-color: #ededed;
width: 100%;
padding-top: 44px;
}
.main-content{
margin: 0 auto;
background-color: white;
width: 960px;
height: auto!important;
min-height: 500px;
padding: 6px 28px 60px 28px;
overflow: hidden;
}
.content-L{
float: left;
width: 630px;
}
.child-nav,.sort-nav{
float: left;
padding: 10px;
}
.publish-btn{
float: right;
padding: 10px;
}
.top-area{
overflow: hidden;
border-bottom: 1px solid #99aecb;
}
.child-nav a{
display: inline-block;
width: 60px;
height: 26px;
line-height: 26px;
text-align: center;
color: #369;
font-weight: 700;
margin-top: 3px;
}
.child-nav .hotbtn{
background: url("/static/images/tip.png") no-repeat 0 -299px;
/*display: inline-block;*/
/*width: 60px;*/
/*height: 26px;*/
/*line-height: 26px;*/
/*text-align: center;*/
color: black;
}
.sort-nav{
margin-left: 100px;
margin-top: 6px;
}
.sort-nav a{
margin-left: 1px;
color: green;
}
.sort-nav .sortbtn{
color: #b4b4b4;
}
.publish-btn{
display: inline-block;
background-color: #84a42b;
width: 120px;
height: 18px;
color: white;
line-height: 18px;
text-align: center;
margin-top: 3px;
}
.content-list .item{
border-bottom: 1px solid red;
margin-top: 10px;
}
.item .news-pic{
float: right;
margin-top: 3px;
margin-left: 8px;
}
.part2{
padding-top: 10px;
margin-bottom: 12px;
}
.hand-icon{
background: url("/static/images/icon_18_118.png") no-repeat 0 0;
width: 18px;
height: 18px;
display: inline-block;
vertical-align: -4px;
}
.icon-recommend{
background-position: 0 -40px;
}
.icon-discuss{
background-position: 0 -100px;
}
.icon-collect{
background-position: 0 -140px;
}
.part2 .user-a span{
vertical-align: -4px;
}
.part2 a{
margin-left: 10px;
}
.part1{
line-height: 20px;
}
.part1 .content-source,.content-kind{
color: #b4b4b4;
}
.part1 .content-kind{
text-decoration: underline;
}
.part1 .show-content{
color: #369;
font-size: 14px;
font-weight: 700;
}
.part2 b,.time-into i{
color: #b4b4b4;
}
.share-icon a{
background: url("/static/images/share_icon.png") no-repeat 0 0;
height: 14px;
width: 17px;
display: inline-block;
vertical-align: -4px;
opacity: 0.5;
}
.share-icon a:hover{
opacity: 1;
}
.icon-sina{
}
.share-site-to .share-icon a.icon-sina{
background-position: 0 -90px;
}
.share-site-to .share-icon a.icon-douban{
background-position: 0 -105px;
}
.share-site-to .share-icon a.icon-qqzone{
background-position: 0 -120px;
}
.share-site-to .share-icon a.icon-tenxun{
background-position: 0 -136px;
}
.share-site-to .share-icon a.icon-renren{
background-position: 0 -151px;
}
.share-site-to{
float: right;
}
.page-area ul li{
display: inline-block;
float: left;
color: #369;
height: 34px;
width: 34px;
line-height: 34px;
text-align: center;
border: 1px solid #a3a3a1;
border-radius: 20%;
margin-left: 4px;
}
.page-area{
margin-left: 10px;
margin-top: 10px;
}
ul li.next{
width: 60px;
}
.page-area ul li:hover{
color: white;
background-color: #204982;
}
.footer-box{
margin-top: -20px;
background-color: #ededed;
width: 100%;
}
.foot-nav,.foot-nav2{
width: 1016px;
margin: 0 auto;
padding-top: 10px;
background-color: white;
text-align: center;
}
.foot-nav{
border-top: 1px solid #a3a3a1;
}
.accountDialog{
position: fixed;
width: 700px;
height: 375px;
left: 50%;
top: 50%;
margin-left: -350px;
margin-top: -250px;
z-index: 1001;
background-color: rgb(230,236,243);
}
.accountDialog .login{
width: 349px;
background-color: #fff;
height: 375px;
border-right: 1px solid #cbdcee;
float: left;
}
.accountDialog .register{
width: 350px;
background-color: white;
height: 375px;
float: right;
}
.accountDialog .login .header,.accountDialog .register .header{
background: #e7ecf2;
padding: 0 10px;
font-size: 14px;
height: 30px;
line-height: 30px;
font-weight: bold;
color: #abb6d2;
position: relative;
}
.accountDialog .register .content .tips,.accountDialog .login .content .tips{
/*padding: 20px 0 0 -10px;*/
padding-top: 10px;
font-size: 14px;
color: #abb6d2;
cursor: pointer;
text-align: left;
}
.accountDialog .login .content .inp,.accountDialog .register .content .inp{
padding: 10px 0;
position: relative;
}
.accountDialog .login .content .inp input,.accountDialog .register .content .inp input{
width: 150px;
padding: 6px;
border: 1px solid #CDDDEF;
}
.accountDialog .register .content .inp .fetch-code{
display: inline-block;
background-color: #336699;
height: 29px;
line-height: 29px;
padding: 0 5px;
color: #ffffff;
text-decoration: none;
}
.accountDialog .login .content .error-msg,.accountDialog .register .content .error-msg{
color: red;
height: 14px;
}
.accountDialog .login .content .inp .check-code{
width: 117px;
padding: 6px;
border: 1px solid #CDDDEF;
display: inline-block;
margin-right: 3px;
}
.accountDialog .login .content .inp .check-img{
height: 29px;
width: 70px;
display: inline-block;
cursor: pointer;
vertical-align: top;
}
.accountDialog .register .content .inp .email-code{
width: 122px;
padding: 6px;
border: 1px solid #CDDDEF;
}
.accountDialog .register .content .inp .fetch-code.sending{
cursor: not-allowed;
background-color: #a3a3a1 !important
}
.accountDialog .register .content .inp .submit,.accountDialog .login .content .inp .submit{
width: 95px;
height: 31px;
line-height: 31px;
margin-top: 5px;
background-color: #336699;
color: #ffffff;
text-align: center;
cursor: pointer;
font-size: 14px;
font-weight: 700;
display: inline-block;
}
.accountDialog .login .content .extra{
padding-bottom: 10px;
}
.accountDialog .login .content .extra a{
color: #369;
}
.accountDialog .register .header .dialog-close{
position: absolute;
right: 10px;
top: 1px;
color: #99aecb;
cursor: pointer;
}
.accountDialog .register{
width: 350px;
background-color: #ffffff;
height: 375px;
}
.accountDialog .register .content .inp .error,.accountDialog .login .content .inp .error{
font-size: 10px;
position: absolute;
color: #e4393c;
background: #FFEBEB;
border: 1px solid #ffbdbe;
z-index: 10;
height: 15px;
width: 208px;
line-height: 15px;
display: block;
overflow: hidden;
}
.not-allow{
cursor: not-allowed !important;
}

views函數:

from django.shortcuts import render,redirect,HttpResponse
from projectct import models
from myutils.myresponse import BaseResponse
from myutils.myforms import SendMsgForm,RegisterForm,LoginForm
import json
import datetime
from myutils import commons
from django.utils import timezone
from chouti import settings
import pytz
def test(req):
news_list = models.News.objects.all().values("title", "url_laiyuan", "counts_acc", "image_url","auth__username")
return render(req,'test.html',{'obj':news_list})
def login(req):
rep = BaseResponse() # 返回前端信息封裝類
form = LoginForm(req.POST) # 對輸入的登錄信息進行格式驗證,不是數據庫數據的驗證
if form.is_valid(): # 驗證無誤時
_value_dict =form.clean()
print(_value_dict)
if _value_dict['code'].lower() != req.session["CheckCode"].lower(): # 輸入的驗證碼與服務器session中的驗證碼比對
rep.message = {'code':[{'message':'驗證碼錯誤'}]}
print('::::::::==>',rep.__dict__)
return HttpResponse(json.dumps(rep.__dict__)) # __dict__,類中的屬性以字典形式存儲在此變量中
# 驗證碼正確
from django.db.models import Q
# Q是或邏輯關系查詢,進行數據庫數據的驗證
con = Q()
q1 = Q()
q1.connector = 'AND'
q1.children.append(('email',_value_dict['user']))
q1.children.append(('pwd',_value_dict['pwd']))
q2 = Q()
q2.connector = 'AND'
q2.children.append(('username', _value_dict['user']))
q2.children.append(('pwd', _value_dict['pwd']))
con.add(q1,'OR')
con.add(q2,'OR')
# con最後形成的是查詢“email與密碼” 或 “username與密碼”,即以郵件登錄或以用戶名登錄
# 一般的filter中直接加字段名=的形式,相互之間是與的關系
obj = models.UserInfo.objects.filter(con).first()
if not obj:
rep.message = {'user':[{'message':'用戶名或郵箱、密碼錯誤'}]}
return HttpResponse(json.dumps(rep.__dict__))
req.session['is_login'] = True
req.session['user_info'] = {'nid':obj.id,'email':obj.email,'username':obj.username}
rep.status = True
else:
error_msg = form.errors.as_json()
rep.message = json.loads((error_msg))
return HttpResponse(json.dumps(rep.__dict__))
def chouti(req):
# news_list = models.News.objects.all().values("title","url_laiyuan","counts_acc","contents","image_url","auth__username")
news_list = models.News.objects.all().values("title", "url_laiyuan", "counts_acc", "image_url","auth__username")
print(req.session.session_key)
print(req.session.keys())
print(req.session.values())
return render(req,'chouti-index.html',{"news_list":news_list})
import pytz
def send_msg(req):
# 注冊時,發送郵箱驗證碼
rep = BaseResponse()
form = SendMsgForm(req.POST)
if form.is_valid():
_value_dict = form.clean()
email = _value_dict['email']
has_exisits_email = models.UserInfo.objects.filter(email=email).count()
if has_exisits_email:
rep.summary = "此郵箱已經被注冊"
return HttpResponse(json.dumps(rep.__dict__))
# current_date = datetime.datetime.now(pytz.UTC) # 獲取的是UTC時間,帶時區,即是active datetime
# print(current_date) # 2022-06-29 00:29:16.044477+00:00
current_date = datetime.datetime.now(pytz.timezone('Asia/Shanghai')) # 獲得是是不帶時區的時間,即naive datetime
print(current_date) # 2022-06-29 08:37:05.017300,是本地時間,沒有轉換為UTC
# 當models中定義的類使用了DateTimeField字段時,要求使用active datetime日期時間對象,否則報如下警告:
# RuntimeWarning: DateTimeField SendMsg.ctime received a naive datetime (2022-06-29 08:37:05.017300) while time zone support is active.
# current_date = timezone.make_aware(current_date)
# print(current_date)
# print(timezone.now()) # 2022-06-29 00:47:02.625482+00:00,默認UTC的active datetime對象,本地時間減去8
code = commons.random_code()
emailsend([email,],code) # 給郵箱發送驗證碼
count = models.SendMsg.objects.filter(email=email).count()
if not count:
models.SendMsg.objects.create(code=code,email=email,ctime=current_date)
rep.status = True
else:
limit_day = current_date - datetime.timedelta(hours=1)
times = models.SendMsg.objects.filter(email=email,ctime__gt=limit_day,times__gt=9).count()
if times:
rep.summary = "'已超最大次數(1小時後重試)'"
else:
unfreeze = models.SendMsg.objects.filter(email=email,ctime__lt=limit_day).count()
if unfreeze:
models.SendMsg.objects.filter(email=email).update(times=0)
else:
from django.db.models import F
models.SendMsg.objects.filter(email=email).update(code=code,ctime=current_date,times=F('times')+1)
rep.status=True
else:
rep.summary = form.errors['email'][0]
return HttpResponse(json.dumps(rep.__dict__))
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
def emailsend(email_list,content,subject="測試自動發送郵件"):
msg = MIMEText(content,'plain','utf-8')
msg['From'] = formataddr(["測試郵箱","[email protected]"])
msg['Subject'] = subject
server = smtplib.SMTP("smtp.sohu.com",25)
server.login("[email protected]","密碼")
server.sendmail("[email protected]",email_list,msg.as_string())
server.quit()
def register(req):
rep = BaseResponse()
form = RegisterForm(req.POST)
if form.is_valid():
current_date = datetime.datetime.now(pytz.timezone('Asia/Shanghai'))
limit_day = current_date -datetime.timedelta(minutes=1)
_value_dict = form.clean()
is_valid_code = models.SendMsg.objects.filter(email=_value_dict['email'],code=_value_dict['email_code'],ctime__gt=limit_day).count()
if not is_valid_code:
rep.message['email_code'] = '郵箱驗證碼不正確或過期'
return HttpResponse(json.dumps(rep.__dict__))
has_exists_email = models.UserInfo.objects.filter(email=_value_dict['email']).count()
if has_exists_email:
rep.message['email'] = '郵箱已經存在'
return HttpResponse(json.dumps(rep.__dict__))
has_exists_username = models.UserInfo.objects.filter(username=_value_dict['username']).count()
if has_exists_username:
rep.message['username'] = '用戶名已經存在'
return HttpResponse(json.dumps(rep.__dict__))
_value_dict['ctime'] = current_date
_value_dict.pop('email_code')
obj = models.UserInfo.objects.create(**_value_dict)
user_info_dict = {'nid':obj.id,'email':obj.email,'username':obj.username}
models.SendMsg.objects.filter(email=_value_dict['email']).delete()
req.session['is_login'] = True
req.session['user_info'] = user_info_dict
rep.status = True
else:
error_msg = form.errors.as_json()
rep.message = json.loads(error_msg)
return HttpResponse(json.dumps(rep.__dict__))
def loginout(req):
req.session.clear()
return redirect('chouti-index.html')
from myutils import create_code
import io
def check_code(req):
stream = io.BytesIO()
img,code = create_code.create_validate_code()
img.save(stream,'PNG')
req.session['CheckCode'] = code
return HttpResponse(stream.getvalue())

models模塊:

from django.db import models
# Create your models here.
class UserInfo(models.Model):
username = models.CharField(max_length=32,db_index=True)
email = models.CharField(max_length=32,unique=True)
pwd = models.CharField(max_length=64)
ctime = models.DateTimeField(auto_now_add=True)
"""
定義聯合索引
class Meta:
index_together = [
('username','pwd'),
]
unique_together = [
('username','pwd'),
]
"""
class SendMsg(models.Model):
email = models.EmailField(max_length=64,unique=True)
code = models.CharField(max_length=6)
ctime = models.DateTimeField()
times = models.IntegerField(default=1)
# 使用SendMsg.objects.create(email="111111",code="1234",stime=datetime.now())
# 上述語句會保存數據庫成功,不會進行email驗證
# 可以先生成對象:obj = SendMsg(email="111111",code="1234",stime=datetime.now())
# 然後obj.clean() 執行這一句時,才會進行驗證,email不符合要求,會直接異常報錯
# 如果正確,使用obj.save()保存數據庫
# django Admin也是可以進行驗證的
class News(models.Model):
title = models.CharField(max_length=256)
content = models.CharField(max_length=1024)
url_laiyuan = models.URLField()
counts_acc = models.IntegerField()
image_url = models.CharField(max_length=128)
contents = models.TextField()
auth = models.ForeignKey("UserInfo",on_delete=models.DO_NOTHING)

form驗證模塊:

from django import forms
class SendMsgForm(forms.Form):
email = forms.EmailField()
class RegisterForm(forms.Form):
username = forms.CharField()
email = forms.EmailField()
pwd = forms.CharField()
email_code = forms.CharField()
class LoginForm(forms.Form):
user = forms.CharField()
pwd = forms.CharField()
code = forms.CharField()

路由項:

from django.contrib import admin
from django.urls import path
from projectct import views
urlpatterns = [
path('admin/', admin.site.urls),
path('test.html',views.test),
path('index.html',views.login),
path('chouti-index.html',views.chouti),
path('send_msg/',views.send_msg),
path('register.html',views.register),
path('loginout.html',views.loginout),
path('check_code/',views.check_code),
path('login.html',views.login),
]

第三個知識點:時間函數的使用,主要是datetime,pytz,timezone模塊的使用,要注意日期時間有有帶時區和不帶時區的區別,使用時一定要一致。

第四個知識點:F和Q,F是對數據庫自增字段操作時需要使用,Q用來組合或查詢(使用connector也可以是與),構建復雜的查詢語句。

通過這個小實驗,感覺還是前端的編寫比較麻煩,要開發B/S架構程序,需要加強js、jQuery、CSS學習。


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved