django-03-老师学生学科

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.在老师界面实现点好评和差评

 

好评 {{ teacher.good_count }}     差评 {{ teacher.bad_count }}

  • 映射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
防止其他的网站套取(淘宝)用户的身份信息去登录淘宝等。
在表单中加 --- 这回加一个隐藏的表单

{% csrf_token %}

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对象

  • 怎么让浏览器记住用户
    客户端
  1. URL重写(在输入地址时加上用户的身份标识)
    http://baidu.com/?userid=用户名
  2. 隐藏域(隐式表单域) - 埋点
  3. 本地存储 - 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模板

{% if request.session.username %} {{ request.session.username}} 注销 {% else %} 用户登录  {% endif %}  快速注册
  • 用户注销

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})

你可能感兴趣的:(django-03-老师学生学科)