Django学习总结(Week01-结)
关于request.Files.get(‘head‘)取不到值的问题,需要在前端把表单的enctype设置为multipart/form-data
这样用户头像的修改和学生头像上传问题一起解决了。
<form action="{% url ‘app:students‘ %}" method="post" enctype="multipart/form-data">
值得一提的是在保存图片时,models中的
s_head = models.ImageField(upload_to=‘Image/student‘, default=‘Image/default.jpg‘) # 学生照片
是以settings中设置的MEDIA_ROOT作为根路径的,所以在使用时记得在settings中注册:
STATICFILES_DIRS = [ os.path.join(BASE_DIR, ‘static‘) ] MEDIA_ROOT = os.path.join(BASE_DIR, ‘static/media‘)
学生管理页面因为缓存问题在更新时不能第一时间把最新数据呈现出来,那只要在更新时删除缓存就行了。
首先在接收到get请求时判断数据库中是否有该页面的缓存,有则直接返回该缓存。
result = cache.get(‘students‘) if result: return HttpResponse(result)
否则在返回页面前将页面数据保存到缓存数据库中。
response = render(request, ‘student.html‘, data) cache.set(‘students‘, response.content, timeout=60)
然后在更新数据时删除该页面的缓存,这个用一个函数实现:
def delCache(key): if cache.get(key): cache.delete(key)
这个方法是用django内置的数据库缓存,性能相对较低,尝试用redis实现
首先安装redis:
pip install django-redis pip install django-redis-cache
redis-server启动redis服务。(PC中要安装redis)
在settings中注册
CACHES = { ‘default‘: { ‘BACKEND‘: ‘django.core.cache.backends.db.DatabaseCache‘, ‘LOCATION‘: ‘my_cache_table‘, ‘TIMEOUT‘: 60 * 5 }, ‘redis_backend‘: { ‘BACKEND‘: ‘django_redis.cache.RedisCache‘, ‘LOCATION‘: ‘redis://127.0.0.1:6379/1‘, ‘OPTIONS‘: { ‘CLIENT_CLASS‘: ‘django_redis.client.DefaultClient‘, } } }
使用时涉及到一个多缓存问题,也很简单,用caches就行了。
cache = caches[‘redis_backend‘]
接下来是中间件的使用练习。
在工程目录下新建middleware目录
新建一个middle.py文件,创建一个继承了MiddlewareMixin的类:
from django.utils.deprecation import MiddlewareMixin class StdMiddle(MiddlewareMixin): pass
在settings中注册中间件:
中间件中有五个方法可以定义:
- process_request(self,request)
- process_view(self, request, callback, callback_args, callback_kwargs)
- process_template_response(self,request,response)
- process_exception(self, request, exception)
- process_response(self, request, response)
process_request
- 客户端发来请求与路由匹配执行之前执行
- 返回值是None时,继续向后执行下一个中间件的process_request或路由映射
- 返回值是HttpResponse对象时,不执行路由与views函数,直接执行该中间件与其之前的process_response,倒序执行
process_view
- 在执行完所有中间件的process_request与路由映射之后,views函数执行之前执行
- 执行顺序依然从第一个中间件到最后一个中间件
- callback参数为执行的views函数
- callback_args, callback_kwargs为views函数的参数
- 返回值是None时,继续向后执行下一个中间件的process_view或views函数
- 返回值是HttpResponse对象时,不执行views函数,直接执行所有中间件的process_response,倒序执行
process_template_response
- 视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)时,才被执行(并不是views函数最后返回render对象)
- 在views执行之后,process_exception执行之前执行
- 返回值必须是response
- 倒序执行
process_exception
- process_exception用于捕捉views函数中的异常
- 在process_response之前执行
- exception是views函数中产生的异常对象
- 返回值是None时继续正常执行
- 返回值是HttpResponse对象:不再执行后面的process_exception方法,直接执行process_response
- 倒序执行
process_response
- 在response返回给客户端之前执行,也就值最后经过
- 必须返回HttpResponse对象
上面一段讲解来自https://www.cnblogs.com/sfencs-hcy/p/10465967.html。
按照我的需求,我要在执行指定的视图函数时,判断用户是否登录,然后进行相应的操作,
所以这里应该定义process_view函数,通过request.META.get(‘PATH_INFO‘)获取请求的路由,判断是否在我指定的路由列表中,然后进行相应的操作,代码实现:
class StdMiddle(MiddlewareMixin): PATH_LIST = [‘/center/‘, ‘/students/‘, ‘/getStudent/‘, ‘/delStudent/‘] def process_request(self, request): print(request.META) def process_view(self, request, view_func, view_args, view_kwargs): ‘‘‘ :param request: 浏览器发来的 request 请求对象 :param view_func: 将要执行的视图函数的名字 :param view_args: 将要执行的视图函数的位置参数 :param view_kwargs: 将要执行的视图函数的关键字参数 :return: ‘‘‘ account = request.session.get(‘account‘) if request.META.get(‘PATH_INFO‘) in self.PATH_LIST: if account is None: # 没有session则返回到登录页面 return redirect(reverse(‘app:index‘)) elif request.META.get(‘PATH_INFO‘) == ‘/index/‘: if account: # 有session则直接进入个人中心 return redirect(reverse(‘app:center‘))
中间件实现频率控制。
思路:在客户端请求时get客户端的ip,在缓存中申请一个列表,里面存放的是发送请求时的时间,
然后用当前请求的时间-最早请求的时间,如果这个时间超过了指定的时间,则把最早请求的时间从列表中删除
如果没有超过,那么列表的长度就会+1,在这之后进行判断,如果长度超过了我们规定的次数,那么就拒绝本次访问,否则把列表存到缓存中。
代码实现:
def process_request(self, request): ip = request.META.get(‘REMOTE_ADDR‘) cache = caches[‘redis_backend‘] requestCount = cache.get(ip, []) while requestCount and time.time() - requestCount[-1] > 30: requestCount.pop() requestCount.insert(0, time.time()) if len(requestCount) >= 10: return HttpResponse(‘小爬虫回家吧‘) cache.set(ip, requestCount, timeout=60 * 5)
30秒内只能请求十次。如果是反爬虫可以设置一个黑名单用户表,如果超过了规定次数就把ip加到表中,然后每次请求时查询黑名单表,看ip是否在表中,这样就实现了简单的反爬虫。
使用process_exception来处理异常:
def process_exception(self, request, exception): return redirect(reverse(‘app:index‘))
这周最后学习了验证码登录,在登录页面添加一个验证码。
使用PIL进行绘图,首先获得一个四位的验证码:
def getCode(): source = ‘qwertyuioplkjhgfdsazxcvbnm1234567890POIUYTREWQASDFGHJKLMNBVCXZ‘ code = ‘‘ for i in range(4): code += random.choice(source) return code
使用PIL绘图,并把得到的image对象存到内存中,传到前端
def getCheckCode(request): # 验证码生成 code = getCode() mode = ‘RGB‘ size = (200, 100) color_bg = (getColor(), getColor(), getColor()) image = Image.new(mode=mode, size=size, color=color_bg) imageDraw = ImageDraw(image, mode=mode) imageFont = ImageFont.truetype(settings.FONT_PATH, 80) for i in range(4): imageDraw.text(xy=(50*i, 0), text=code[i], font=imageFont, fill=(getColor(), getColor(), getColor())) for i in range(1000): imageDraw.point(xy=(random.randrange(200), random.randrange(100)), fill=(getColor(), getColor(), getColor())) fp = BytesIO() image.save(fp, ‘png‘) request.session[‘checkCode‘] = code return HttpResponse(fp.getvalue(), content_type=‘image/png‘)
在前端加上一个Img标签即可,效果图:
......