django-admin startproject dailyfresh
cd dailyfresh
# 新建 apps包 用于存放项目模块
cd apps
python manage.py startapp user
python manage.py startapp goods
python manage.py startapp cart
python manage.py startapp order
传统的 应用注册
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'apps.user',
'apps.cart',
'apps.order',
'apps.goods',
)
修改默认的绝对路径
settings.py
import os
import sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0,os.path.join(BASE_DIR,'apps'))
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'user',
'cart',
'order',
'goods',
)
富文本编辑器
pip install django-tinymce==2.6.0
pip install Pillow
dailyfresh/settings.py
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
import sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0,os.path.join(BASE_DIR,'apps'))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '9emu2f$d9wbp2hw)88=(p!iwco#@ni=3)4ewj3n=(^_)%(u*gv'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'tinymce', #富文本编辑器
'user', #用户模块
'order', #订单模块
'cart', #购物车模块
'goods', #商品模块
)
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
)
ROOT_URLCONF = 'dailyfresh.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [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',
],
},
},
]
WSGI_APPLICATION = 'dailyfresh.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'dailyfresh',
'USER': 'root',
'PASSWORD': 'root',
'HOST':'127.0.0.1',
'PORT': 3306,
}
}
#Django认证系统使用的模型类
AUTH_USER_MODEL = 'user.User'
# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR,'static')]
# 富文本编辑器配置
TINYMCE_DEFAULT_CONFIG = {
'theme': 'advanced',
'width': 600,
'height': 400,
}
dailyfresh/urls.py
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^tinymce/',include('tinymce.urls')), #富文本编辑器
url(r'^user/',include('user.urls',namespace='user')), #用户模块
url(r'^cart/',include('cart.urls',namespace='cart')), #购物车模块
url(r'order/',include('order.urls',namespace='order')), #订单模块
url(r'^',include('goods.urls',namespace='goods')), #商品模块
]
apps/goods/urls.py
from django.conf.urls import url
from goods import views
urlpatterns = [
url(r'^$',views.index,name='index')
]
apps/goods/views.py
from django.shortcuts import render
# Create your views here.
#http://127.0.0.1:8000
def index(request):
'''首页'''
return render(request,'index.html')
apps/user/urls.py
from django.conf.urls import url
from user import views
urlpatterns = [
url(r'^register$',views.register,name='register'), #注册
url(r'^register_handle$',views.register_handle,name='register_handle'), #注册处理
]
apps/user/views.py
from django.shortcuts import render,redirect
from django.core.urlresolvers import reverse #使用反向解析
from user.models import User
import re
# Create your views here.
# /user/register
def register(request):
'''显示注册页面'''
return render(request,'register.html')
def register_handle(request):
#'''进行注册处理'''
# 接收数据
username = request.POST.get('user_name')
password = request.POST.get('pwd')
email = request.POST.get('email')
allow = request.POST.get('allow')
# 进行数据校验
# all方法,对其中参数判断,都为真,all函数返回真
if not all([username, password, email]):
# 数据不完整
return render(request, 'register.html', {'errmsg': '数据不完整'})
# 校验邮箱 /^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$/
if not re.match(r'^[a-z0-9][\w.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
return render(request, 'register.html', {'errmsg': '邮箱格式不正确'})
# 是否同意协议,同意协议传过来的参数是 on
if allow != 'on':
return render(request, 'register.html', {'errmsg': '请同意协议'})
# 校验用户名是否重复
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
# 用户不存在
user = None
if user:
# 用户已存在
return render(request, 'register.html', {'errmsg': '用户名已存在'})
# 进行业务处理
# 数据存储
user = User.objects.create_user(username, email, password)
user.is_active = 0
user.save()
# 返回应答, 跳转到首页
return redirect(reverse('goods:index'))
注册页面和注册处理使用同一个URL
apps/urls/views.py
from django.shortcuts import render,redirect
from django.core.urlresolvers import reverse #使用反向解析
from user.models import User
import re
# Create your views here.
# /user/register
def register(request):
'''显示注册页面'''
# 判断请求方式处理请求页面
if request.method == 'GET':
# 显示注册页面
return render(request,'register.html')
else:
# 进行注册处理
# 接收数据
username = request.POST.get('user_name')
password = request.POST.get('pwd')
email = request.POST.get('email')
allow = request.POST.get('allow')
# 进行数据校验
# all方法,对其中参数判断,都为真,all函数返回真
if not all([username, password, email]):
# 数据不完整
return render(request, 'register.html', {'errmsg': '数据不完整'})
# 校验邮箱 /^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$/
if not re.match(r'^[a-z0-9][\w.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
return render(request, 'register.html', {'errmsg': '邮箱格式不正确'})
# 是否同意协议,同意协议传过来的参数是 on
if allow != 'on':
return render(request, 'register.html', {'errmsg': '请同意协议'})
# 校验用户名是否重复
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
# 用户不存在
user = None
if user:
# 用户已存在
return render(request, 'register.html', {'errmsg': '用户名已存在'})
# 进行业务处理
# 数据存储
user = User.objects.create_user(username, email, password)
user.is_active = 0
user.save()
# 返回应答, 跳转到首页
return redirect(reverse('goods:index'))
更改注册页面HTML
基于类试图实现 注册页面
apps/user/views.py
from django.shortcuts import render,redirect
from django.core.urlresolvers import reverse #使用反向解析
from user.models import User
from django.views.generic import View # 类试图
import re
class RegisterView(View):
'''注册类'''
def get(self,request):
'''显示注册页面'''
return render(request,'register.html')
def post(self,request):
'''进行注册处理'''
# 接收数据
username = request.POST.get('user_name')
password = request.POST.get('pwd')
email = request.POST.get('email')
allow = request.POST.get('allow')
# 进行数据校验
# all方法,对其中参数判断,都为真,all函数返回真
if not all([username, password, email]):
# 数据不完整
return render(request, 'register.html', {'errmsg': '数据不完整'})
# 校验邮箱 /^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$/
if not re.match(r'^[a-z0-9][\w.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
return render(request, 'register.html', {'errmsg': '邮箱格式不正确'})
# 是否同意协议,同意协议传过来的参数是 on
if allow != 'on':
return render(request, 'register.html', {'errmsg': '请同意协议'})
# 校验用户名是否重复
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
# 用户不存在
user = None
if user:
# 用户已存在
return render(request, 'register.html', {'errmsg': '用户名已存在'})
# 进行业务处理
# 数据存储
user = User.objects.create_user(username, email, password)
user.is_active = 0
user.save()
# 返回应答, 跳转到首页
return redirect(reverse('goods:index'))
apps/user/urls.py
from django.conf.urls import url
from user.views import RegisterView
urlpatterns = [
url(r'^register$',RegisterView.as_view(),name='register'), #注册
]
激活用户token
itsdangerous 内部默认使用基于HMAC和SHA1来签名,基于 Django签名模块
pip install itsdangerous
使用简介:
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
serializer = Serializer('secretkey',3600)
info = {'confirm':1}
res = serializer.dumps(info) #对 info 进行加密
res # 返回加密信息
b'eyJhbGciOiJIUzUxMiIsImlhdCI6MTU2NTYwOTE4NCwiZXhwIjoxNTY1NjEyNzg0fQ.eyJjb25maXJtIjoxfQ._nMyozjltsFrk7cNYD2eT7rjLDl4bwE0Ur15Z-k5RM_cZyqkv0WYji9gvbJqaNUIAI_Rvld_VjG4Fu6dIC8Dxw'
serializer.loads(res) # 解密,加密是一个字典,解密也是一个字典
第一个参数是秘钥,第二个参数是过期小时(以秒为单位)
apps/user/views.py
from django.shortcuts import render,redirect
from django.core.urlresolvers import reverse #使用反向解析
from user.models import User
from django.http import HttpResponse
from django.views.generic import View # 类试图
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer # 签名模块
from itsdangerous import SignatureExpired # 签名过期异常
from django.core.mail import send_mail # 发送邮件
from django.conf import settings # 导入 dailyfresh/settings.py 的配置项
import re
class RegisterView(View):
'''注册类'''
def get(self,request):
'''显示注册页面'''
return render(request,'register.html')
def post(self,request):
'''进行注册处理'''
# 接收数据
username = request.POST.get('user_name')
password = request.POST.get('pwd')
email = request.POST.get('email')
allow = request.POST.get('allow')
# 进行数据校验
# all方法,对其中参数判断,都为真,all函数返回真
if not all([username, password, email]):
# 数据不完整
return render(request, 'register.html', {'errmsg': '数据不完整'})
# 校验邮箱 /^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$/
if not re.match(r'^[a-z0-9][\w.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
return render(request, 'register.html', {'errmsg': '邮箱格式不正确'})
# 是否同意协议,同意协议传过来的参数是 on
if allow != 'on':
return render(request, 'register.html', {'errmsg': '请同意协议'})
# 校验用户名是否重复
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
# 用户不存在
user = None
if user:
# 用户已存在
return render(request, 'register.html', {'errmsg': '用户名已存在'})
# 进行业务处理
# 数据存储
user = User.objects.create_user(username, email, password)
user.is_active = 0
user.save()
# 发送激活邮件,包含激活链接:
# 激活链接中需要包含用户的身份信息 http://127.0.0.1:8000 /user/active/ 数据库ID
# 不能明文发送,需要加密
# 加密用户用户信息,生成激活token
# 秘钥可以使用 dailyfresh/settings.py 中 SECRET_KEY
serializer = Serializer(settings.SECRET_KEY,3600) # 3600=1h 过期
info = {'confirm':user.id}
token = serializer.dumps(info) # bytes 类型
token = token.decode('utf8') # 转换为 utf8 字符串
# 发送邮件
# 邮件标题 通过 subject 指定, message 邮件正文, sender 指定发件人, receiver 收件人列表, html_message 将消息渲染称HTML
subject = '天天生鲜欢迎信息'
message = ''
sender = settings.EMAIL_FROM
html_message = '<h1> %s, 欢迎您成为天天生鲜注册会员</h1> 请点击下面链接激活您的账户 <br/> <a href="http://127.0.0.1:8000/user/active/%s">http://127.0.0.1:8000/user/active/%s </a>' %(username,token,token)
receiver = [email]
send_mail(subject,message,sender,receiver,html_message=html_message)
# 返回应答, 跳转到首页
return redirect(reverse('goods:index'))
# 激活视图
class ActiveView(View):
'''用户激活'''
def get(self,request,token):
'''进行用户激活'''
# 解密,获取要激活的用户信息
serializer = Serializer(settings.SECRET_KEY, 3600) # 3600=1h 过期
try:
info = serializer.loads(token)
# 获取激活用户的id
user_id = info['confirm']
# 根据id 获取获取用户信息
user = User.objects.get(id=user_id)
user.is_active = 1
user.save()
# 激活成功,调整到登录界面
return redirect(reverse('user:login'))
except SignatureExpired as e:
# 激活链接已过期
return HttpResponse('激活链接已过期')
# /user/login
# 登录视图
class loginView(View):
'''登录'''
def get(self,request):
'''显示登录页面'''
return render(request,'login.html')
apps/user/urls.py
from django.conf.urls import url
from user.views import RegisterView,ActiveView,loginView
urlpatterns = [
url(r'^register$',RegisterView.as_view(),name='register'), #注册
url(r'^active/(?P<token>.*)',ActiveView.as_view(),name='active'), # 用户激活
url(r'^login/$',loginView.as_view(),name='login'), #登录
]
dailyfresh/settings.py
中设置邮箱
# 发送邮件配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# smpt服务地址
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
# 发送邮件的邮箱
EMAIL_HOST_USER = 'smart@163.com'
# 在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = 'root'
# 收件人看到的发件人
EMAIL_FROM = '天天生鲜<smart@163.com>'
celery 异步发送邮件
pip install celery==4.1.1
pip install redis
在项目文件夹下,新建 ·celery_tasks
python package 文件包 作为发送者任务方
celery_tasks/tasks.py
# 使用celery
from celery import Celery #导入 Celery 类
from django.conf import settings # 导入dailyfresh/settings.py 中配置信息
from django.core.mail import send_mail #导入 发送邮件函数
import time
#创建一个Celery 对象
app = Celery('celery_tasks.tasks',broker='redis://127.0.0.1:6379/8')
#第一个参数是路径, broker 定义队列
#定义任务函数
@app.task
def send_register_active_email(to_email,username,token): # 发送邮件地址,用户,token信息
'''发送激活邮件'''
# 发送邮件
# 邮件标题 通过 subject 指定, message 邮件正文, sender 指定发件人, receiver 收件人列表, html_message 将消息渲染称HTML
subject = '天天生鲜欢迎信息'
message = ''
sender = settings.EMAIL_FROM
html_message = '<h1> %s, 欢迎您成为天天生鲜注册会员</h1> 请点击下面链接激活您的账户 <br/> <a href="http://127.0.0.1:8000/user/active/%s">http://127.0.0.1:8000/user/active/%s </a>' % (username, token, token)
receiver = [to_email]
send_mail(subject, message, sender, receiver, html_message=html_message)
time.sleep(30)
apps/user/views.py
from celery_tasks.tasks import send_register_active_email #导入 celery 中定义的任务函数
class RegisterView(View):
...
# 发送邮件
# 使用 celery 发送异步邮件, 使用delay方法
send_register_active_email.delay(email,username,token)
# 返回应答, 跳转到首页
return redirect(reverse('goods:index'))
处理者 端
在使用Redis 另一台机器上(或同一台机器上)同样拷贝 项目文件,进入项目文件目录
celery -A celery_tasks.tasks worker -l info
# Django 配置初始化,以便celery的监听工作者 可以使用 Django中的一些信息
# 在任务处理者一段,添加如下代码
import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dailyfresh.settings") #源码在 dailyfresh/wsgi.py 中
django.setup()
异常问题处理
Task handler raised error: ValueError('not enough values to unpack (expected 3, got 0)',)
网上的一些博客说是windows 不支持 celery4+, linux上支持
解决方法参考链接
pip install eventlet
支持异步处理命令 添加一个参数
celery -A celery_tasks.tasks worker -l info -P eventlet
登录校验
apps/user/views.py
from django.contrib.auth import authenticate # 用户登录认证模块
# 认证一组指定的用户名和密码,它接收关键字参数形式的凭证,使用默认配置对象是username和password,如果密码能够匹配指定的用户名,返回一个User对象,否则返回None
from django.contrib.auth import login
# 有一个认证的用户,使用login函数将其放入session会话中,它接收一个HTTPRequest对象和一个User对象,使用Django的session框架来讲用户的ID保存在session中
class loginView(View):
'''登录'''
def get(self,request):
'''显示登录页面'''
return render(request,'login.html')
def post(self,request):
'''登录校验'''
# 接收数据
username = request.POST.get('username')
password = request.POST.get('pwd')
# 校验数据
# 是否都 传递成功
if not all([username,password]):
return render(request,'login.html',{'errmsg':'数据不完整'})
# 业务处理
# 登录校验
user = authenticate(username=username,password=password)
if user is not None:
# 正确,验证用户是否激活
if user.is_active:
# 密码正确 ,记录用户登录状态
login(request,user)
# 页面跳转,跳转首页
return redirect(reverse('goods:index'))
else:
return render(request,'login.html',{'errmsg':'用户未激活,请激活您的账号'})
else:
# 用户密码不正确
return render(request,'login.html',{'errmsg':'用户名或密码不正确'})
配置Redis作为Django缓存和Session存储后端
pip install django-redis==4.8.0
apps/dailyfresh/settings.py
# Django 配置Redis缓存
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/9",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
# Redis 作为session backend 使用配置:配置session存储
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
Redis 客户端输出
127.0.0.1:6379[9]> keys *
1) ":1:django.contrib.sessions.cacheksskn9cekdz5u6q2qqwtrudyk6y79twf"
实现 记住用户名
apps/user/views.py
class loginView(View):
'''登录'''
def get(self,request):
'''显示登录页面'''
# 判断是否记住了用户名
if 'username' in request.COOKIES:
username = request.COOKIES.get('username')
checked = 'checked'
else:
username = ''
checked = ''
#使用 ,输出传入模板
return render(request,'login.html',{'username':username,'checked':checked})
def post(self,request):
'''登录校验'''
# 接收数据
username = request.POST.get('username')
password = request.POST.get('pwd')
# 校验数据
# 是否都 传递成功
if not all([username,password]):
return render(request,'login.html',{'errmsg':'数据不完整'})
# 业务处理
# 登录校验
user = authenticate(username=username,password=password)
if user is not None:
# 正确,验证用户是否激活
if user.is_active:
# 密码正确 ,记录用户登录状态
login(request,user)
response = redirect(reverse('goods:index')) # 是一个HttpResponseRedirect 对象类
# 判断是否需要记住用户名
remember = request.POST.get('remember')
if remember =='on':
# 记住用户名
response.set_cookie('username',username,max_age=7*24*3600) #第二参数是过期时间,单位秒
else:
# 不记住,session删掉
response.delete_cookie('username')
# 页面跳转,跳转首页
return response
else:
return render(request,'login.html',{'errmsg':'用户未激活,请激活您的账号'})
else:
# 用户密码不正确
return render(request,'login.html',{'errmsg':'用户名或密码不正确'})
templates/login.html
输入标签添加vlue
<input type="text" name="username" class="name_input" value="{{ username }}" placeholder="请输入用户名">
<input type="checkbox" name=""> {{ checked }}