Django基础知识点二
【一】分页器
【1】关键参数
- current_page :当前所在页数
- start_data : 起始数据
- end_data :结束数据
- per_count :每一页展示的数据
- all_count :所有数据数量
- page_count :总页数 :divmod(all_count,per_count)
current_page (per_count =5 ) |
start_data |
end_data |
1 |
0 |
5 |
2 |
5 |
10 |
3 |
10 |
15 |
| start_data = current_page * per_count |
| end_data = current_page * per_count |
【2】代码实现
| |
| current_page = request.GET.get('page', 1) |
| |
| '''如果page小于1,意味着出现了负数,这是不可能的,所以当出现复数时,设置当前页为1''' |
| if current_page < 1: |
| current_page = 1 |
| '''如果当前页大于总页数,说明定向到了不存在的页数,所以设置当前页为总页数''' |
| if current_page > all_pager: |
| current_page = all_pager |
- 可以为页码添加动态调整,总共展示指定的页码
- 如:总共展示5个页码跳转
- current =1 :1 2 3 4 5
- current = 2 :2 3 4 5 6
| |
| '''当总页码小于展示页码时,就是从1开始到结束''' |
| if all_pager <= pager_count: |
| pager_start = 1 |
| pager_end = all_pager + 1 |
| else: |
| |
| '''当总页码大于展示页码时,意味着需要动态展示页数''' |
| |
| if current_page <= pager_count /2 : |
| pager_start = 1 |
| pager_end = pager_count + 1 |
| |
| |
| |
| else: |
| |
| if (current_page + pager_count_half) > all_pager: |
| |
| pager_end = all_pager + 1 |
| |
| |
| pager_start = all_pager - pager_count + 1 |
| else: |
| pager_start = current_page - pager_count_half |
| pager_end = current_page + pager_count_half + 1 |
- 另外需要注意,在前端是没有for .. range语法的
- 所以需要在后端将html标签作为字符串传给前端
| tmp_list = [] |
| for i in range(start,end): |
| tmp = '<li><a href="?page=%s">%s</a></li>' % (i, i,) |
| tmp_list.append(tmp) |
| html = ''.join(tmp_list) |
【3】完善代码封装
| |
| class Pagination(object): |
| def __init__(self, current_page, all_count, per_page_num=5, pager_count=5): |
| """ |
| 封装分页相关数据 |
| :param current_page: 当前页 |
| :param all_count: 数据库中的数据总条数 |
| :param per_page_num: 每页显示的数据条数 |
| :param pager_count: 最多显示的页码个数 |
| """ |
| try: |
| current_page = int(current_page) |
| except Exception as e: |
| current_page = 1 |
| |
| if current_page < 1: |
| current_page = 1 |
| |
| self.all_count = all_count |
| self.per_page_num = per_page_num |
| |
| |
| all_pager, tmp = divmod(all_count, per_page_num) |
| if tmp: |
| all_pager += 1 |
| self.all_pager = all_pager |
| if current_page > all_pager: |
| current_page = all_pager |
| self.current_page = current_page |
| self.pager_count = pager_count |
| self.pager_count_half = int((pager_count - 1) / 2) |
| |
| @property |
| def start(self): |
| return (self.current_page - 1) * self.per_page_num |
| |
| @property |
| def end(self): |
| return self.current_page * self.per_page_num |
| |
| def page_html(self): |
| |
| if self.all_pager <= self.pager_count: |
| pager_start = 1 |
| pager_end = self.all_pager + 1 |
| |
| else: |
| |
| if self.current_page <= self.pager_count_half: |
| pager_start = 1 |
| pager_end = self.pager_count + 1 |
| |
| |
| else: |
| |
| if (self.current_page + self.pager_count_half) > self.all_pager: |
| pager_end = self.all_pager + 1 |
| pager_start = self.all_pager - self.pager_count + 1 |
| else: |
| pager_start = self.current_page - self.pager_count_half |
| pager_end = self.current_page + self.pager_count_half + 1 |
| |
| page_html_list = [] |
| |
| page_html_list.append(''' |
| <nav aria-label='Page navigation>' |
| <ul class='pagination'> |
| ''') |
| first_page = '<li><a href="?page=%s">首页</a></li>' % (1) |
| page_html_list.append(first_page) |
| |
| if self.current_page <= 1: |
| prev_page = '<li class="disabled"><a href="#">上一页</a></li>' |
| else: |
| prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,) |
| |
| page_html_list.append(prev_page) |
| |
| for i in range(pager_start, pager_end): |
| if i == self.current_page: |
| temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,) |
| else: |
| temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,) |
| page_html_list.append(temp) |
| |
| if self.current_page >= self.all_pager: |
| next_page = '<li class="disabled"><a href="#">下一页</a></li>' |
| else: |
| next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,) |
| page_html_list.append(next_page) |
| |
| last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,) |
| page_html_list.append(last_page) |
| |
| page_html_list.append(''' |
| </nav> |
| </ul> |
| ''') |
| return ''.join(page_html_list) |
| |
| |
| def check_info(request): |
| |
| current_page = request.GET.get('page', 1) |
| |
| publish_obj_all = Publish.objects.all() |
| |
| all_count = publish_obj_all.count() |
| |
| page_obj = Pagination(current_page=current_page, all_count=all_count, per_page_num=5, pager_count=5) |
| |
| |
| publish_obj = publish_obj_all[page_obj.start:page_obj.end] |
| |
| page_html = page_obj.page_html() |
| return render(request, 'author_publish.html', locals()) |
| |
【1】创建基本语法
| from django import forms |
| class Forms组件名(forms): |
| 字段 = forms.字段类型(label='显示在input框前的提示,不填默认是字段名', |
| initial = '默认值', |
| [字段约束条件], |
| error_message={ |
| '约束条件':'希望提示的报错信息' |
| }, |
| widget = '前端的格式,可以设置input框类型以及属性' |
| ) |
| class RegisterForm(forms.Form): |
| username = forms.CharField(label='用户名', min_length=5, max_length=12, |
| widget=forms.TextInput(attrs={ |
| "class": "form-control", |
| "placeholder": "请输入用户名", |
| }), |
| error_messages={ |
| 'min_length': '用户名最少5位', |
| 'max_length': '用户名最多12位' |
| }) |
| password = forms.CharField(label='密码', min_length=5, max_length=24, |
| initial='a123456', |
| widget=forms.PasswordInput(attrs={ |
| "class": "form-control", |
| "placeholder": "请输入密码", |
| }), |
| error_messages={ |
| 'required': '密码不能为空', |
| 'min_length': '密码最少5位', |
| 'max_length': '密码最多24位' |
| }) |
| email = forms.EmailField(label='邮箱', |
| widget=forms.TextInput(attrs={ |
| "class": "form-control", |
| "placeholder": "请输入邮箱", |
| }), |
| error_messages={ |
| 'invalid': '邮箱格式错误' |
| }) |
【2】后端使用基本语法
| from forms import Forms组件名 |
| |
| def func(request): |
| form_obj = Forms组件名 |
| |
| if request.method == 'POST': |
| |
| data = requset.POST |
| |
| form_data = Forms组件名(data) |
| |
| isClean = form_data.is_vaild() |
| |
| |
| clean = form_data.cleaned_data |
| error = form_data.errors |
【3】前端使用基本用法
| '''通过print【forms组件对象】可以看到是生成对应的字段标签''' |
| print LoginForm() |
| ''' |
| <tr><th> |
| <label for="id_username">用户名:</label> |
| </th><td><input type="text" name="username" class="form-control" placeholder="请输入用户名" maxlength="12" minlength="5" required id="id_username"> |
| </td></tr> |
| |
| <tr><th><label for="id_password">密码:</label></th><td><input type="password" name="password" class="form-control" placeholder="请输入密码" maxlength="24" minlength="5" required id="id_password"></td></tr> |
| |
| <tr><th><label for="id_confirm_pwd">确认密码:</label></th><td><input type="password" name="confirm_pwd" class="form-control" placeholder="请再次输入密码" maxlength="24" minlength="5" required id="id_confirm_pwd"></td></tr> |
| |
| <tr><th><label for="id_email">邮箱:</label></th><td><input type="text" name="email" class="form-control" placeholder="请输入邮箱" required id="id_email"></td></tr> |
| '''''' |
| |
| <form action="" class="form-horizontal" id="register_form"> |
| |
| {% csrf_token %} |
| |
| {% for form in register_form %} |
| <div class="form-group"> |
| |
| <div class="col-sm-3 text-right">{{ form.label }}</div> |
| |
| <div class="col-sm-8">{{ form }} |
| |
| <span id="{{ form.auto_id }}" class="pull-right" style="color: red"></span> |
| </div> |
| </div> |
| {% endfor %} |
| </form> |
- 其实可以直接使用
{{ forms }}
渲染所有的input标签和label标签,因为通过print可以看到是生成了一个完整的label+input,但就做不到定制化了
【4】钩子函数(hook)
- 【注】钩子函数相当于把数据从“管道”中取出,切记注意在处理完毕后,将数据返回
- 局部钩子与全局钩子的区别就在于函数名,在pycharm中会有提示
- 局部钩子与全局钩子的返回对象也是不用的
- 局部钩子返回取出的那个数据即可
- 全局钩子需要返回所有的数据,哪怕只用到了其中一部分的数据
【4.1】局部钩子clean_field
| class Forms(forms.Form): |
| username = forms.Charfield('xxx') |
| password = forms.Charfield('xxx') |
| |
| def clean_username(self): |
| |
| username = self.cleaned_data.get('username') |
| |
| is_exists = UserInfo.objects.filter(username=username).exists() |
| if is_exists: |
| self.add_error(field='username', error='用户已存在,不可以重复注册') |
| |
| return username |
【4.2】全局钩子
| class Forms(forms.Form): |
| username = forms.Charfield('xxx') |
| password = forms.Charfield('xxx') |
| confirm_pwd = forms.Charfield('xxx') |
| |
| |
| def clean(self): |
| |
| pwd = self.cleaned_data.get('password') |
| confirm_pwd = self.cleaned_data.get('confirm_pwd') |
| if pwd != confirm_pwd: |
| self.add_error(field='confirm_pwd', error='密码不一致') |
| |
| return self.cleaned_data |
【三】cookies
【1】cookie
- cookie是浏览器保存在计算机本地的一种文件,用于跟踪用户的会话信息、存储用户偏好设置以及其他与用户相关的信息

【2】Django操作cookie
【2.1】设置cookie
| |
| |
| def func(request): |
| obj = HttpResponse('ok') |
| |
| obj.set_cookie('key','value',max_age=5) |
| return obj |

- 如果不设置
max_age
,则 cookie 默认会在用户关闭浏览器时过期,这种类型的 cookie 称为会话 cookie

【2.2】获取cookie
| |
| cookie = request.COOKIES |
| |
| cookie = request.COOKIES.get('key') |
| |
| cookie = request.COOKIES.get('key','error') |
【四】sessions
【1】session
- 会话(session)是指在客户端和服务器之间建立的一种持续的连接。
- 在 Web 开发中,会话常用于跟踪用户的状态和存储用户特定的信息,例如登录状态、购物车内容等。
- 在 Django 中,会话由服务器端维护,并通过在客户端浏览器中存储会话 ID 来实现跟踪用户状态的目的。
【2】Django操作session

【2.1】设置session
| ''' |
| 设置session后 |
| 会先将该session加密后保存到数据库中 |
| 并将sessionid发送给浏览器客户端 |
| ''' |
| request.session['key'] = 'value' |
| |
| |
| request.session.set_expiry(value) |
| |
| |
| |
| |
| |

【2.2】删除session


【补】当session同一个key有多个值时
- 当在同一次请求中设置了两次不一样的key,在数据中保存的数据只会存在一条
- 通过
request.session·
可以查看到两条数据都是存在的
| request.session['key1'] = 'value1' |
| request.session['key2'] = 'value2' |

【五】中间件
【1】中间件
- 中间件(middleware)是 Django 框架中的一个关键概念
- 它是一种用于在请求到达视图之前或响应返回客户端之前执行的代码。
- 中间件可以对请求进行预处理、对响应进行后处理,也可以对请求和响应进行修改。
- Django 中的中间件以类的形式实现,每个中间件类包含了一系列的方法
| from django.utils.deprecation import MiddlewareMixin |
| |
| |
| |
| class MyMiddle1(MiddlewareMixin): |
| def process_request(self, request): |
| '''在这里可以对请求进行预处理''' |
| print('这是MyMiddle1中的request') |
| |
| def process_response(self, request, response): |
| '''在这里可以对响应进行后处理''' |
| print('这是MyMiddle1中的response') |
| |
| return response |
| |
| |
| |
| MIDDLEWARE = [ |
| ... |
| |
| 'app004.MyMiddleWare.MyMiddle1', |
| ] |

【2】中间件的执行顺序
- 在处理请求时,中间件从上至下依次执行
- 在处理响应时,中间件从下至上依次执行
【六】csrf跨站请求
【1】钓鱼网站
| <body> |
| <h1>正规网站</h1> |
| <form action="" method="post"> |
| <p>用户: <input type="text" name="user"></p> |
| <p>收款方: <input type="text" name="payee"></p> |
| <input type="submit"> |
| </form> |
| <a href="http://127.0.0.1:8001/app004/transfer">钓鱼链接</a> |
| </body> |
| <body> |
| <h1>钓鱼网站</h1> |
| |
| <form action="http://127.0.0.1:8000/app004/transfer/" method="post"> |
| <p>用户: <input type="text"></p> |
| <p><input type="text" name="user" value="Someone" hidden></p> |
| <p>收款方: <input type="text" name="payee"></p> |
| <input type="submit"> |
| </form> |
| </body> |


【2】Django添加csrf验证
【2.1】CSRF 防护机制
- CSRF 防护机制的工作原理是在用户登录时生成一个 CSRF 标记,并将该标记存储在用户的会话中(或者作为 cookie 发送给用户)。
- 每当用户提交表单或者发送 POST 请求时,Django 会检查请求中是否包含 CSRF 标记,如果标记不匹配或者缺失,Django 将拒绝请求。
【2.2】CSRF防护中间件
- Django中默认为我们添加了CSRF防护,这就是为什么我们之前如果不将csrf中间件注释掉,我们使用post请求提交数据时会
403 forbidden
| |
| MIDDLEWARE = [ |
| 'django.middleware.csrf.CsrfViewMiddleware', |
| ] |
【2.3】添加CSRF验证


【2.3.2】在Ajax中通过jQuery语法取值
- 在上面我们看到,其实
{% csrf_token %}
其实就是自动生成了一个input标签,name是csrfmiddlewaretoken
,value值就是生成的字符串
- 当我们通过Ajax提交post请求时,就可以通过jQuery语法取值并传给后端
| <body> |
| <h1>正规网站</h1> |
| <form action=""> |
| {% csrf_token %} |
| <p>用户: <input type="text" name="user"></p> |
| <p>收款方: <input type="text" name="payee"></p> |
| <button id="btn1">button</button> |
| </form> |
| <script> |
| $("#btn1").click(function () { |
| let user = $('[name="user"]').val(); |
| let payee = $('[name="payee"]').val(); |
| let token = $('[name="csrfmiddlewaretoken"]').val(); |
| $.ajax({ |
| url: '', |
| type: 'post', |
| data: { |
| 'user': user, |
| 'payee': payee, |
| {# 将csrf的token值传递给后端 #} |
| 'csrfmiddlewaretoken': token |
| }, |
| dataType: 'json', |
| success:function (result) { |
| alert(result.res) |
| } |
| }) |
| }) |
| </script> |
| </body> |
| |
| def transfer(request): |
| if request.method == 'POST': |
| print(request.POST) |
| print('这里是正规的转账业务,扣钱等操作将在此执行') |
| user = request.POST.get('user') |
| payee = request.POST.get('payee') |
| from django.http import JsonResponse |
| return JsonResponse({'res': f'这里是正规的转账业务,{user}转账给{payee}成功'}) |
| return render(request, 'transfer.html') |
【2.3.3】在Ajax中通过模板语法传值

【七】auth模块
【1】创建超级管理员
| # 创建超级管理员 |
| python manage.py createsuperuser |
| # 密码输入时会不显示,这是为了安全考虑,正常输入敲回车即可 |
| # 当密码太简单时,会提示让你输入【y/n】确认 |
- 创建完以后,就可以访问
/admin/
进入django后台管理

【2】注册用户或超级管理员
- 【注】普通的将数据插入数据库中的数据是明文的,在登录后台是无法使用的,所以需要特殊方法
- 错:
库名.objects.create(**kwargs)
只能插入指定的数据,对数据不会处理
- 对:
库名.objects.create_user(**kwargs)
:其实是调用了auth模块的方法,对密码进行了加密处理
| UserInfo.objects.create_user(username='xxx',password='xxx') |
| UserInfo.objects.create_superuser(username='xxx',password='xxx') |
【3】使用auth模块比对登录信息
| |
| |
| from django.contrib import auth |
| |
| |
| user_obj = auth.authenticate(request,username,password) |
| |
| <body> |
| <form action="" method="post"> |
| {% csrf_token %} |
| username: <input type="text" name="username"> |
| password: <input type="text" name="password"> |
| <input type="submit"> |
| </form> |
| </body> |
| |
| from django.contrib import auth |
| |
| def login(request): |
| if request.method == 'POST': |
| data = request.POST |
| username = data.get('username') |
| pwd = data.get('password') |
| user_obj = auth.authenticate(request, username=username, password=pwd) |
| print(user_obj) |
| if not user_obj: |
| return HttpResponse('不ok') |
| print(user_obj.username) |
| |
| print(user_obj.password) |
| return HttpResponse('ok') |
| return render(request, 'AuthLogin.html') |
【4】保存用户状态
| from django.contrib import auth |
| |
| |
| auth.login(request,user_obj) |
| '''类似于''' |
| request.session[key]=user_obj |

【补充方法】request.user
/request.user.is_authenticated
| '''request.user :返回当前登录用户''' |
| |
| |
| |
| '''request.user.is_authenticated:返回是否是登录用户,如果是匿名用户返回False''' |
【5】auth模块中的登录装饰器
| |
| from django.contrib.auth.decorators import login_required |
| |
| @login_required(login_url='指定页面') |
| def func(request): |
| return xxx |
- 如果不想每次都指定
login_url
,可以在配置文件中设置参数
【6】修改用户密码
- 修改用户密码的前提是已经登录过
- 通过
request.user.set_password(new_password)
request.user.check_password('输入的密码')
:与数据库中密码进行比对,返回布尔值
| @login_required |
| def set_password(request): |
| if request.method == 'POST': |
| old_password = request.POST.get('old_password') |
| new_password = request.POST.get('new_password') |
| confirm_password = request.POST.get('confirm_password') |
| |
| if new_password == confirm_password: |
| |
| is_right = request.user.check_password(old_password) |
| |
| if is_right: |
| |
| request.user.set_password(confirm_password) |
| |
| request.user.save() |
| |
| return redirect('/login/') |
| |
| return render(request, 'set_password.html', locals()) |
【7】注销登录logout
| @login_required |
| def logout(request): |
| |
| |
| auth.logout(request) |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库