1. 建一个poll2的应用,在建学科、老师模型,admin注册学科、老师管理后台
学科与老师 -- 1对多,在多的一方设置外键属性
# to表示参照那个模型
subject = models.ForeignKey(to=Subject, on_delete=models.PROTECT, db_column='sno', verbose_name='所属学科')
2. 写视图函数并且映射url
- path('', views.show_subjects, name='index'), -- name是别名,可以通过index解析出url
- path('teachers/', views.show_teachers),
3. 进行后端渲染,render
4. 静态资源配置 -在文件夹assets中。如图片
修改配置。setting中 , BASE_DIR--根路径
访问/static/images/图片名 就可以访问到图片
STATICFILES_DIRS = [os.path.join(BASE_DIR, "assets"), ]
STATIC_URL = '/static/'
前端用取图片。
5.查询参数/URL参数 - query parameter
url后面跟?+参数名1=参数值1&参数名2=参数值2
{{ subject.name }}
- 点a标签会发生get请求,
- veiw通过映射url--path('teachers/', views.show_teachers),
- 在views的show_teachers函数中,用上面a标签url参数对应的参数名request.GET['sno']取到参数值sno
- 再用sno拿到对应的学科subject
- 在用学科拿到对应的学生,subject.teacher_set.all()
这里不加all(),会提示'RelatedManage' object is not iterable
,这是Django在1的一方的隐藏属性,用于用1的一方(学科)拿到对应的多的(老师)值
def show_teachers(request):
"""显示指定学科的老师"""
try:
sno = int(request.GET['sno']) # 'sno'是subjects.html中的请求参数名
subject = Subject.objects.get(no=sno)
teachers = subject.teacher_set.all()
return render(request,
template_name='teachers.html',
context={'subject': subject, 'teachers': teachers})
except (KeyError, ValueError, Subject.DoesNotExist):
return redirect('index')
6.在写展示老师的模板页面 - teachers.html
要展示老师头像,要改老师模型,加photo属性,把图片路径/images/luohao.jpg赋给改数据库中的该属性。数据库直接存图片(二进制类型)太大。所有用路径好。
在前端模板中,按下面取静态图片。
png - 颜色单调,有损压缩
jpg - 颜色丰富
{% load static %} # 写在页面的最上面,要加载静态资源就这么写
7.在老师界面实现点好评和差评
- 映射url
- 写view函数,与上面一样,也是通过用上面a标签url参数对应的参数名request.GET['tno']取到参数值tno
这里不用序列化,是因为没有返回对象给json数据,对照02末尾来看
def praise_or_criticize(request):
"""点赞或差评"""
code, hint = 10001, '投票成功'
try:
tno = int(request.GET['tno'])
teacher = Teacher.objects.filter(no=tno).first()
if teacher:
if request.path.startswith('/praise/'):
teacher.good_count += 1
else:
teacher.bad_count += 1
teacher.save()
code, hint = 10001, '投票操作成功'
except (KeyError, ValueError):
pass
return JsonResponse({'code': code, 'hint': hint})
- 然后返回json数据用js代码进行异步请求,局部刷新
8.用户注册
- 写view,POST请求就,注册,GET就到登录界面
def register(request):
"""用户注册"""
hint = ''
if request.method == 'POST':
form = RegisterForm(request.POST)
# 验证表单填的是否正确.POST取到浏览器的请求。是字典,与表单对象一一检查
if form.is_valid():
# is_valid()验证后才有clean_data
code_from_session = request.session.get('mobile_code')
code_from_user = form.cleaned_data['code']
if code_from_session == code_from_user:
form.save()
hint = '注册成功,请登录'
return render(request, 'login.html', {'hint': hint})
else:
hint = '请输入正确的手机验证码'
else:
print(form.errors)
hint = '请输入有效的注册信息'
return render(request, 'register.html', {'hint': hint})
- 映射url
- 写前端模板register.html
- 在写用户模型,迁移成表
8.1 Django框架对跨站请求伪造的处理
csrf
防止其他的网站套取(淘宝)用户的身份信息去登录淘宝等。
在表单中加 --- 这回加一个隐藏的表单
8.2自定义表单实现用户注册表单验证
看poll2应用下面的forms文件
在再对应的view函数中写上面的代码
f1 = RegisterForm()
f1.is_valid() --- 验证注册对象f1是否有效,返回False和True
f1.errors -- 返回错误的内容,是一个字典
9.用户登录表单验证
写view函数 --- 也用在forms里面写验证表单 LoginForm
def login(request):
"""用户登录"""
hint = ''
# 吧backurl埋到登录界面的表单,get方法有backurl就回到刚才的界面,没有就回到index首页
backurl = request.GET.get('backurl', 'index')
if request.method == 'POST':
backurl = request.POST['backurl']
form = LoginForm(request.POST)
if form.is_valid():
code_from_session = request.session.get('captcha_code')
code_from_user = form.cleaned_data['code']
if code_from_session.lower() == code_from_user.lower():
# 这里的['username']与表单的input控件的name属性名对应
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = User.objects.filter(
username=username, password=password).first()
if user:
request.session['userid'] = user.no
request.session['username'] = user.username
return redirect(backurl)
else:
hint = '用户名或密码错误'
else:
hint = '请输入正确的验证码'
else:
hint = '请输入有效的登录信息'
return render(request, 'login.html', {'hint': hint, 'backurl': backurl})
映射url
写login.html前端模板
10.网站用户跟踪和session对象
- 怎么让浏览器记住用户
客户端
- URL重写(在输入地址时加上用户的身份标识)
http://baidu.com/?userid=用户名 - 隐藏域(隐式表单域) - 埋点
- 本地存储 - Cookie/ localStorage / sessionStorage / IndexDB
userid <--> jackfrued
====> 服务器 session对象(每个用户一个)
- 登录成功在subjects页面显示用户名
先改view的登录函数
if user:
request.session['userid'] = user.no
request.session['username'] = user.username
return redirect(backurl)
在写subjects模板
- 用户注销
def logout(request):
"""用户注销"""
request.session.flush()
return redirect('index')
映射url
11.实现图片验证码
- 封装captcha.py
def get_captcha(request):
"""生成图片验证码"""
code = generate_captcha_code()
request.session['captcha_code'] = code
image_data = Captcha.instance().generate(code, fmt='PNG')
return HttpResponse(image_data, content_type='image/png')
- 映射url
- 在login模板中用jQuery实现生成验证码可以换,用ajax实现更换
12.实现手机短信验证码
- 短信网管:云片短信 / SendCloud / luosimao
也是view函数,url,前面模板
判断用户登录:
因为有很多函数要判断登录,所有用中间件来实现,减少代码重复。
LOGIN_REQUIRED_URLS = {
'/praise/',
'/criticize/',
'/pdf/',
'/excel/'
}
def check_login_middleware(get_resp):
@wraps(get_resp)
def wrapper(request, *args, **kwargs):
if request.path in LOGIN_REQUIRED_URLS:
if 'userid' in request.session:
return get_resp(request, *args, **kwargs)
else:
if request.is_ajax():
return JsonResponse({'code': 10003, 'hint': '请先登录'})
else:
backurl = request.get_full_path()
return redirect(f'/login/?backurl={backurl}')
return get_resp(request, *args, **kwargs)
return wrapper
13.导出ecl报表和下载pdf
写views,映射url,用ECharts显示柱状图
14.ORM框架下的分组和聚合查询
按学科subject分组在去取好评good_count和差评bad_count的平均值
def get_subjects_data(request):
# 分组+聚合
queryset = Teacher.objects.values('subject').annotate(
good=Avg('good_count'), bad=Avg('bad_count')
)
names = [result['subject'] for result in queryset]
good = [result['good'] for result in queryset]
bad = [result['bad'] for result in queryset]
return JsonResponse({'names': names, 'good': good, 'bad': bad})