一个在IT行业摸爬打滚的程序猿

0%

django踩坑记录(4)

0x0d、如何设置不同登录方式,显示不同的标签及对应的form表单的字段?下面代码超级多细节,真的值得看

描述的情景如下图:
不同的form表单对应不同的标签

form、及对应的标签

代码实现思路:两个不同的登录方式是:账号密码登录和使用短信验证码登录。

1.如果是使用短信验证码登录的话,给它多传递一个字段用于区别这个form表单到底是哪个,如,ynamic_login = True

2.设置这个字段用于标记,当前这个登录方式是短信验证码登录,然后将这个字段传递到前端的html页面里面,去判断,如果存在这个字段并且为True那么前端的html代码就展示短信验证码登录的相关代码即可。

3.下面看一下前端的相关代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/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的时候,巨坑点,中间不能留有空格

错误代码:

1
2
3
4
5
6
7
8
9
10
<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>

再放大一点给宁萌康康:

1
2
3
4
5
6
7
8
再定位准确一点就是下面这里:
{% 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字段名”)

1
2
3
4
5
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是第几个

详情使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
<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模式:

    1
    2
    3
    4
    5
    6
    7
    8
    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>
  • [x] 使用include机制的模式:

    1
    2
    3
    4
    5
    6
    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>
感谢认真读完这篇教程的您

先别走呗,这里有可能有你需要的系列文章:

Django踩坑记录系列