windows虚拟环境安装及配置
pip install virtualenv
virtualenv testvir # 默认在C:\Users\Administrator\testvir\
cd testdir/Scripts
activate.bat # 进入testdir虚拟环境
deactivate.bat # 退出环境
## 虚拟环境需要知道当前安装的虚拟环境的目录,这样较麻烦,所以用另一款开发库
pip install virtualenvwrapper-win
mkvirtualenv -p C:\Users\Anaconda3\python.exe testvir2 # 默认安装在 C:\Users\Administrator\Envs\testvir2
deactivate.bat # 退出当前环境
workon # 查看所有虚拟环境
workon testvir2 # 这样就进入了虚拟环境,就可以在虚拟环境下进行匹配安装了
# pycharm 创建项目导入虚拟环境的python.exe,查看setting中的interpreter下安装了哪些包
配置host 0.0.0.0 ,那么你本机的ip4地址也能访问了。
django配置
1. 创建log,static,media[存放用户上传的文件],apps目录,将所有的app(message)放入apps,并在setting中注册app
2. pycharm设置apps目录为mark Directory as sources root,pycharm可以直接from message import views(不用from apps.message import views)
3. cmd的命令行不能from message import views,需要settings中设置,把apps添加进去 import sys sys.path.insert(0,os.path.join(BASE_DIR,'apps'))
4. 配置mysql数据库
5. python manage.py makemigrations(变更表的比较) // python manage.py migrate (将变更迁徙到数据库)
6. 配置template路径 'DIRS': [os.path.join(BASE_DIR, 'templates')]
配置static路径: STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
django的admin后台管理
1. 创建超级用户:python manage.py createsuperuser
2. settings中该admin后台管理为中文:LANGUAGE_CODE = 'zh-hans' ,更改时区:TIME_ZONE = 'Asia/Shanghai' USE_TZ = False
3. 将写好的UserProfile类添加到admin中,urlProfile参考 点击打开链接
class UserProfileAdmin(admin.ModelAdmin):
pass
admin.site.register(UserProfile,UserProfileAdmin)
这样就可以管理了
使用django的xadmin
1. windows:https://github.com/sshwsfc/xadmin.git (可能这个最新的版本不适合django1.9.8了,可以下载git@github.com:movehaolv/eduProj.git 里的xadmin)然后 pip install xadmin-master.zip (多试几次) ;或者在django的根目录(templates同级目录下)下创建extra_app,解压 xadmin-master.zip将里面的admin文件夹复制到extra_app,再将extra_app 修改权限mark Directory as sources root,这样就能直接用admin了,但是xadmin的依赖包crispy_forms(pip install x-admin 附带下载的)是没有的(也可以直接pip install django-crispy-forms)。linux可以直接pip
2. 注册app : 'xadmin' ,'crispy_forms'
3. 替换url: import xadmin url(r'^xadmin/', xadmin.site.urls),
4. 迁徙xadmin的表 : python manage.py migrate ,会生成4张xadmin的表,里面的表会自动关联到上面创建的UserProfile表(比如:xadmin_usersettings)
5. 注册app到xadmin后台管理系统:
新建对应的app下新建adminx.py文件,添加model类(继承的AbstractUser用以扩展的User的Model无需注册,会自己注册)
class EmailVerifyRecordAdmin(object):
list_display = ['code','email','send_type','send_time'] # 打开xadmin后显示的字段。不是添加对象(比如添加课程显示的字段,添加课程显示的字段是这个表的所有字段)
search_fields = ['code','email','send_type'] # 添加搜寻,不要在这里添加 “时间”的字段
list_filter = ['code','email','send_type','send_time'] # 添加过滤功能
xadmin.site.register(EmailVerifyRecord,EmailVerifyRecordAdmin)
1.用户登录
1. 用post提交form的用户名和密码,后台进行验证
from django.shortcuts import render,redirect
from django.contrib.auth import authenticate,login
class LoginView(View):
def get(self,request):
# 记住来源的url,如果没有则设置为首页('/')
request.session['login_from'] = request.META.get('HTTP_REFERER', '/')
return render(request,'login.html')
def post(self,request):
form = LoginForm(request.POST)
if form.is_valid(): # form字段的数据格式是否符合form表单要求
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(username=username,password=password) # 拿用户名和密码去数据中对比是否存在 A
if user is not None:
login(request,user)
if user.is_active:
login(request,user) # 登录账户,这样xadmin也就登录了
# 重定向到来源的url
return redirect(request.session['login_from'])
else:
return render(request, 'login.html', {'msg': '用户未激活'})
else:
return render(request,'login.html',{'msg':'用户名/密码错误'})
else:
return render(request,'login.html',{'form':form}) # form传到前端,用以显示错误 <div class="error btns login-form-tips" id="jsLoginTips">{% for k,v in form.errors.items %}{{ v }}{% endfor %}{{ msg }}</div>
# 记住来源的url,如果没有则设置为首页('/')
request.session['login_from'] = request.META.get('HTTP_REFERER', '/')
return render(request,'login.html')
def post(self,request):
form = LoginForm(request.POST)
if form.is_valid(): # form字段的数据格式是否符合form表单要求
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(username=username,password=password) # 拿用户名和密码去数据中对比是否存在 A
if user is not None:
login(request,user)
if user.is_active:
login(request,user) # 登录账户,这样xadmin也就登录了
# 重定向到来源的url
return redirect(request.session['login_from'])
else:
return render(request, 'login.html', {'msg': '用户未激活'})
else:
return render(request,'login.html',{'msg':'用户名/密码错误'})
else:
return render(request,'login.html',{'form':form}) # form传到前端,用以显示错误 <div class="error btns login-form-tips" id="jsLoginTips">{% for k,v in form.errors.items %}{{ v }}{% endfor %}{{ msg }}</div>
在前端想要添加错误信息,那么可以这样写,如果有错就会有提示。为了更有好提示,可以添加样式
{% for k,v in form.errors.items %}{{ v }}{% endfor %}{{ msg }}
2 . 上面的方式只能邮箱,密码登录。通过重写authenticate方法,达到邮箱也可以登录
× 在settings中注册
# Application definition
AUTHENTICATION_BACKENDS = (
'users.views.CustomBackend',
)
× 然后重写authenticate,这样登录的时候就可以用邮箱登录了。执行顺序 A --> B,最后返回B中的user
from django.db.models import Q
from django.contrib.auth.backends import ModelBackend
from .models import UserProfile
class CustomBackend(ModelBackend):
def authenticate(self, username=None, password=None, **kwargs):
try:
user = UserProfile.objects.get(Q(username=username)|Q(email=username)) # 获取到username的记录 B
if user.check_password(password): # django不支持form表单的明文密码反解,所以只能先将form里的明文passowrd转换成密文,然后和数据库的比对
return user
except Exception as e:
return None
用户登出
from django.contrib.auth import logout
class LogoutView(View):
def get(self,request): #
logout(request)
return redirect(request.META.get('HTTP_REFERER', '/'))
2.用户注册
第六章1 100min
第一步:添加验证码登录
× pip install django-simple-captcha==0.4.6 # https://github.com/mbi/django-simple-captcha
× 配置url url(r'^captcha/', include('captcha.urls')), ----> 注册app 'captcha', -----> migrations , migrate
× form表单:
class RegisterForm(forms.Form):
email = forms.EmailField(required=True,max_length=20)
password = forms.CharField(required=True,min_length=5)
captcha = CaptchaField(error_messages={"invalid":u"验证码错误"})
× 视图函数:
class RegisterView(View):
def get(self,request):
register_form = RegisterForm()
return render(request,'register.html',{'register_form':register_form})
def post(self,request):
pass
× 前端页面添加: {{ register_form.captcha }} # 错误则红框标红显示 <div class="form-group marb8 {% if register_form.password.errors %}errorput{% endif %} ">
此时,页面显示验证码。对应的数据库表中生成相应的验证码的值和hash值。同时,前端页面有个hidden的text框,值为该验证码的hash值。
系统会将用户填写的验证码和hidden框中的hash值跟数据库中的值进行联合匹配是否一致。
第二步:点击注册并登录按钮,发送邮件至邮箱进行验证
× 完善上面的post方法,点击注册,先在userprofile中保存字段
def post(self,request):
register_form = RegisterForm(request.POST)
if register_form.is_valid():
username = register_form.cleaned_data['email']
password = register_form.cleaned_data['password']
if UserProfile.objects.get(email=username):
return render(request,'register.html',{'msg':"用户已存在"})
user_profile = UserProfile()
user_profile.username = username
user_profile.emial = username
user_profile.password = make_password(password)
user_profile.is_active = 0
user_profile.save()
send_register_email(username,"register") # 调用定义的邮件方法
return render(request,"login.html",{'register_form':register_form})
else:
return render(request,'register.html',{'register_form':register_form})
× 添加Model EmailVerifyRecord
注:为什么设计的该表需要code和email字段?比如用户需要密码找回,服务器如何才能安全把修改密码的html返回给用户,只能通过发送邮箱等方式让用户点击邮箱中的链接,进入到修改密码的界面(总不能直接给页面让你修改密码)。邮件格式肯定是 localhost/resetpwd/active_code带上激活码这种格式(总不能localhost/resetpwd/email 带上邮箱的名字,这样别人也能修改密码了)。所以就需要一个保存激活码和邮箱关系的表
from utils.email_send import send_register_email
class EmailVerifyRecord(models.Model):
code = models.CharField(verbose_name=u"验证码",max_length=100)
email = models.EmailField(verbose_name=u"邮箱")
send_type = models.CharField(max_length=100,verbose_name=u"验证码类型",choices=(("register","注册"),("forget","找回密码"),('update','修改邮箱')))
send_time = models.DateTimeField(default=datetime.now,verbose_name=u'发送时间')
class Meta:
verbose_name = u"邮箱验证"
verbose_name_plural = verbose_name
def __str__(self):
return "{0},{1}".format(self.email,self.send_time) # 这会在xadmin后台管理中成功添加数据后返回的导航提示
× settings配置qq邮箱为发送邮箱,
EMAIL_HOST = 'smtp.qq.com'
EMAIL_HOST_USER = '839985880@qq.com'
EMAIL_HOST_PASSWORD = 'spgqttasdasxzrlqnbegh' # 需要打开qq邮箱的POP3/SMTP服务,发送短信获取该值,可能过段时间会失效,需要重新发送短信获取
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_FROM = '839985880@qq.com'
× 创建utils的python目录并创建email.send.py
import random
from django.core.mail import send_mail
from users.models import EmailVerifyRecord
from DjangoDdu.settings import EMAIL_FROM
def random_str(randomLength=8):
"""生成随机字符串"""
chars = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789"
str = ""
for i in range(randomLength):
j = random.randint(0,len(chars)-1)
str += chars[j]
return str
def send_register_email(email,send_type="register"):
email_record = EmailVerifyRecord()
code = random_str(16)
email_record.code = code
email_record.email = email
email_record.send_type = send_type
email_record.save()
if send_type == "register":
email_title = "用户注册链接"
email_body = "点击下面链接激活账号:http://127.0.0.1:8000/active/{0}".format(code)
send_status = send_mail(email_title,email_body,EMAIL_FROM,[email]) # EMIAL_FROM:再settings中注册的邮箱。[email] 发送给哪个邮箱
if send_status:
pass
第三步 邮箱激活账户 (把User表中的is_active设置为1)
× url配置 url(r'^active/(?P<active_code>.*)/$',ActiveUserView.as_view(),name='active')
× 视图函数
class ActiveUserView(View):
def get(self,request,active_code):
register_user = EmailVerifyRecord.objects.get(code=active_code)
if register_user:
email = register_user.email # Email 表中的email
user = UserProfile.objects.get(email=email) # 根据email查找出User表中的对象
user.is_active = 1
user.save()
return render(request,'login.html')
else:
return HttpResponse('激活链接不对')
3.找回密码
找回密码的步骤跟用户注册差不多,不详细写。
× 点击提交,逻辑:将认证邮箱保存在
class ForgetPwdView(View):
def get(self,request):
forget_form = ForgetForm()
return render(request,'forgetpwd.html',{'forget_form':forget_form})
def post(self,request):
forget_form = ForgetForm(request.POST)
if forget_form.is_valid():
email = forget_form.cleaned_data['email']
user = UserProfile.objects.filter(email=email)
if user:
send_register_email(email,'forget') # 将active_code和邮箱 保在EmailVersityRecord表中,作为后续修改密码时候获取相关用户使用
return HttpResponse('验证码已发送,请注意查收')
else:
return render(request, 'forgetpwd.html', {'forget_form': forget_form,'msg':"邮箱不存在"})
else:
return render(request,'forgetpwd.html',{'forget_form':forget_form})
× 获取邮箱链接进行密码重置
class ResetView(View):
def get(self,request,active_code):
"""通过激活码获取对应的邮箱"""
record_email_query = EmailVerifyRecord.objects.filter(code=active_code)
if record_email_query:
email = record_email_query.first().email
return render(request,'password_reset.html',{'email':email})
else:
return render(request,'email_link_fail.html')
× 提交form表单,进行逻辑认证
class ModifyPwdForm(forms.Form):
password = forms.CharField(required=True,min_length=5)
password2 = forms.CharField(required=True,min_length=5)
class ModifyPwdView(View):
def post(self, request):
modify_pwd_form = ModifyPwdForm(request.POST)
email = request.POST.get('email')
if modify_pwd_form.is_valid():
password = request.POST.get('password')
password2 = request.POST.get('password2')
if password != password2:
return render(request,'password_reset.html',{'msg':'两次密码不一致,请重新输入','email':email})
email = request.POST.get("email")
user = UserProfile.objects.get(email=email)
user.password = make_password(password)
user.save()
return redirect('/login/')
else:
return render(request,'password_reset.html',{'modify_pwd_form':modify_pwd_form,'email':email})
4.404或500等页面显示
如果想让django显示自定义的http错误提示,那么需要让django进入生产环境,在settings中设置
DEBUG = False
ALLOWED_HOSTS = ["*"] # × 代表允许所有ip访问
在主程序的url中添加 (users是创建的一个app)
# 全局404页面处理
handler_404 = 'users.views.page_not_found'
在users的app的views中添加,这样就配置完成了。
def page_not_found(request):
response = render_to_response('404.html',{}) # 好像404.html只能放在templates目录的第一层下,不能新建一个目录
response.status_code = 404 # 该状态码会影响浏览器的显示
return response
但是由于设置了debug=false,会认为你是生产环境,staticfiles_dirs就会失效,django就找不到你配置的{% ‘static ’css/com.css ‘ %}的样式文件。需要在settings中设置
STATIC_ROOT = os.path.join(BASE_DIR,'static')
然后在主url文件中添加
from DjangoDdu.settings import MEDIA_ROOT,STATIC_ROOT
urlpatterns = [
# http错误页面
url(r'^static/(?P<path>.*)$',serve,{'document_root':STATIC_ROOT}),
]
http错误显示
400错误
遇到访问地址不存在就会找到404.html并显示(比如访问了 http://127.0.0.1:8000/asdasd 这样随意的一个url)。
500错误
1 如果访问了 http://127.0.0.1:8000/course/detail/2000/ 这样的url会找到500.html返回(因为你的网站中只设定了20门课,只能访问到http://127.0.0.1:8000/course/detail/20/ )
2 或者你在一个视图函数中写了 print(1/0) 也会显示500错误