写在前面
本篇笔记我们将介绍我要学习小页面的配置,为什么需要单独介绍,是因为这个和其他页面的耦合度不是很高,单独写一篇便于今后学习的需要。
本篇笔记对应于第十七篇代码,对应于github的位置是https://github.com/licheetools/eduline。
配置我要学习
这就是我要学习的页面,它是通过Ajax来控制的,也就是异步的javascript和xml。它可以在保证当前页面不被重新刷新的条件下,提交表单向后台传送数据。
正如你在前面所学习的那样,但凡涉及到了表单的提交都会有一个form验证。我们仿照前面的逻辑在operation应用下面新建一个forms.py文件,用于此处表单的验证。
然后在里面添加如下代码:
from django import forms
class UserAskForm(forms.Form):
name = forms.CharField(required=True, min_length=2, max_length=20)
mobile = forms.CharField(required=True, min_length=11, max_length=11)
course_name = forms.CharField(required=True, min_length=5, max_length=50)
就是这个样子:
接着打开operation/models.py文件,发现这两个函数定义的字段信息非常相似,连要求都是一样的:
那么我们就有一个疑问,如何让代码重复利用,不需要再次重新造轮子呢?我们可以利用Django自带的ModelForm,这比form强大多了,除了继承现有的字段还可以新增字段!新增代码如下:
# 进阶版的函数
class AnotherUserAskForm(forms.ModelForm):
# 除了继承现有的字段还可以新增字段
class Meta:
model = UserAsk
# 自定义需要验证的字段
fields = ["name", "mobile", "course_name"]
就是这个样子:
接下来的一步就是path的配置了,现在有一个疑问,就是我们的项目url文件,已经存放了很多url,如果我们再往里面新增url就很容易造成管理上的混乱:
我们在思考能不能像高考考场一样,有许多分考场,学生在各个分考场的试卷会送到省里,在保证考试有效的情况下,避免人员来往市区的麻烦。Django里面其实也是有的,所以我们就采用include的机制,来达到这个目的:在保证path的有效情况下,将各个app应用的path分开放置,最后将app的path引入项目的path就可以了。下面我们开始在organization应用下面,新建urls.py文件,里面增加如下代码:
from django.urls import path, include, re_path
from .views import OrgView
urlpatterns = [
# 课程机构列表页url
path("list/", OrgView.as_view(), name="org_list"),
]
然后回到我们的eduline/urls.py文件,我们将刚才的那行path用下面的一行include代码替换掉:
# 课程机构应用path配置
path("org/", include('organization.urls', namespace="org")),
注意: namespace是命名空间,可以防止因名字重复而导致发生错误。
就是这个样子:
Specifying a namespace in include() without providing an app_name '
django.core.exceptions.ImproperlyConfigured: Specifying a namespace in
include() without providing an app_name is not supported. Set the app_name
attribute in the included module, or pass a 2-tuple containing the list of
patterns and app_name instead.
错误上说我们没有写上app的名字,所以我们需要在各个app的urls.py文件里面加上各个app_name:app_name = "organization"
就是这个样子:
然后重启我们的项目,在浏览器地址栏输入http://127.0.0.1:8000/org/list然后回车,发现和我们之前的页面一样。不知道你注意到没有,现在我们输入的是http://127.0.0.1:8000/org/list而不是之前的http://127.0.0.1:8000/org_list。也就是说,现在我们所有的关于课程机构的url都是在org这个分页面之下的,其实这个org就是我们之前在项目urls中配置的organization的分path的一个总名字。
为了验证我们的操作是否有问题,我们采用点击页面中课程机构列表来查看当前页面是不是没有变化(没有变化是正常的,因为指向同一个url)。首先我们需要打开base.html页面,在里面ctrl+F 搜索授课机构
,将所在一行的代码修改如下:
<li class="active" ><a href="{% url 'org:org_list' %}">授课机构</a></li>
然后运行我们的项目,在浏览器地址栏输入http://127.0.0.1:8000/org/list然后回车,出现这个页面,再点击页面中课程机构列表,发现还是这个页面,确实是这样,我们的配置没有问题:
继续Ajax配置
前面说到我要学习的页面,它是通过Ajax来控制的,是一种异步加载方式,所以我们此时不能在view里面直接render一个页面回来,应该是给前端返回json数据,而不是页面。这里我们就要用到HttpResponse这个类,它指明了给用户返回哪种类型的数据。
打开organization/views.py文件,在里面添加如下代码:
from operation.forms import AnotherUserAskForm
# 我要学习功能实现
class AddUserAskView(View):
def post(self, request):
userask_form = AnotherUserAskForm(request.POST)
# 判断form是否有效
if userask_form.is_valid():
# 注意modelform和form的区别,modelform它有model的属性,而且有个参数commit,当它为真时会把数据存入到数据库
user_ask = userask_form.save(commit=True)
# 如果保存成功,则返回json,不过后面必须有content_type用于告诉浏览器返回的类型
return HttpResponse("{'status': 'success'}", content_type='application/json')
else:
# 如果保存失败,则返回json,并将form的错误信息通过msg传递到前端进行显示
return HttpResponse("{'status': 'fail', 'msg':{0}}".format(userask_form.errors), content_type='application/json')
接着打开organization/urls.py文件,我们配置path,在里面添加如下代码:
from .views import AddUserAskView
# 用户咨询配置url
path("add_ask/", AddUserAskView.as_view(), name="add_ask"),
然后打开org-list.html页面,在最底下新添加如下Ajax代码:
{% block custom_js %}
<script>
$(function(){
$('#jsStayBtn').on('click', function(){
$.ajax({
cache: false,
type: "POST",
url:"{% url 'org:add_ask' %}",
data:$('#jsStayForm').serialize(),
async: true,
success: function(data) {
if (data.status == 'success'){
$('#jsStayForm')[0].reset();
alert("提交成功")
}else if (data.status == 'fail'){
$('#jsCompanyTips').html(data.msg)
}
},
});
});
})
</script>
{% endblock %}
这个是Ajax代码,熟悉javascript的小伙伴们肯定很好理解,$(function(){})是程序入口,这里面其实就是通过DOM(文本对象模型)来控制节点,从而达到控制节点树的目的。我们是通过监听id为jsStayBtn的按钮来实现对数据的控制,当用户点击了这个按钮,就会触发url跳转,如果成功则继续调用data函数,它有两个状态:成功和失败,分别对应不同的提示信息。这里就不详细介绍了。别忘记在form表单提交加上{% csrf_token %}。
现在我们测试一下我们的项目,在图示位置打上断点,开启DeBug模式:
在前端页面输入信息:
再打开数据库,查看operation_userask这个数据表,发现数据已经保存成功了:
不知道细心地你发现没有,我刚才电话号码输了:12306,页面提示是手机号,所以必须是11位,因此这里必须加上手机号码的合法性验证。
打开我们的operation/forms.py文件,里面加上如下代码:
import re
# 验证手机号码是否合法
def clean_mobile(self):
mobile = self.cleaned_data["mobile"]
REGEX_MOBILE = "^1[358]\d{9}$|^147\d{8}$|^176\d{8}$"
p = re.compile(REGEX_MOBILE)
if p.match(mobile):
return mobile
else:
raise forms.ValidationError("手机号码非法", code="mobile_invalid")
关于正则表达式的用法,大家可以关注我的另一个专题《趣玩Python爬虫》,我目前在筹划中,预计9月份就会更新了。
接下来你可以重复刚才的测试操作,看手机号码验证是否生效了,这里就不一一演示了。
至此,我们关于我要学习的小页面的配置就到此为止了,感谢你的赏阅。下一篇笔记,我们就介绍课程机构的详情页面的配置了。
本篇笔记对应于第十七篇代码,对应于github的位置是https://github.com/licheetools/eduline。
网友评论