参考文档:http://django-simple-captcha.readthedocs.io/en/latest/usage.html
下载地址:https://github.com/mbi/django-simple-captcha
在类中定义CaptchaField
from captcha.fields import CaptchaField
class RegisterForm(forms.Form):
email = forms.EmailField(required=True)
password = forms.CharField(required=True, min_length=5)
captcha = CaptchaField(error_messages={'invalid': u'验证码错误', }) # invlid 返回错误信息提示,因为invalid值默认显示为英文
在view.py文件,判断验证是否通过,与其它form校验一样
from django.contrib.auth.hashers import make_password
class RegisterView(View):
def post(self,request):
registerform = RegisterForm(request.POST) # form验证
if registerform.is_valid(): 判断校验是否通过
email=request.POST('email','')
password=request.POST('password','')
userprofile=UserProfile() #创建一个userprofile的实例
userprofile.username=email
userprofile.email=email
userprofile.password=make_password(password) #对明文密码进行加密保存,数据库中存储的密码是密文
userprofile.save()
else:
return render(request, 'register.html', {
'registerform': registerform, # 将registerform加载到页面
}) # 返回登录页面,加载错误信息
在html页面使用registerform信息,与其它form验证一样
class=" {% if forgetpwd_form.errors.captcha %}errorput{% endif %}"> # 如果验证码错误信息dict中包含captcha键值对
<label>验 证 码label>
{{ forgetpwd_form.captcha }} # 显示验证码
div>
二、邮箱验证
settings.py配置邮箱
EMAIL_HOST = 'smtp.sina.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = 'qd_ltf@sina.com' # 登录用户
EMAIL_HOST_PASSWORD = 'litaifa001'
EMAIL_FROM = 'qd_ltf@sina.com' # 指明发件人,应与邮件登录用户保持一致
EMAIL_USE_TLS = False
models.py设计数据表
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from datetime import datetime
from django.db import models
class EmailVerifyRecord(models.Model):
email = models.EmailField(verbose_name=u'邮箱', max_length=50)
code = models.CharField(verbose_name=u'验证码', max_length=20)
send_type = models.CharField(verbose_name=u'发送类型', choices=(('register', u'注册'), ('forget', u'忘记'), ('update', u'更改邮箱')),max_length=10)
send_time = models.DateTimeField(verbose_name=u'发送时间', default=datetime.now)
class Meta:
verbose_name = u'邮箱验证记录'
verbose_name_plural = verbose_name
def __unicode__(self):
return self.email
定义邮件发送函数
from random import Random
from django.core.mail import send_mail
# 引入全局变量
from mxonline2.settings import EMAIL_FROM
from .models import EmailVerifyRecord
def send_verify_email(to_email, subject, message, send_type='register', from_email=EMAIL_FROM):
# 生成随机字符串,以便用户激活时进行比对
code = random_str(16)
message = message+code+r'/'+to_email+r'/'
email_record = EmailVerifyRecord()
email_record.email = to_email
email_record.code = code
email_record.send_type = send_type
# 将发送的邮件code保存到数据库,以便在激活时比对
email_record.save()
send_status = send_mail(recipient_list=[to_email], subject=subject, message=message, from_email=from_email )
return send_status
# 生成随机字符串
def random_str(random_len=8):
chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy"
chars_len = len(chars) - 1
rand_str = ''
random = Random()
while random_len > 0:
rand_str += chars[random.randint(0, chars_len)]
random_len -= 1
return rand_str
在views.py中发送邮件
class XXXXView(View):
def post(self, request):
send_status = send_verify_email(
to_email=email,
subject=u'慕学在线网激活与注册',
message=u'请点击链接完成慕学在线网的激活 http://127.0.0.1:8000/active/',
send_type='register'
)
if send_status:
return render(request, 'login.html', {})
三、修改密码
配置url
\# 修改密码
url(r'^update/pwd/$', UpdatePwdView.as_view(), name='update_pwd'),
定义form
class ModifyPwdForm(forms.Form):
password1 = forms.CharField(required=True, min_length=5)
password2 = forms.CharField(required=True, min_length=5)
定义view
class UpdatePwdView(LoginRequiredMixin,View):
def post(self,request):
pwd1=request.POST.get('password1')
pwd2=request.POST.get('password2')
if not pwd1 == pwd2:
return HttpResponse(json.dumps({'status':'fail','msg':u'两次输入密码不一致'}),content_type='application/json')
modify_form=ModifyPwdForm(request.POST)
if not modify_form.is_valid():
HttpResponse(json.dumps(modify_form.errors), content_type='application/json') # 将错误信息dict,转化为json,此处需要根据js代码而进行调整
user_profile=request.user # password在dango中是加密的方式存放,所以,需要将pwd1加密后存放;如果需要验证用户输入的密码是否正确,不能直接比较,需要用user = authenticate(username=username, password=password) 判断user返回是否是非None
user_profile.password=make_password(pwd1)
user_profile.save()
return HttpResponse(json.dumps({'status': 'success', }), content_type='application/json')
html文件
class="resetpwdbox dialogbox" id="jsResetDialog">
<h1>修改密码h1>
<div class="close jsCloseDialog"><img src="{% static '' %}images/dig_close.png"/>div>
<div class="cont">
<form id="jsResetPwdForm" autocomplete="off">
<div class="box">
<span class="word2">新 密 码span>
<input type="password" id="pwd" name="password1" placeholder="6-20位非中文字符"/>
div>
<div class="box">
<span class="word2">确定密码span>
<input type="password" id="repwd" name="password2" placeholder="6-20位非中文字符"/>
div>
<div class="error btns" id="jsResetPwdTips">div>
<div class="button">
<input id="jsResetPwdBtn" type="button" value="提交"/>
div>
{% csrf_token %}
form>
div>
div>
js代码
$(function(){
//个人资料修改密码
$('#jsUserResetPwd').on('click', function(){
Dml.fun.showDialog('#jsResetDialog', '#jsResetPwdTips');
});
$('#jsResetPwdBtn').click(function(){
$.ajax({
cache: false,
type: "POST",
dataType:'json',
url:"/user_center/update/pwd/", # 如果js文件是由外部引入,不能使用动态{% url ' ' %%}
data:$('#jsResetPwdForm').serialize(),
async: true,
success: function(data) {
if(data.password1){
Dml.fun.showValidateError($("#pwd"), data.password1); # 此处可通过将request.errors信息转化为json传入
}else if(data.password2){
Dml.fun.showValidateError($("#repwd"), data.password2);
}else if(data.status == "success"){
Dml.fun.showTipsDialog({
title:'提交成功',
h2:'修改密码成功,请重新登录!',
});
Dml.fun.winReload();
}else if(data.msg){
Dml.fun.showValidateError($("#pwd"), data.msg);
Dml.fun.showValidateError($("#repwd"), data.msg);
}
},
errors:function (data) {
alert('error')
}
});
});
四、电话号码校验
电话号码校验,对字段进行更深层次的自定义封装,
class UserAskModelForm(forms.ModelForm): # 继承forms.ModelForm,而不是 forms.Form ModelForm的使用实例
class Meta:
model = UserAsk # Model类 # 需要form校验的字段,此处字段名应与form表单提交的input的name保持一致
fields = ['name', 'mobile', 'coursename']
def clean_mobile(self): # 对字段进行更深一层的自定义封装 ,固定写法
mobile = self.cleaned_data['mobile'] # 取出mobile字段的值。cleaned_data为一个字典型数据
reg_mobile = "^1[358]\d{9}$|^147\d{8}$|^176\d{8}$" # 手机号码匹配正则表达式
p = re.compile(reg_mobile) # p为re对象
if p.match(mobile): # 能正确匹配
return mobile # 根据需要还可以返回其它值
else:
raise forms.ValidationError(u'手机号码非法', code='mobileInvalid') # 抛出错误异常
五、分页功能
相关文档:https://github.com/jamespacileo/django-pure-pagination
一、文档主要内容
Installation
Install package from PYPI:
pip install django-pure-pagination
or clone and install from repository:
git clone git@github.com:jamespacileo/django-pure-pagination.git
cd django-pure-pagination
python setup.py install
Add pure_pagination to INSTALLED_APPS
INSTALLED_APPS = (
...
'pure_pagination',
)
Finally substitute from django.core.paginator import Paginator with from pure_pagination import Paginator
Settings
A few settings can be set within settings.py # 默认值,可以不设置
PAGINATION_SETTINGS = {
'PAGE_RANGE_DISPLAYED': 10,
'MARGIN_PAGES_DISPLAYED': 2,
'SHOW_FIRST_PAGE_WHEN_INVALID': True,
}
PAGE_RANGE_DISPLAYED is the number of pages neighbouring the current page which will be displayed (default is 10)
MARGIN_PAGES_DISPLAYED is the number of pages neighbouring the first and last page which will be displayed (default is 2)
Set SHOW_FIRST_PAGE_WHEN_INVALID to True when you want to just show first page when provided invalid page instead of 404 error
Usage example
Following is a simple example for function based views. For generic class-based views, see bellow.
view file: views.py
from django.shortcuts import render_to_response
from pure_pagination import Paginator, EmptyPage, PageNotAnInteger
def index(request):
try:
page = request.GET.get('page', 1)
except PageNotAnInteger:
page = 1
objects = ['john', 'edward', 'josh', 'frank']
# Provide Paginator with the request object for complete querystring generation
p = Paginator(objects, per_page=5, request=request) # 此处说明文档有错误,request默认为none
people = p.page(page)
return render_to_response('index.html', {
'people': people,
}
template file: index.html
{# index.html #}
{% extends 'base.html' %}
{% block content %}
{% for person in people.object_list %} # 此处应修改
<div>
First name: {{ person }}
div>
{% endfor %}
{# The following renders the pagination html #}
<div id="pagination">
{{ people.render }} # 此方法,html样式不可控
div>
{% endblock %}
Usage
There a few different way you can make use of the features introduced within django-pure-pagination.
Easiest way to render the pagination is to call the render method i.e. {{ page.render }}
Alternatively you can access the Page object low level methods yourself
Special note: page_obj and current_page both point to the page object within the template.
{% load i18n %}
<div class="pagination">
{% if page_obj.has_previous %} # 如果当前页有前一页,则显示上一页,否则不显示上一页
<a href="?{{ page_obj.previous_page_number.querystring }}" class="prev">‹‹ {% trans "previous" %}a>
{% else %}
<span class="disabled prev">‹‹ {% trans "previous" %}span>
{% endif %}
{% for page in page_obj.pages %} # 对所有页面进行循环显示
{% if page %}
{% ifequal page page_obj.number %}
<span class="current page">{{ page }}span>
{% else %}
<a href="?{{ page.querystring }}" class="page">{{ page }}a>
{% endifequal %}
{% else %}
...
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a href="?{{ page_obj.next_page_number.querystring }}" class="next">{% trans "next" %} ››a>
{% else %}
<span class="disabled next">{% trans "next" %} ››span>
{% endif %}
div>
Generic Class-Based Views
Documentation for Django generic class-based views on https://docs.djangoproject.com/en/dev/ref/class-based-views/
- views.py
from django.views.generic import ListView
from pure_pagination.mixins import PaginationMixin
from my_app.models import MyModel
class MyModelListView(PaginationMixin, ListView):
# Important, this tells the ListView class we are paginating
paginate_by = 10
# Replace it for your model or use the queryset attribute instead
object = MyModel
template files:
Note that the Django generic-based list view will include the object page_obj in the context. More information on https://docs.djangoproject.com/en/dev/ref/generic-views/#list-detail-generic-views
- _pagination.html
{% load i18n %}
<div class="pagination">
{% if page_obj.has_previous %}
<a href="?{{ page_obj.previous_page_number.querystring }}" class="prev">‹‹ {% trans "previous" %}a>
{% else %}
<span class="disabled prev">‹‹ {% trans "previous" %}span>
{% endif %}
{% for page in page_obj.pages %}
{% if page %}
{% ifequal page page_obj.number %}
<span class="current page">{{ page }}span>
{% else %}
<a href="?{{ page.querystring }}" class="page">{{ page }}a>
{% endifequal %}
{% else %}
...
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a href="?{{ page_obj.next_page_number.querystring }}" class="next">{% trans "next" %} ››a>
{% else %}
<span class="disabled next">{% trans "next" %} ››span>
{% endif %}
div>
- my_app/myobject_list.html
{# my_app/myobject_list.html #}
{% extends 'base.html' %}
{% block content %}
{% for object in object_list %}
<div>
First name: {{ object.first_name }}
div>
{% endfor %}
{# The following renders the pagination html #}
{% include "_pagination.html" %}
{% endblock %}
二、应用案例
定义包含分页html代码的文件 _pagination.html # 相关css文件参照慕学在线案例
```
{% if page_obj.has_previous %} # 只需要传统参数page_obj
- 上一页
{% endif %}
{% for page in page_obj.pages %}
{% if page %}
{% ifequal page page_obj.number %}
- {{ page }}
{% else %}
- {{ page }}
{% endifequal %}
{% else %}
...
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
- 下一页
{% endif %}
# 注意:
{% for teacher in page_obj.object_list %} # 对page_obj对象遍历时,需要取page_obj.object_list
```
在html文件中插入 {% include ‘_pagination.html’ %}
{% include '_pagination.html' %}
在views.py文件中传入数据page_obj
all_course = all_course.order_by('-' + order_by)
try:
page = request.GET.get('page', 1)
except PageNotAnInteger:
page = 1 # 保证page不为空
p = Paginator(all_course, 3, request=request) # 3 表示每页3个对象
page_obj = p.page(page)
# page_obj=all_course
return render(request, 'course-list.html', {
'page_obj': page_obj,
'hot_course': hot_course,
'order_by': order_by,
})
六、404及500页面
urls.py中配置
handler404 = 'user_profile.views.page_not_found'
handler500 = 'user_profile.views.page_error'
编写函数
def page_not_found(request):
# 全局404处理函数
from django.shortcuts import render_to_response
response = render_to_response('404.html', {})
response.status_code = 404
return response
def page_error(request):
# 全局500页面处理函数,500错误主要指的是服务器端的错误,如程序中出现:print 1/0
from django.shortcuts import render_to_response
response = render_to_response('500.html', {})
response.status_code = 500
return response
修改测试
urls.py文件修改
urlpatterns = [
.....
# 配置静态文件,404 测试用
url(r'^static/(?P.*)$' , serve, {"document_root": STATIC_ROOT}),
....
settings.py文件修改
DEBUG = True
DEBUG = False # 测试404 用
ALLOWED_HOSTS = []
ALLOWED_HOSTS = ['*'] # 测试404 用
STATIC_ROOT = os.path.join(BASE_DIR, 'static') # 静态文件根目录(测试404用)
七、django与ajax
例:有Form表单
html文件中form表单
<form class="rightform" id="jsStayForm">
<div>
<img src="{% static 'images/rightform1.png' %}"/>
<input type="text" name="name" id="companyName" placeholder="名字" maxlength="25"/>
div>
<div>
<img src="{% static 'images/rightform2.png' %}"/>
<input type="text" name="mobile" id="companyMobile" placeholder="联系电话"/>
div>
<div>
<img src="{% static 'images/rightform3.png' %}"/>
<input type="text" name="coursename" id="companyAddress" placeholder="课程名" maxlength="50"/>
div>
<p class="error company-tips" id="jsCompanyTips">p>
<input class="btn" type="text" id="jsStayBtn" value="立即咨询 >"/>
{% csrf_token %}
form>
html文件中ajax函数
<script>
$(function () {
$('#jsStayBtn').on('click', function () {
$.ajax({
cache: false, #
type: "POST", # 类型,POST或GET,默认为GET
url: "{% url 'org:user_ask' %}", #发送请求的地址
data: $('#jsStayForm').serialize(), #是一个对象,连同请求发送到服务的数据
async: true,# 预期服务器返回的数据类型。如果不指定,jQ将自动根据HTTP包MINME信息来智能判断,一般我们采用json格式,可以设置为
dataType:json # 是一个方法,请求成功后的回调函数,传入返回后的数据,以及包含成功代码的字符串,此处data与前面标绿色的data不同
success: function (data) {
if (data.status) {
$('#jsStayForm')[0].reset();
alert("提交成功")
}
else {
$('#jsCompanyTips').html(data.msg)
}
},
error: function (data) { # 是一个方法,请求失败时调用此函数。传入XMLHttpRequest对象
alert('error');
}
});
});
})
script>
view文件中返回json
import json
from django.http import HttpResponse
class UserAskView(View):
def post(self, request):
......
json_dict = {'status': True} # 需要根据需要设置
# 需要用json.dumps 将dict 转换成str
return HttpResponse(json.dumps(json_dict), content_type="application/json")
又如:return HttpResponse(json.dumps(user_info.errors),content_type='application/json')
例:无Form表单,收藏
html文件中收藏元素
<div class="btn fr collectionbtn notlogin" data-favid="22" data-fav-type="1">
{% if has_fav %}已收藏{% else %}收藏{% endif %} # 保证页面刷新后仍正确显示
div>
html文件中ajax函数
// add fav
function add_fav(current_elem, fav_id, fav_type) {
$.ajax({ # ajax 的第一个参数,字典
cache: false,
type: "POST",
url: "{% url 'org:add_fav' %}",
data: {'fav_id': fav_id, 'fav_type': fav_type},
async: true,
beforeSend: function (xhr, settings) { # 需要添加csrf_token
xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}"); # 添加csrf_token
},
success: function (data) { # ajax 的第二个参数,回调函数
if (data.status == 'fail') {
if (data.msg == '用户未登录') {
window.location.href = "login.html";
} else {
alert(data.msg)
}
} else if (data.status == 'success') {
current_elem.text(data.msg)
}
},
error:function (data) { # ajax 的第三个参数,回调函数
alert('error')
},
});
}
// click add fav
$('.collectionbtn').on('click', function () { # 点击触发
add_fav($(this), {{ org.id }}, 'organization'); # 参数
});
view文件中返回json
class AddFavView(View):
def post(self, request):
# 检查用户是否登录
user = request.user # 无论用户是否登录,request 都有一个user对象
if not user.is_authenticated: # 检查用户是判断登录
return HttpResponse(json.dumps({'status': 'fail', 'msg': u'用户未登录'}), content_type='application/json')
fav_id = request.POST.get('fav_id', '0')
fav_type = request.POST.get('fav_type', '')
fav_record = UserFavorite.objects.filter(userprofile=user, favid=int(fav_id), favtype=fav_type)
# 如果有户已收藏,则取消收藏
if fav_record:
fav_record.delete()
return HttpResponse(json.dumps({'status': 'success', 'msg': u'收藏'}), content_type='application/json')
# 如果用户未收藏,则收藏
user_fav = UserFavorite()
user_fav.userprofile = user
user_fav.favid = int(fav_id)
user_fav.favtype = fav_type
user_fav.save()
return HttpResponse(json.dumps({'status': 'success', 'msg': u'已收藏'}), content_type='application/json')
八、课程播放页面
参考网站:http://videojs.com/getting-started/#customize
插入相应的js css
<head>
<link href="http://vjs.zencdn.net/5.8.8/video-js.css" rel="stylesheet">
<script src="http://vjs.zencdn.net/ie8/1.1.2/videojs-ie8.min.js">script>
head>
<body>
<video id="my-video" class="video-js" controls preload="auto" width="640" height="264"
poster="MY_VIDEO_POSTER.jpg" data-setup="{}">
<source src="{{ video.url }}" type='video/mp4'>
<source src="{{ video.url }}" type='video/webm'>
<p class="vjs-no-js">
To view this video please enable JavaScript, and consider upgrading to a web browser that
<a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 videoa>
p>
video>
<script src="http://vjs.zencdn.net/5.8.8/video.js">script>
body>
将外链保存到数据库存的url中