0x0d、如何設置不同登錄方式,顯示不同的標籤及對應的form表單的字段?下面代碼超級多細節,真的值得看
描述的情景如下圖:
代碼實現思路:兩個不同的登錄方式是:賬號密碼登錄和使用短信驗證碼登錄。
1.如果是使用短信驗證碼登錄的話,給它多傳遞一個字段用於區別這個form表單到底是哪個,如,ynamic_login = True
2.設置這個字段用於標記,當前這個登錄方式是短信驗證碼登錄,然後將這個字段傳遞到前端的html頁面裏面,去判斷,如果存在這個字段並且爲True
那麼前端的html代碼就展示短信驗證碼登錄的相關代碼即可。
3.下面看一下前端的相關代碼:
<div class="fl form-box">
<div class="tab">
<h2 class="{% if dynamic_login %}{% else %}active{% endif %}">賬號登錄</h2>
<h2 class="{% if dynamic_login %}active{% else %}{% endif %}">動態登錄</h2>
</div>
<form class="tab-form {% if dynamic_login %}hide{% else %}{% endif %}" action="{% url 'login' %}" method="post" autocomplete="off" id="form1">
<div class="form-group marb20 {% if login_form.errors.username %}errorput{% endif %}">
<input name="username" id="account_l" value="{{ login_form.username.value }}" type="text" placeholder="手機號/郵箱" />
</div>
<div class="form-group marb8 {% if login_form.errors.password %}errorput{% endif %}">
<input name="password" id="password_l" value="{{ login_form.password.value }}" type="password" placeholder="請輸入您的密碼" />
</div>
<div class="error btns login-form-tips" id="jsLoginTips">{% if login_form.errors %}{% for key,error in login_form.errors.items %}{{ error }}{% endfor %}{% else %}{{ msg }}{% endif %}</div>
<div class="auto-box marb38">
<a class="fr" href="forgetpwd.html">忘記密碼?</a>
</div>
<input class="btn btn-green" id="jsLoginBtn" type="submit" value="立即登錄 > " />
{% csrf_token %}
</form>
<form class="tab-form {% if dynamic_login %}{% else %}hide{% endif %}" action="{% url 'd_login' %}" id="mobile_register_form" autocomplete="off" method="post" id="form2">
<div class="form-group marb20">
<input id="jsRegMobile" value="{{ login_form.mobile.value|default_if_none:'' }}" name="mobile" type="text" placeholder="請輸入您的手機號碼">
</div>
<div class="form-group marb20 blur" id="jsRefreshCode">
{{ login_form.captcha }}
{{ d_form.captcha }}
</div>
<div class="clearfix">
<div class="form-group marb8 verify-code">
<input id="jsPhoneRegCaptcha" value="{{ login_form.code.value }}" name="code" type="text" placeholder="輸入手機驗證碼">
</div>
<input class="verify-code-btn sendcode" id="jsSendCode" value="發送驗證碼">
</div>
<div class="error btns" id="jsMobileTips" style="">{% if login_form.errors %}{% for key,error in login_form.errors.items %}{{ error }}{% endfor %}{% else %}{{ msg }}{% endif %}</div>
<div class="auto-box marb8">
</div>
<input class="btn btn-green" id="jsMobileRegBtn" type="button" value="立即登錄">
{% csrf_token %}
</form>
<p class="form-p">沒有慕學在線網帳號?<a href="register.html">[立即註冊]</a></p>
</div>
4.看一下後端代碼:
views.py:
import redis
from django.shortcuts import render
from django.views.generic.base import View
from django.contrib.auth import authenticate, login, logout
from django.http import HttpResponseRedirect, JsonResponse # 提供url重定向; 提供返回json類型的數據
from django.urls import reverse
from users.forms import LoginForm, DynamicLoginForm, DynamicLoginPostForm
from utils.random_str import generate_random
from utils.YunPian import send_single_sms
from MxOnline.settings import yp_api_key, REDIS_HOST, REDIS_PORT
from users.models import UserProfile
class DynamicLogin(View):
"""11-02 動態短信登錄提交方式"""
def post(self, request, *args, **kwargs):
login_form = DynamicLoginPostForm(request.POST)
dynamic_login = True # todo 這個標記用於,標記如果是動態登錄的話,輸出驗證碼,重新跳轉的頁面,給自動跳到原來的頁面當中
if login_form.is_valid():
# 沒有註冊賬號,依然可以登錄(其實就是幫忙給註冊一個)
mobile = login_form.cleaned_data["mobile"]
existed_users = UserProfile.objects.filter(mobile=mobile)
if existed_users:
user = existed_users[0]
else:
# 不存在即創建一個用戶
user = UserProfile(username=mobile)
password = generate_random(10, 2)
user.set_password(password) # TODO 這裏設置密碼是要注意:如果使用user.password=password的話,那麼這裏是一個明文,一般不建議這樣做;要使用密文可以使用user.set_password(password)
user.mobile = mobile
user.save() # 保存創建的用戶
login(request, user)
return HttpResponseRedirect(reverse("index")) # 登錄之後跳轉到首頁
else:
d_form = DynamicLoginForm(request.POST) # 這個是用於自動刷新驗證碼
return render(request, "login.html", {"login_form": login_form,
"dynamic_login": dynamic_login,
"d_form": d_form}) # 這個會自動校驗,並且將錯誤的信息返回
class LoginView(View):
"""2019-10-29 1.必須繼承這個View 2.下面的get和post分別表示不同的請求,在這裏是表示重載這兩種請求的方法"""
def get(self, request, *args, **kwargs): # 這個request參數是Django自動注入的,不需要自己賦值
if request.user.is_authenticated: # 10-30 如果用戶是登錄狀態,點擊login請求會自動重定向到首頁
return HttpResponseRedirect(reverse("index"))
login_form = DynamicLoginForm()
return render(request, "login.html", {"login_form": login_form})
def post(self, request, *args, **kwargs):
# username = request.POST.get("username")
# password = request.POST.get("password")
#
# # 方法1、通過用戶名和密碼查詢用戶是否存在
# user = authenticate(username=username, password=password)
# # 方法2、通過自定義的userprofile去查詢,存在的問題有:數據庫存的密碼是密文,校驗的時候需要再次加密才能進行校驗
# from users.models import UserProfile
# user = UserProfile.objects.get(username=username, password=password)
login_form = LoginForm(request.POST)
if login_form.is_valid():
username = login_form.cleaned_data["username"] # todo 重點是這種用法,是這樣從form裏面提取對應的值
password = login_form.cleaned_data["password"]
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
return HttpResponseRedirect(reverse("index"))
else:
return render(request, "login.html", {"msg": "用戶名或者密碼錯誤", "login_form": login_form})
else:
return render(request, "login.html", {"login_form": login_form}) # 這個會自動校驗,並且將錯誤的信息返回
class LogoutView(View):
"""10-30 退出接口開發"""
def get(self, request, *args, **kwargs):
logout(request)
return HttpResponseRedirect(reverse("index"))
class SendSmsView(View):
"""2019-10-31 發送驗證碼"""
def post(self, request, *args, **kwargs):
send_sms_form = DynamicLoginForm(request.POST)
re_dict = {}
if send_sms_form.is_valid():
mobile = send_sms_form.cleaned_data["mobile"]
# 隨機生成驗證碼
code = generate_random(4, 0)
r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=0, charset="utf-8", decode_responses=True)
r.set(str(mobile), code)
r.expire(str(mobile), 60*5) # 設置5分鐘失效
re_json = send_single_sms(api_key=yp_api_key, code=code, mobile=mobile)
if re_json["code"] == 0:
re_dict["status"] = "success"
else:
re_dict["msg"] = re_json["msg"]
else:
for key, value in send_sms_form.errors.items():
re_dict[key] = value[0] # 注意這個value是list類型
return JsonResponse(re_dict)
class RegisterView(View):
pass
forms.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time : 2019/10/29 22:55
# @Author : qizai
# @File : forms.py
# @Software: PyCharm
import redis
from django import forms
from captcha.fields import CaptchaField
from MxOnline.settings import REDIS_PORT, REDIS_HOST
class LoginForm(forms.Form):
"""2019-10-29 繼承Django默認的form"""
username = forms.CharField(required=True, min_length=3)
password = forms.CharField(required=True, min_length=6)
class DynamicLoginForm(forms.Form):
"""10-30 圖片驗證碼form,存圖片驗證碼"""
mobile = forms.CharField(required=True, min_length=11, max_length=11)
captcha = CaptchaField()
class DynamicLoginPostForm(forms.Form):
"""11-02 短信登錄方式,登錄提交的時候"""
mobile = forms.CharField(required=True, min_length=11, max_length=11)
code = forms.CharField(required=True, min_length=4, max_length=4)
def clean_code(self):
"""11-03 add 這個能夠可以指定驗證的字段,並且拋出的異常就是這個指定的字段作爲key"""
mobile = self.data.get("mobile") # todo 這裏校驗指定字段的話,推薦使用self.data.get() ,因爲使用這個程序是先調用def clean_code這個方法再到def clean方法的,所以self.cleaned_data[]可能會出現取不到的問題
code = self.data.get("code")
r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=0, charset="utf-8", decode_responses=True)
redis_code = r.get(str(mobile))
if code != redis_code:
raise forms.ValidationError("驗證碼不正確") # 主動拋出異常,但是拋出的異常的key字段是這裏這個方法的clean_code對應的字段,即key就是code
return self.cleaned_data
# def clean(self):
# """這個是進行驗證這個form的數據"""
# mobile = self.cleaned_data["mobile"]
# code = self.cleaned_data["code"]
#
# r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=0, charset="utf-8", decode_responses=True)
# redis_code = r.get(str(mobile))
# if code != redis_code:
# raise forms.ValidationError("驗證碼不正確") # 主動拋出異常,但是拋出的異常的key字段是__all__不能自定義字段,所以這個clean不推薦使用,推薦使用clean_code
# return self.cleaned_data
0x0e、使用django的模板層的內建標籤及過濾器——stringformat的時候,巨坑點,中間不能留有空格
錯誤代碼:
<li>
<h2>所在地區</h2>
<div class="more">更多</div>
<div class="cont">
<a href="?ct={{ category }}"><span class="{% ifequal city_id '' %}active2{% endifequal %}">全部</span></a>
{% for city in all_citys %}
<a href="?city={{ city.id }}&ct={{ category }}"><span class="{% ifequal city_id city.id|stringformat: 'i' %}active2{% endifequal %}">{{ city.name }}</span></a>
{% endfor %}
</div>
</li>
再放大一點給寧萌康康:
再定位準確一點就是下面這裏:
{% ifequal city_id city.id|stringformat: 'i' %}
解釋:
出錯的原因是stringformat: 'i',這裏中間有個空格隔開了,而導致的不能識別
鄙人將其改爲下面這個即可正確解析網頁:
{% ifequal city_id city.id|stringformat:'i' %}
這個bug找的我好辛苦,是在下太菜了,跪了跪了 ○| ̄|_
延伸:django中文文檔,個人感覺已經很好的了,本小點的知識點鏈接,請點擊我進行了解
0x0f、對查詢結果進行排序,直接使用object.order_by(“object字段名”)
降序是:object.order_by("-object字段名")
sort = request.GET.get("sort", "")
if sort == "students":
all_orgs = all_orgs.order_by("-students") # 降序students
elif sort == "courses":
all_orgs = all_orgs.filter("-course_nums") # 對course_nums降序
0x10、forloop:在html for循環中能夠記住當前的index是第幾個
詳情使用如下:
<div class="right companyrank layout">
<div class="head">授課機構排名</div>
{% for hot_org in hot_orgs %}
<dl class="des">
<dt class="num fl">{{ forloop.counter }}</dt>
<dd>
<a href="/company/2/"><h1>{{ hot_org.name }}</h1></a>
<p>{{ hot_org.address }}</p>
</dd>
</dl>
{% endfor %}
</div>
0x11、通過url的include機制,設計url匹配模式
- 當時舊版url模式:
urls.py部分:
urlpatterns = [
url(r'^org_list/', OrgView.as_view(), name="org_list"), # OrgView爲對應app的views裏面的某個view;name是等於給這個url起個標籤類似的
]
html使用:
<a class="more" href="{% url 'org_list' %}">查看更多機構 ></a>
- 使用include機制的模式:
urlpatterns = [
url(r'^org/', include(('organizations.urls', "organizations"), namespace="org")), # 如果不熟悉,請查看源碼,urlconf_module, app_name = ("organizations.urls", "organizations")
]
html使用:
<a class="more" href="{% url 'org:list' %}">查看更多機構 ></a>
感謝認真讀完這篇教程的您
先別走唄,這裏有可能有你需要的系列文章:
1、Django踩坑記錄系列
2、或者點擊我的個人博客進行查看這系列文章