Django高级扩展
静态文件
- css、js、图片、Json文件、字体文件等 配置settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR,'static')
]
{% load static from staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
<link rel="stylesheet" type="text/css" href="{% static 'myApp/css/style.css' %}"/>
<script type="text/javascript" src="/static/myApp/js/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="/static/myApp/js/sunck.js"></script>
</head>
<body>
<h1>sunck is a good man</h1>
<img src="/static/myApp/img/2.png"/>
<img src="{% static 'myApp/img/2.png' %}"/>
</body>
</html>
中间件
-概述 - 一个轻量级、底层的插件,可以介入Django的请求和响应
- 本质
- 一个Python类
- 方法
- init
- 不需要传参数,服务器响应第一个请求的时候自动调用,用于确定是否启用该中间件
- process_request(self,request)
- 在执行视图之前被调用(分配url匹配视图之前),每个请求上都会调用,返回None或者HttpResponse对象
- process_view(self,request,view_func,view_args,view_kwargs)
- 调用视图之前执行,每个请求都会调用,返回None或者HttpResponse对象
- process_template_response(self,request,response)
- 在视图刚好执行完后调用,每个请求都会调用,返回None或者HttpResponse对象 使用render
- process_response(self,request,response)
- 所有响应返回浏览器之前调用,每个请求都会调用,返回HttpResponse对象
- process_exception(self,request,exception)
- 当视图跑出异常时调用,返回HttpResponse对象 执行位置
- init
自定义中间件
- 工程目录下middleware目录下创建myApp目录
创建一个python文件
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils.deprecation import MiddlewareMixin
from backweb.models import User
class AuthMiddleWare(MiddlewareMixin):
#重写process_request方法
def process_request(self,request):
#获得页面
path = request.path
#放过登录页面
if path == '/backweb/my_login/':
return None
#获得页面cookie中的session_id
#(页面cookie中的session_id是登录时产生的随机数,将其付给cookie同时在数据库中存储该值。
#用户访问页面时间就拿cookie中的session_id 与数据库做对比,有就说明用户是登录状态,用户退出时将数据库清空)
session_id = request.COOKIES.get('session_id')
#cookie中没有session说明没有登录
if not session_id:
return HttpResponseRedirect(reverse('backweb:my_login'))
#获的cookie中有session_id同时获取其数据库模型对象,没有对象说明这个人不是网站的注册用户重新返回登录页面
user = User.objects.filter(session_id=session_id)
#
if not user:
return HttpResponseRedirect(reverse('backweb:my_login'))
return None
- 使用自定义中间件
- 配置settings.py文件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
#添加自定义中间件
'utils.UserAuthMiddleware.AuthMiddleWare',
]
app/views.py
def my_login(request, rando=None):
if request.method == 'GET':
return render(request, 'backweb/login.html')
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
#验证用户信息
user = User.objects.filter(username=username,password=password).first()
if user:
#确定跳转重定向
res = HttpResponseRedirect(reverse('backweb:index'))
#写随机数字符串生成cookie
s = 'qazwsxedcrfvtgbyhnujmikolpQZWXEDCRFVTGBYHNUJMIKOLP1234567890'
#给cookie定名字
session_id = ''
#循环写cookie
for i in range(20):
session_id += random.choice(s)
#创建cookie过期时限默认(一天)
out_time = datetime.now() + timedelta(days=1)
#为用户(服务器客户端添加)添加cookie,用来判断用户是否登录。没有登录的用户没有cookie值无法直接进网站
res.set_cookie('session_id',session_id, expires=out_time)
#为服务器添加进数据库
user.session_id = session_id
user.out_time = out_time
user.save()
#执行跳转
return res
#找不到对应数据库数据用户提供信息错误
else:
error = '用户名或密码错误'
return render(request, 'backweb/login.html')
def my_logout(request):
if request.method == 'GET':
session_id = request.COOKIES.get('session_id')
user = request.user
user.session_id = ''
user.save()
res = HttpResponseRedirect(reverse("backweb:my_login"))
res.delete_cookie('session_id')
return res
login.html
<body>
<dl class="admin_login">
<dt>
<strong>站点后台管理登录</strong>
<em>Management System</em>
</dt>
<form action="" method="post">
{% csrf_token %}
<dd >
<input type="text" name="username" placeholder="账号" />
</dd>
<dd>
<input type="password" name="password" placeholder="密码"/>
</dd>
<dd>
<input type="submit" value="立即登录" class="submit_btn"/>
</dd>
</form>
</dl>
</body>
上传图片
- 概述
- 文件上传时,文件数据存储在request.FILES属性中
- 注意:form表单要上传文件需要加enctype="multipart/form-data"
- 注意:上传文件必须是post请求
- 需要安装pip install pillwo
网页
<body>
<form method="post" action="/savefile/" enctype="multipart/form-data">
{%csrf_token%}
<input type="file" name="filename"/>
<input type="submit" value="上传"/>
</form>
项目/setting.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
app/models.py
class Article(models.Model):
image = models.ImageField(upload_to='',null=True)
项目/urls.py
from django.contrib.staticfiles.urls import static
from blogger import settings
urlpatterns = [
url(r'^admin/', admin.site.urls),
]
# 添加media为静态文件像static一样可以{% load xxx %}调用
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
app/views.py
def add(request):
if request.method == 'GET':
return render(request, 'backweb/add.html')
if request.method == 'POST':
filename = request.FILES['filename']
Article.objects.create( image=filename, )
return HttpResponseRedirect(reverse('backweb:index'))
展示.html
{# 图片 #}
<td ><img src="/media/{{set.image}}" width="50" height="50"/></td>
分页
-
Paginator对象
-
创建对象
- 格式Paginator(列表, 整数)
- 返回值返回的分页对象
- 属性
- count对象总数
- num_pages页面总数 -page_range页码列表[1,2,3,4,5]页码从1开始
-
方法 -page(num)获得一个Page对象,如果提供的页码不存在会抛出"InvalidPage"异常
-InvalidPage当向page()传递的是一个无效的页码时抛出 -PageNotAnInteger当向page()传递的不是一个整数时抛出 -EmptyPage当向page()传递一个有效值,但是该页面时没有数据时抛出
-
Page对象
- 创建对象Paginator对象的page()方法返回得到Page对象不需要手动创建
- 属性
- object_list当前页上所有的数据(对象)列表
- number当前页的页码值
- paginator当前page对象关联的paginator对象
- 方法
- has_next()判断是否有下一页,如果有返回True
- has_previous()判断是否有上一页,如果有返回True
- has_other_pages()判断是否有上一页或下一页,如果有返回True
- next_page_number()返回下一页的页码,如果下一页不存在抛出InvalidPage异常
- previous_page_number()返回上一页的页码,如果上一页不存在抛出InvalidPage异常
- len()返回当前页的数据(对象)个数 Paginator对象与Page对象的关系
-
def pagetitle(request, pageid):
if request.method == 'GET':
#拿到所欲数据
sets = Article.objects.all()
#确定每个页面展示2条数据
paginator = Paginator(sets,1)
#拿到对应页面的id
page = paginator.page(pageid)
return render(request, 'backweb/pagetitle.html', {'sets':page})
pagetitle.html
{# 分页 #}
<aside class="paging">
<a href="/backweb/pagetitle/1">第一页</a>
{% if sets.has_previous %}
<a href="/backweb/pagetitle/{{ sets.previous_page_number }}">上一页</a>
{% endif %}
{# 上一页码页码 #}
{% if sets.has_previous %}
<a href="/backweb/pagetitle/{{ sets.previous_page_number }}">{{ sets.previous_page_number }}</a>
{% endif %}
{# 本页页码(在本页面,那么该页面的页码就不能点击) #}
<a style="color:black;" href="#">{{ sets.number }}</a>
{# 下一页页码 #}
{% if sets.has_next %}
<a href="/backweb/pagetitle/{{ sets.next_page_number }}">{{ sets.next_page_number }}</a>
{% endif %}
{# 下一页 #}
{% if sets.has_next %}
<a href="/backweb/pagetitle/{{ sets.next_page_number }}">下一页</a>
{% endif %}
{# 最后一页 #}
<a href="/backweb/pagetitle/{{ sets.paginator.num_pages }}">最后一页</a>
- 使用接口前后分离
- html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>add</title>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
function add_students() {
var name = $('#name').val()
var age = $('#age').val()
var sex = $('#sex').val()
var csrf = $('input[name="csrfmiddlewaretoken"]').val()
$.ajax({
url:'/app/student/',
type:'POST',
dataType:'json',
data:{'s_name':name,'s_age':age,'s_sex':sex},
headers:{'X-CSRFToken':csrf},
success:function (data) {
alert('成功')
},
error: function (data) {
alert('失败')
}
})
}
</script>
</head>
<body>
<span>姓名</span><input id="name" type="text" name="s_name">
<span>年龄</span><input id="age" type="text" name="s_age">
<select name="s_sex" id="sex">
<option value="0">男</option>
<option value="1">女</option>
</select>
</body>
<br/>
<input type="button" value="提交" onclick="add_students()">
</body>
</html>
-views
from django.shortcuts import render
from django.http import HttpResponse
from rest_framework import mixins, viewsets
from rest_framework.response import Response
from app.app_filter import StudentFilter
from app.app_serializers import StudentSerializer
from app.models import Student
class StudentSoure(mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
#查询资源的所有数据
queryset = Student.objects.all().filter(is_delete=1)
#序列化
serializer_class = StudentSerializer
#过滤
# filter_class = StudentFilter
def get_queryset(self):
queryset = self.queryset
queryset.filter(s_name__contains='小')
return queryset
def get_object(self):
pass
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
if instance.is_delete:
serializer = self.get_serializer(instance)
else:
serializer = self.get_serializer()
return Response(serializer.data)
def retrieve(self, request, *args, **kwargs):
try:
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
except:
data = {
'code': 1001,
'msg': '数据不存在'
}
return Response(data)
-app/urls
from django.conf.urls import url
from rest_framework.routers import SimpleRouter
from app import views
router = SimpleRouter()
router.register('student',views.StudentSoure)
urlpatterns = [
]
urlpatterns += router.urls
- app/app_filter
import django_filters
from rest_framework import filters
from app.models import Student
class StudentFilter(filters.FilterSet):
# 过滤s_name参数,精确过滤
s_name = django_filters.CharFilter('s_name', lookup_expr='contains')
s_age_gt = django_filters.NumberFilter('s_age', lookup_expr='gt')
s_age_lt = django_filters.NumericRangeFilter('s_age', lookup_expr='lt')
class Meta:
model = Student
fields = ['s_name', 's_age', 's_age', ]
- app/app_serializers
from rest_framework import serializers
from app.models import Student
class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = ['id', 's_name', 's_age', 's_sex']
def to_representation(self, instance):
#拿到系列化结果
data = super().to_representation(instance)
#添加参数
data['address'] = 'hgj '
return instance