上一节我们完成了会员功能的后台管理, 这一节我们需要完成会员注册功能, 涉及到以下几个模块
- URL配置
- views视图模块
- 模板
- Request/Response对象的使用
项目地址:https://gitee.com/ccnv07/django_example
URL路由配置
django是通过项目的urls.py文件来定义网站的url路由, 在我们的项目中是cms/urls.py文件
django的基本访问流程
- 访问url时, 通过cms/urls.py中定义的url路由, 获取到要执行的视图函数或者类(保存在views.py)
- 将Request(请求对象)转发到指定的视图函数/类中, 执行视图函数/类的代码
- 通过模板渲染(没有模板则是其他的JsonResponse等资源对象), 将结果数据返回并输出到浏览器中
打开cms/urls.py文件
from django.contrib import admin
from django.urls import path
urlpatterns = [
path(‘admin/‘, admin.site.urls),
]
urlpatterns是整个路由的一个list, 是django定义的固定名称
path函数
path(route, view, kwargs=None, name=None)route
: 指定访问的路由view
: 是指定访问的视图函数/类name
: 是指定这条路由的名称, 通过这个名称, 我们就可以生成一个可访问的url
默认的这一条路由, 就是定义了整个后台的访问url, 都是以admin/开头的url, django会将admin.site.urls中定义的路由都加载过来
比如我们之前做的后台管理功能, url就是:/admin/account/account/
路由route参数的格式
1. 固定字符串路由
这种路由是固定访问的url, 不会发生变化, 比如关于我的访问页面.
urlpatterns = [
path(‘about/‘, views.about),
]
2. 带有变量的url路由, 比如我们访问指定栏目下的文章
urlpatterns = [
path(‘list/<int:nav_id>‘, views.list),
]
这种应该用会比较多一些, <int:>指定这个nav_id必须是数字类型, 会执行类型强制转换, 而nav_id就是参数名, 通过以下的方式, 就可以访问到这个参数。
# views.py
def list(request, nav_id):
pass
除了支持int, django的url路由也支持str,slug,uuid,path四种类型, 一般常用的也就是str和int
3. 正则表达式路由
from django.urls import path, re_path
from . import views
urlpatterns = [
path(‘articles/2003/‘, views.special_case_2003),
re_path(r‘^articles/(?P<year>[0-9]{4})/$‘, views.year_archive),
]
path函数定义的是普通的路由
re_path韩都定义正则路由, 参数完全一样
像上面这个例子中的re_path, 每个()中就是一个参数的定义, ?P说明这里定义的是一个参数, <year>是参数key, [0-9]{4}是正则表达式, $符代表路由结束, 不再往后匹配。
所以这个url可以匹配到articles/2018/ 这样的url
urlpatterns = [
re_path(r‘^comments/(?:page-(?P<page_number>\d+)/)?$‘, comments), # good
]
在这个例子中, ?: 代表是这是一个字符串的url, page-并不是一个参数
所以匹配的url是comments/page-1 的url
4. 包含其他的路由
from django.urls import include, path
urlpatterns = [
path(‘account/‘, include(‘account.urls‘)),
]
include
include(module, namespace=None)
include(pattern_list)
include((pattern_list, app_namespace), namespace=None)
module
: urlconf的模块namespace
: url入口的命名空间pattern_list
: 可以迭代的path()/re_path实例类app_namespace
: url入口的app的命名空间
之后会依次讲, 一般我们常见的也就是include(‘account.urls‘))
这种方式, 会将account/urls.py中定义的url路由都加载进来
一下就是在account/urls.py中定义的路由
# account/urls.py
from django.urls import path
from . import views
urlpatterns = [
path(‘index/‘, views.index, name=‘account-index‘)
]
根据以上的路由规则, 我们可访问的url就是account/index/
这个url
定义会员注册的路由
将模块路由文件加载进项目中
# cms/urls.py
import debug_toolbar
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path(‘admin/‘, admin.site.urls),
path(‘account/‘, include(‘account.urls‘))
]
这样关于会员模块的url路由, 我们就都可以在account/urls.py文件中定义了。
在模块中定义url路由
创建account/urls.py文件
# account/urls.py
from django.urls import path
from . import views
urlpatterns = [
path(‘register/‘, views.register, name=‘account-register‘)
]
View视图模块
视图函数的定义
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
每个视图函数都有一个固定的参数request, 这个是Request对象, 包含浏览器发起请求带的参数, 包括常见的url, post数据, 请求类型, header头等等。
然后视图函数的返回也必须是一个Reponse对象, 一般我们返回的都是html代码, 所以使用的是HttpResponse对象
有时候我们写的是接口, 使用的是JsonResponse对象
视图中最常用的函数
render 模板渲染函数
但是如果把html代码写在python文件中, 也太不好看了, 所以, 我们可以通过render函数来完成模板的渲染
render(request, template_name, context=None, content_type=None, status=None, using=None)
request
: 是请求对象, template_name
: 模板文件名称context
: 要传递给模板文件的变量content_type
: 文档header头中Content-Type的类型status
: http响应码, 就是200, 301, 302, 400, 500那个using
: 要使用的模板引擎
from django.shortcuts import render
def my_view(request):
# View code here...
return render(request, ‘myapp/index.html‘, {
‘foo‘: ‘bar‘,
}, content_type=‘application/xhtml+xml‘)
render会自动帮我们转换成HttpResponse对象, 所以也不需要再写一遍HttpResponse了
redirect 跳转函数
当会员注册完成后, 我们就需要自动跳转到会员中心页或者首页, 这时就得使用redirect函数来实现了
redirect(to, permanent=False, *args, **kwargs)
to
: 要跳转的地址, permanent
: 是否永久跳转, 说白了就是301/302代码的区别, 不动301,302自行百度。
from django.shortcuts import redirect
def my_view(request):
...
return redirect(‘/some/url/‘)
reverse Url路由转url函数
redirect完成url跳转时, 万一我定义的url都变了, 觉得以前定义的url太丑了, 太长了, 老板不喜欢了, 那不坑爹了么?那我不得满项目找url, 挨个改阿?
其实django已经想到这点了, 还记得我们在定义url路由时的name
参数么?
通过reverse函数, 就可以将urls.py中定义的url路由转换为url了
reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)viewname
: url路由的名称urlconf
: url路由的模块名, 默认是根模块, 也就是咱们的cms文件中的urls.pyargs
: 要传递给url路由的参数
视图中的Request和Response对象
一般发生请求后有两种资源, 一种是请求的资源,是浏览器发送给服务器的资源, 包括请求的url, 头, 传递的参数, cookie什么的。
还有一种是返回的资源, 就是服务器发送给浏览器的资源。
HttpRequest对象
常用的属性如下
属性 | 说明 |
---|---|
scheme | http 或 https |
body | 请求的主体 |
path | 请求的路径 account/register/ |
path_info | 请求的路径 |
method | 请求方法GET,POST |
encoding | 编码类型 |
content_type | header头 的Content-Type |
COOKIES | cookie信息 |
FILES | 表单的file字段上传的文件信息 |
META | header头信息 |
session | 保存session信息, dict结构 |
常用的方法
方法 | 说明 |
---|---|
get_host() | 127.0.0.1:8000 |
get_port() | 请求的端口 |
get_full_path() | 请求的全路径 |
is_ajax() | 是否ajax请求 |
HttpResponse对象
常用的属性
属性 | 说明 |
---|---|
content | 请求返回的资源内容 |
charset | 编码 |
status_code | 返回的http状态码 |
JsonResponse对象
JsonResponse(data, encoder=DjangoJSONEncoder, safe=True, json_dumps_params=None, **kwargs)
data
: 要返回的json数据, 是dict结构encoder
: 数据的转码类, 一般不需要更改json_dumps_params
: json_dumps函数
针对我们的项目, 就可以在cms/utils.py(没有就创建)文件中定义一个通用的返回jsonResponse的函数,
from django.http import JsonResponse
def return_json(code = 0, message = ‘success‘, data = [], url=‘‘):
return JsonResponse({
‘code‘: code,
‘url‘: url,
‘message‘: message,
})
模板层说明
模板文件路径
默认模板文件路径会在模块名/templates中, 但是在一般的项目开发中, 都会把所有的模板放在一起, 所以我们需要重新定义模板的路径
# cms/settings.py
TEMPLATES = [
{
‘BACKEND‘: ‘django.template.backends.django.DjangoTemplates‘,
‘DIRS‘: [
# 将templates目录放在根目录
os.path.join(BASE_DIR, ‘templates‘),
],
‘APP_DIRS‘: True,
‘OPTIONS‘: {
‘context_processors‘: [
‘django.template.context_processors.debug‘,
‘django.template.context_processors.request‘,
‘django.contrib.auth.context_processors.auth‘,
‘django.contrib.messages.context_processors.messages‘,
],
},
},
]
在settings.py 中TEMPLATES.DIRS中增加os.path.join(BASE_DIR, ‘templates‘), 模板文件的目录就变为了cms/templates
静态资源文件路径和访问url修改
静态文件路径同模板一样, 我们也需要修改到根目录下
# cms/settings.py
STATIC_URL = ‘/static/‘
STATICFILES_DIRS = (‘static‘, )
模板常用标签说明
一般模板的顶部、底部等很多地方都是一样的, 所以我们可以定义一个布局html页面, 将这些一样的地方提取出来放在一起
# templates/layout.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{% block title %} {% endblock %}</title>
<link rel="stylesheet" href="{% static ‘css/bootstrap.min.css‘%}">
</head>
<body>
{% block body %} {% endblock %}
</body>
<script src="{% static ‘js/jquery.min.js‘ %}"></script>
<script src="{% static ‘js/bootstrap.min.js‘ %}"></script>
<script src="{% static ‘js/layer/layer.js‘ %}"></script>
<script src="{% static ‘js/utils.js‘ %}"></script>
</html>
load static
这个标签是指加载static模块, 只有加载了后, 才可以使用{% static %}来读取静态资源文件block endblock
定义了不同的块, 并且为每个块进行命名
这样假设我定义了一个会员注册页
# templates/account/register.html
{% extends ‘layout.html‘ %}
{% block title %} 注册 {% endblock %}
那么, layout.html中的{% block title %} {% endblock %}就会被替换成"注册"
extends
标签, 指定的就是加载layout.html这个布局页面
定义注册会员的表单
我们先在account/forms.py中定义表单RegisterForm, 因为之前已经定义了一个AccountForm, 所以我们这个表单可以直接继承AccountForm
class RegisterForm(AccountForm):
# 设置场景是新增用户
scene = ‘insert‘
class Meta(AccountForm.Meta):
# 使用自定义的Form, 就必须指定fields or exclude属性, 否则报错
# 注册的时候我们不需要设置status, 字段, 所以排除掉。
fields = (‘account‘, ‘password‘, ‘email‘, ‘phone‘)
注册的时候, 一般需要输入重复密码, 所以我们多定义一个rep_password字段
class RegisterForm(AccountForm):
... 忽略代码
rep_password = forms.CharField(
label=‘重复密码‘,
required=True,
error_messages={‘required‘: ‘请再次输入密码‘},
widget=forms.PasswordInput())
def clean_rep_password(self):
# 验证两次输入的密码是否一致
# 因为在clean_password方法中, 已经加密了cleaned_data[‘password‘], 所以这里只能取data[‘password‘]
if self.data[‘password‘] != self.cleaned_data[‘rep_password‘]:
raise ValidationError(‘两次输入的密码不一致‘)
return self.cleaned_data[‘rep_password‘]
定义视图
在视图中, 如果是GET请求, 我们则渲染表单, 如果是POST请求, 我们就执行注册用户
GET 请求的代码
from django.shortcuts import render
from .forms import RegisterForm
def register(request):
form = RegisterForm()
return render(request, ‘account/register.html‘, {‘form‘: form})
编写模板代码
我使用的是bootstrap前端框架, 大家可以下载了放在static文件夹中, 修正layout.html中的路径
首先我们先将layout.html布局模板加载进来
# templates/account/register.html
{% extends ‘layout.html‘ %}
{% block title %} 注册 {% endblock %}
然后在block body部分, 写入我们要渲染的表单
{% block body %}
<div class="container">
<div class="row" style="width:500px">
<form action="{% url ‘account-register‘%}" method="post" onsubmit="return post(this)">
{% csrf_token %}
<div class="form-group">
<label for="{{ form.account.id_for_label}}">{{ form.account.label}}</label> {{ form.account}}
</div>
<div class="form-group">
<label for="{{ form.password.id_for_label}}">{{ form.password.label}}</label> {{ form.password}}
</div>
<div class="form-group">
<label for="{{ form.rep_password.id_for_label}}">{{ form.rep_password.label}}</label> {{ form.rep_password}}
</div>
<div class="form-group">
<label for="{{ form.email.id_for_label}}">{{ form.email.label}}</label> {{ form.email}}
</div>
<div class="form-group">
<label for="{{ form.phone.id_for_label}}">{{ form.phone.label}}</label> {{ form.phone}}
</div>
<input type="submit" value="提交" class="btn btn-success">
</form>
</div>
</div>
{% endblock %}
很多地方基本都是一样的, {{ form.}} 中的form就是我们在view中传过来的form表单类form.account_id_for_label
: 就是input的类{{ form.account.label}}
: 是显示的表单字段的名称{{ form.account}}
: 会直接生成一段input的表单字段代码。
打开浏览器, 我们可以看一下效果
看起来样式还不错
现在, 我们就可以点击提交尝试一下了
原文地址:http://blog.51cto.com/a3147972/2311477