django踩坑記錄(4)

0x0d、如何設置不同登錄方式,顯示不同的標籤及對應的form表單的字段?下面代碼超級多細節,真的值得看

描述的情景如下圖:
form表單對應的標籤

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、或者點擊我的個人博客進行查看這系列文章

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