Django笔记

一、开始

1.安装

pip install Django==3.2.7
pip install pymysql==1.0.2
pip install channels==2.1.7
pip install channels_redis==2.3.3
pip install pillow==3.0.4

pip uninstall django

2.创建

  • 创建工程和app
django-admin startproject projectName
django-admin startapp app
  • 注册app
#
INSTALLED_APPS = [
	...
    'django.contrib.staticfiles',
    'loginApp',
]

二、路由系统

1.路由与分发

  • 一对一
url(r'index/',view.index)
url(r'home/',view.Home.as_view())
  • 一对多路由
# 方法1
url(r'detail-(\d+).html',view.detail)

def detail(request,mid):
    detail_info = MDICT['mid']
    return TemplateResponse(request,'myapp2/madd.html',detail_info)

#方法2
url(r'detail-(?P<nid>\d+)-(?P<uid>\d+).html',view.detail)

def detail(request,**kwargs):
    kwargs = {'nid':1,'uid':2}
    
def detail(request,*args):
    args = (2,9)
<li><a href="/detail-{{ mid }}.html">{{ row.name }}</a></li>
  • 路由器分发
#一级路由
from django.contrib import admin
from django.urls import path,include

from appAuthentication import urls as auth_urls
from appDataDisplay import urls as display_urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('auth/',include((auth_urls,'auth_np'),namespace='auth_np')),
    path('display/',include((display_urls,'display_np'),namespace='display_np')),
]
#二级路由
from django.urls import path
from appAuthentication import views

# app_name = 'loginApp'
urlpatterns = [
    path('index.html',views.index,name='index'),
]

2.路由地址生成(模板生成)

  • 单条路由生成

    • 方法1
    url(r'asdgasgasgd/',view.index,name='indexx')
    
    <form action="{% url 'indexx' %}" method="POST">
        ...
    </form>
    
    • 方法2
    def index(request,nid):
    	print(request.path_info)	#当前路由地址,可用于获取动态的地址
    	...
    
  • 命名空间路由生成

# 工程.urls.py
from django.conf.urls import url,include
 
urlpatterns = [
    url(r'^a/', include('app01.urls', namespace='author-polls')),
    url(r'^b/', include('app01.urls', namespace='publisher-polls')),
]
# app.urls.py
from django.conf.urls import url
from app01 import views
 
app_name = 'app01'
urlpatterns = [
    url(r'^(?P<pk>\d+)/$', views.detail, name='detail')
]
# app.views.py
def detail(request, pk):
    print(request.resolver_match)
    return HttpResponse(pk)

# name的使用方法需调整
from django.urls import reverse
v = reverse('app01:detail', kwargs={'pk':11})
{% url 'app01:detail' pk=12 pp=99 %}

三、视图

1.用户请求

if request.method == "GET":		# 获取请求类型
	request.GET.get('name')		# 获取get数据
    ...
elif request.method == "POST":
	request.POST.get('name')		# 获取post数据
    request.POST.getlist('name')	# 获取post数据,checkbox或多选内容!
	request.FILES.get('name')		# 获取文件数据,具体操作在后面
    request.environ.items()			# 封装了用户所有信息
    request.environ['HTTP_USER_AGENT']	# 请求头,判断是手机端还是客户端
    ...

2.COOKIE和SESSION

  • cookie
# cookie中不设置超时时间,则表示关闭浏览器自动清除
# 设置 cookie:
rep = HttpResponse || render || redirect 
rep['name'] = 'sjl'									# 添加响应头
rep.set_cookie(key,value,...) 
rep.set_signed_cookie(key,value,salt='加密盐',...)	  # cookie加密

# 获取 cookie:
request.COOKIES.get(key)

# 删除 cookie:
rep.delete_cookie(key)
  • session
#session 设置:
request.session["key"] = value
request.session.setdefault('key',123)	#存在则不设置

# session 获取:
request..get('key')

# session删除
del request.session["is_login"] 	# 删除session_data里的一组键值对
request.session.flush()				# 删除一条记录包括(session_key session_data expire_date)三个字段
request.session.delete('session_key')# 删除当前用户的所有Session数据
request.session.clear()				# 注销,然后删除当前用户全部session数据

#用户session的随机字符串
request.session.session_key

#将单个用户session失效日期小于当前日期的数据删除
request.session.clear_expired()

# 检查用户session的随机字符串 在数据库中是否存在
request.session.exists('session_key')

# 如果value是个整数,session会在这些秒数后失效
# 如果value是个datetime或者timedelta,session就会在这个时间后失效
# 如果value是0,用户关闭浏览器时失效
# 如果value是None,session会依赖全局session失效策略
request.session.set_expiry(value)
SESSION_SAVE_EVERY_REQUEST = True
  • 全局session设置
#setting.py中设置session的默认选项(通用配置)
SESSION_SAVE_EVERY_REQUEST = False			# 每次请求是否都会重置时效时间
SESSION_EXPIRE_AT_BROWSER_CLOSE = False		# 是否关闭浏览器使得Session过期
SESSION_COOKIE_AGE = 1209600				# Session的cookie失效日期(2周)
SESSION_COOKIE_HTTPONLY = True				# 是否Session的cookie只支持http传输
SESSION_COOKIE_SECURE = False				# 是否Http传输cookie
SESSION_COOKIE_DOMAIN = None				# Session的cookie保存的域名
SESSION_COOKIE_PATH = "/"					# Session的cookie保存的路径
SESSION_COOKIE_PATH = "sessionid"			# Session的cookie保存到浏览器的键值对键名

# Session数据默认放到数据库中
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
# 缓存
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
# 文件
SESSION_ENGINE = 'django.contrib.sessions.backends.file'
SESSION_FILE_PATH = os.path.join(BASE_DIR,'session_cache')	#创建一个session_cache文件,将session放入其中
# 缓存+数据库
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
# 加密cookie session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'

3.基本方法

  1. FBV(function base view)、CBV(class base view)

    url(r'home/',view.Home.as_view())
    
    #CBV
    from django.views import View
    class Home(View):
        
        def dispatch(selft,request,*args,**kwargs):
            print('before')
            result = super(Home,self).dispatch(request,*args,**kwargs)
            print('after')
            return result
        
    	def get(self,request):
    		pass
        
        def post(self,request):
    		pass
    #FBV    
    def home_index(request):
        pass
    
  2. 给浏览器返回数据

from django.http import HttpResponse,HttpResponseRedirect,JsonResponse
from django.shortcuts import render,redirect
from django.views.decorators.csrf import csrf_exempt
from django.template.response import TemplateResponse

#老
render(request,"模板文件的路径",字典)
redirect("url")	#跳转到另外一个页面
#新
HttpResponse("字符串")
HttpResponseRedirect('url')#跳转到另外一个页面
TemplateResponse(request,"模板文件的路径",字典)
SimpleTemplateResponse('foo.html', 'path/to/bar.html')
  • 用户认证
def auth_is_login(func):  
    def warpper(request, *args, **kwargs):
        if not request.session.get('username'):
            return redirect('/auth/login.html')
    return warpper

# FBV
@auth_is_login
def index(request):
    pass

# CBV
from django views
from django.utils.decorators import method_decorator

@method_decorator(auth_is_login,name='dispatch')
class Order(View.view):
    
    def get(self,request):
        pass
    
    def post(self,request):
        pass

四、数据库

1.配置数据库

  • 在数据库中创建一个数据库
create database mydb default charset=utf8;   
  • 配置项目settings.py文件
DATABASES = { 
    'default': 
    { 
        'ENGINE': 'django.db.backends.mysql',    	# 数据库引擎
        'NAME': 'mydb',						 		# 数据库名称
        'HOST': '127.0.0.1', 						# 数据库地址,本机 ip 地址 127.0.0.1 
        'PORT': 3306,				 				# 端口 
        'USER': 'root',  							# 数据库用户名
        'PASSWORD': 'root', 						# 数据库密码
    }  
}

INSTALLED_APPS = [
    ...
    'loginApp',
]
  • 在与settings.py同目录下的_init_.py进行配置
# Django默认是用的MySQLdb模块连接MYSQL,主动改为pymysql
import pymysql
pymysql.install_as_MySQLdb()

2.创建模型

  • 创建app
django-admin.py startapp myModelapp
  • 创建表
class Person(models.Model):
    SEX_CHOICES = (
        ( 0 , '男'),
        ( 1 , '女')
    )
 	# id列,自增,主键
    name = models.CharField(max_length=32)
    age  = models.IntegerField()
    sex  = models.BooleanField(choices=SEX_CHOICES)
    id_card = models.CharField(max_length=18)
    addr = models.CharField(max_length=255,null=True)	#可以为空
    temp = models.FloatField()
    
    class Meta:
        #db_table = 'M_Major'        #指明数据库表名
        verbose_name = '专业表'      #指明admin显示名称
        
    #外键的关键字段,将对象转换成字符时调用
    def __str__(self):
    	return self.major_name

# DateField,日期类型date
# DateTimeField,日期类型datetime
# TimeField 时间 HH:MM[:ss[.uuuuu]]
# AutoField(primary_key=True)主键

"""
字段:
	字符串类型
	数字
	时间
	二进制

字段参数
	null=True			是否可以为空
	default=143			默认值
	primary_key=True	主键
	db_column=’cn‘		表的当前列列名
	db_index=True		索引
	unique=True			唯一索引
	unique_for_date		只对日做索引
	unique_for_month	只对月做索引
	unique_for_year		只对年做索引	
	auto_now			创建时,自动生成时间
	auto_now_add		更新时,自动更新为当前时间
	
	choices				django admin下拉选择
	blank				django admin是否为空
	verbose_name		django admin显示字段为中文
	editable			django admin是否可以被编辑
	error_messages		django admin错误提示信息
	help_text			django admin提示
	validators			django form/admin自定义错误信息
"""

  • 外键创建
class Major(models.Model):
    name = models.CharField(max_length=30,verbose_name = '专业名称') 
    college_id = models.ForeignKey(to="College",on_delete=models.CASCADE,verbose_name = '学院id号')
    
class College(models.Model):
    name = models.CharField(max_length=30,verbose_name = '学院名称') 
    
#(uid = models.AutoField(primary_key=True) --> to_field='uid')  
  • 多对多
#自定义关系表
class Host(models.Model):
    hostname = models.CharField(max_length=32,db_index=True)
    ip = models.GenericIPAddressField(protocol="ipv4",db_index=True)
    port = models.IntegerField()
   
class Application(models.Model):
    name = models.CharField(max_length=32)
    
class HostToApp(models.Model):
    hobj = modeels.ForeignKey(to='Host',to_field='id')
    aobj = modeels.ForeignKey(to='Application',to_field='id')
    
#自动创建关系表(无法直接操作第三张表,通过Application来操作)
class Host(models.Model):
    hostname = models.CharField(max_length=32,db_index=True)
    ip = models.GenericIPAddressField(protocol="ipv4",db_index=True)
    port = models.IntegerField()
   
class Application(models.Model):
    name = models.CharField(max_length=32)
    r = models.ManyToManyField('Host')
   
obj = models.Application.objects.create(name=app_name)
#增删改
obj.r.add(2)
obj.r.add(2,3)
obj.r.add(*host_list)	#host_list是一个列表
obj.r.remove(..)
obj.r.clear()
obj.r.set([1,2,3,55])	#交集,有就保存,没有就删除
#查
obj.r.all()					#全部,多个组
obj.r.filter(name='CTO')	#筛选
  • 创建数据库
# 让 Django 知道我们在我们的模型有一些变更,生成迁移文件
python manage.py makemigrations loginApp  
# 创建表结构,执行迁移文件
python manage.py migrate loginApp  
#大型改动需要删除数据库表和app目录下migrations目录下的文件c

3.数据库操作

  • 添加数据
 #111111
test1 = models.Person(name='李四',
                		age=19,
                		sex=1,
                		id_card='438726200008142097',
                		addr='长沙',
                		temp=36.6)
test1.save()

#222222
models.Person.objects.create(title="如来神掌",
    						  price=200,
                              publish="功夫出版社",
                              pub_date="2010-10-10") 

#3333333
dic = {'title':"如来神掌",'price':200,'publish':"功夫出版社"}
models.Person.objects.create(dic)

#外键插入1,需要两次数据库查询
pub_obj = models.Publish.objects.filter(pk=1).first()
book = models.Book.objects.create(title="菜鸟教程", price=200, pub_date="2010-10-10", publish=pub_obj)
#外键插入2(外键字段_id=id值)
book = models.Book.objects.create(title="菜鸟教程", price=200, pub_date="2010-10-10", publish_id=12)
  • 查找数据
# 通过objects这个模型管理器的all()获得所有数据行,相当于SQL中的SELECT * FROM
result = Person.objects.all()
#result,QuerySet => Django => [obj(title,price),obj...]        
    
    
# 获取单个对象
response3 = Person.objects.get(id=1) 
# filter相当于SQL中的WHERE,可设置条件过滤结果
response2 = Person.objects.filter(id=1) 
# 查询价格大于200的数据 
books = models.Book.objects.filter(price__gt=200)
# 查询价格小于300的数据 
books=models.Book.objects.filter(price__lt=300)
# 查询价格在200到300之间的数据
books=models.Book.objects.filter(price__range=[200,300])
# 查询包含"菜"的数据
books=models.Book.objects.filter(title__contains="菜")
# 查询包含"python"的数据(不区分大小写)
books=models.Book.objects.filter(title__icontains="python") 
# 查询以指定字符开头的数据
books=models.Book.objects.filter(title__startswith="菜")
# 查询以指定字符结尾的数据
books=models.Book.objects.filter(title__endswith="教程")
#  查询DateField 数据类型的年份
books=models.Book.objects.filter(pub_date__year=2008) 
#  查询DateField 数据类型的月份
books=models.Book.objects.filter(pub_date__month=10)
#  查询DateField 数据类型的日
books=models.Book.objects.filter(pub_date__day=01)

# 数据排序
Person.objects.order_by("id")
# 限制返回的数据 相当于 SQL 中的 OFFSET 0 LIMIT 2;
Person.objects.order_by('name')[0:2]    
# 上面的方法可以连锁使用
Person.objects.filter(name="runoob").order_by("id")
    
# 查询所有数据的数量 
models.Book.objects.count()
# 返回所有数据的第一条数据
books = models.Book.objects.first() 
# 返回所有数据的最后一条数据
books = models.Book.objects.last() 
# 判断的数据QuerySet中是否有数据
books = models.Book.objects.exists()
    
# 查询所有的id字段和price字段的数据
books = models.Book.objects.values("pk","price")
books = models.Book.objects.values_list("price","publish")
# 对数据进行去重
books = models.Book.objects.values_list("publish").distinct()

#Book:  title="菜鸟教程", price=200, pub_date="2010-10-10", publish=pub_obj
# 跨表查询1(对象)
book = Book.objects.all()
book.first().publish_id.title
# 跨表查询2(字典),模板里面也可以用双下划线!!!
book = Book.objects.filter(id__gt=0).values('id','price','publish_id','publish__title')
# 跨表查询3(元组)
book = Book.objects.filter(id__gt=0).values_list('id','price','publish_id','publish__title')
  • 删除数据
books = models.Book.objects.filter(pk=8).first().delete()
books = models.Book.objects.filter(pk__in=[1,2]).delete()
  • 修改数据
books = models.Book.objects.filter(pk=7).first() 
books.price = 400 
books.save()

books = models.Book.objects.filter(pk__in=[7,8]).update(price=888)
  • 外键的关键字段
def __str__(self):
    return self.major_name

4.admin后台管理页面

# 创建管理员账户和密码
python manage.py createsuperuser
# 在app下admin.py文件中添加注册
from .models import Student,College,Major

# Register your models here.
admin.site.register(Student)
admin.site.register(College)
admin.site.register(Major)

五、模板

  • view中传递方法
def func(request):
  content['name'] = "我是名字"
  content['dict'] = {"name":"6666"}
  content['list'] = ["我是数据1","我是数据2","我是数据3"] 
  return TemplateResponse(request,"runoob.html", content)
  • 常用方法
<h1>{{ hello }}</h1>
<h1>{{ dict.name }}</h1>

{% if condition1 %}
   ... display 1
{% elif condition2 %}
   ... display 2
{% else %}
   ... display 3
{% endif %}

{% for i in views_list %}
{{ forloop.parentloop }} {# 查看循环信息 #}
{{ forloop.counter }}	 {# 循环信息之一——计数 #}	
{{ i }}
{% endfor %}

{% if x > 0 %}
<h2>{{ x }}是大于0的</h2>
{% elif x == 0%}
<h3>{{ x }}是等于0的</h3>
{%elif x < 0 %}
<h4>{{ x }}是小于0的</h4>
{% endif %}

{{ str|safe }}								{# 这个字符串是安全的,不需要转义,可以直接执行 #}
{{ item.event_start|date:"Y-m-d H:i:s" }}	{# 时间 #}
{{ str|truncatewords:"30" }}				{# 取前30个字符 #}
{{ name|lower }}							{# 字符串转小写 #}
  • 自定义方法(simple_tag,filter)
# templatetags/xxoo.py
# 目录的名字必须是templatetags,在app目录下,不能改!!!
from django import template
from django.utils.safestring import mark_safe

register = template.Library()

#可以随意传参数
@register.simple_tag
def myfun(a1,a2):
    return a1 + a2

#只能传2个参数
@register.filter
def myfun2(a1,a2):
    return a1 + a2
{# a.html templates文件 #}
{% load xxoo %}

{% myfun 2 5 %}

{{ "666"|myfun2:"999"}}
{% if "666"|myfun2:"999" %}
	...
{% endif %}
  • 模板继承
{% extends "母板路径"%} 

{% block 名称 %} 
	预留给子模板的区域,可以设置设置默认内容
{% endblock 名称 %}
  • 模板包含
{% include "tag.html" %}  
  • 模板加载路径设置
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR,'templates')],   #自定义模板路径app/templates/就不用打了
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
  • 静态文件
# 在工程的更目录下建立static文件夹,然后把建立js,css等等文件夹,把文件放入
# 在setting.py文件中加入以下代码
import os
STATIC_URL = '/static/'
STATICFILES_DIRS=(
     os.path.join(BASE_DIR,'static').replace('\\','/'),
)

#在html使用静态文件
{% load static %}
<script src="{% static 'js/jquery.js' %}"></script>
  • 分页
# view.py
def index(request): 
    notices = models.Notice.objects.all().order_by('-pub_time')
    
    paginator = Paginator(notices,10)			#创建分页器,10条为一页
    num_p = request.GET.get('page',1)           # 以page为键得到默认的页面1
    page  = paginator.page(int(num_p))			#找到第一个页面

    return TemplateResponse(request,'a.html',locals())

<!--a.html-->
{% for p in page %}
<div class="item">
    <h2>{{ p.title }}</h2>
    <p>{{ p.content }}</p>
</div>
<hr/>
{% endfor %}

<!--判断是否有上一页,然后拼接关于page的查询字符串-->
{% if page.has_previous %}
	{# 得到上一页的页码编号 #}
	<a href="/loginApp/thrindex.html?page={{ page.previous_page_number }}">上一页</a>
{% else %}
	上一页
{% endif %}

{# 在页面范围内循环选择一个页面号码 #}
{% for p in paginator.page_range %}
	{# 如果页面编号等于当前页码序号 #}
	{% if p == page.number %} 
		{{ p }}
	{% else %}
		{# 否则跳转到页码相对应的页面 #}
		<a href="/loginApp/thrindex.html?page={{ p }}">{{ p }}</a>
	{% endif %}
{% endfor %}

{% if page.has_next %}
	<a href="/loginApp/thrindex.html?page={{ page.next_page_number }}">下一页</a>
{% else %}
	下一页
{% endif %}

六、高级操作

1.csrf

  • 开启
#setting.py
'django.middleware.csrf.CsrfViewMiddleware',	#开启中间件csrf
  • 表单引用组件
<form>
    {# 表单提交生成一个隐藏带值的控件 #}
	{% csrf_token %}	
</form>
  • ajax引用组件
<script>
   
$.ajaxSetup({
     //在所有ajax请求之前执行的代码
     beforeSend:function(xhr,settings){
         //xhr:XMLHttpRequest
         //setting:当前点的ajax的所有配置
         xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken'))	//把csrftoken的cookie加入头
     }
})
    
 $('.cal').click(function(){
     $.ajax({
         url:'/login/',
         type:'POST',
         data:{
             'a1':$('.a1').val(),
             'a2':$('.a2').val()
         },
         success:function(data){
             $('.a3').val(data);
             #location.reload();
         }
     })
 });
    
</script>
  • view中操作
# 有csrf过滤
def add(request):
    if request.method == 'POST':
        a = request.POST.get('a1')
        b = request.POST.get('a2')
        c = int(a) + int(b)
        return HttpResponse(c)
    elif request.method == 'GET':
        # return HttpResponse('我是get请求')
        return TemplateResponse(request,'myapp2/madd.html')
  
# 取消当前函数防止跨站请求伪造功能
@csrf_exempt
def add(request):
    pass

# 为当前函数强制设置跨站请求伪造功能
@csrf_exempt
def add(request):
    pass
  • csrf配置
CSRF_COOKIE_AGE			#cookie的有效期
CSRF_COOKIE_DOMAIN		#允许访问的域名
CSRF_COOKIE_HTTPONLY	#是否允许JS读取cookie
CSRF_COOKIE_NAME		#cookie的键
CSRF_COOKIE_PATH		#cookie的位置
CSRF_COOKIE_SAMESITE
CSRF_COOKIE_SECURE		#将此设置为 True,避免不小心使用 HTTP 传输 CSRF cookie。
CSRF_FAILURE_VIEW		#csrf拒绝后跳转的视图
CSRF_HEADER_NAME		#csrf在header中的键
CSRF_TRUSTED_ORIGINS	#信任源
CSRF_USE_SESSIONS		#是否使用基于session的csrf令牌
  • ajax字典传输写法
def test_ajax(request):
	import json
	ret = {'status',True,'error':None,'data',None}
	try:
		h = request.POST.get('hostname')
		j = request.POST.get('port')
		if len(h) >5:
			pass #插入到数据库
		else:
			ret['status'] = False
			ret['error'] = "太短了"
	except Exception as e:
		ret['status'] = False
		ret['error'] = "请求错误..."
	return HttpResponse(json.dumps(ret))
success:function(data){
	var obj = JSON.parse(data);
	if(obj.status){
		...
	}else{
		...
	}
}

3.中间件

  • 创建中间件
# Middle/m1.py	在根目录下创建
from django.utils.deprecation import MiddlewareMixin
"""
流程:过滤所有用户的请求,一下是整个运行流程
 请求 -> row1:process_request -> row2:process_request -> row3:process_request \ 
     \ row3:process_view <- row2:process_view <- row1:process_view <- 路由映射匹配
      自己写的view函数 -> row3:process_response -> row2:process_response -> row1:process_response
"""
#view_func是View函数指针,view_func_args和view_func_kwargs是view函数的参数

class Row1(MiddlewareMixin):
	def process_request(self,request):
        print("111111") 
    def process_view(self,request,view_func,view_func_args,view_func_kwargs):
        print("zzz111")
    def process_response(self,request,response):
        print("666666")
        return response
        
class Row2(MiddlewareMixin):
	def process_request(self,request):
        print("222222")     
        return HttpResponse("走")	#阻止请求。。。
    def process_view(self,request,view_func,view_func_args,view_func_kwargs):
        print("zzz222")
    def process_response(self,request,response):
        print("555555")
        return response

class Row3(MiddlewareMixin):
	def process_request(self,request):
        print("333333")    
    def process_view(self,request,view_func,view_func_args,view_func_kwargs):
        print("zzz333")
    def process_response(self,request,response):
        print("444444")
        return response
    def process_exception(self,request,exception)
    	printf("view函数报错了就执行")
        if isinstance(exception,ValueError):
            return HttpResponse('出现异常了...')
    def process_template_exception(self,request,response):
        # 如果Views中的函数返回的对象中,具有render方法就执行
        print("-------------")
        return response
  • 注册中间件
# setting.py
MIDDLEWARE = [
    ...,
    'Middle.m1.Row1',
    'Middle.m1.Row2',
    'Middle.m1.Row3',
]

4.缓存

由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。

  • 设置缓存

    • 开发调试
    # 此为开始调试用,实际内部不做任何操作 配置:
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.dummy.DummyCache',     # 引擎
            'TIMEOUT': 300,                # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
            'OPTIONS':{
                'MAX_ENTRIES': 300,        # 最大缓存个数(默认300)
                'CULL_FREQUENCY': 3,       # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认1/3)
            },
            'KEY_PREFIX': '',              # 缓存key的前缀(默认空)
            'VERSION': 1,                  # 缓存key的版本(默认1)
            'KEY_FUNCTION': 函数名           # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
        }
    }
    
    # 自定义key
    def default_key_func(key, key_prefix, version):
        """
            Default function to generate keys.
    
            Constructs the key used by all other methods. By default it prepends
            the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
            function with custom key making behavior.
            """
        return '%s:%s:%s' % (key_prefix, version, key)
    
    def get_key_func(key_func):
        """
            Function to decide which key function to use.
    
            Defaults to ``default_key_func``.
            """
        if key_func is not None:
            if callable(key_func):
                return key_func
            else:
                return import_string(key_func)
            return default_key_func
    
    • 内存
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            'LOCATION': 'unique-snowflake',	# 全局其实就是一个字典,这是变量名
        }
    }
    
    # 注:其他配置同开发调试版本
    
    • 文件
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
            'LOCATION': os.path.join(BASE_DIR,'django_cache'),	#路径
        }
    }
    # 注:其他配置同开发调试版本
    
    • 数据库
    # 数据库
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
            'LOCATION': 'my_cache_table', # 数据库表
        }
    }
    
    # 注:执行创建表命令 python manage.py createcachetable
    # 就会生成一个表my_cache_table
    
    • Memcache缓存(python-memcached模块)
    # 此缓存使用python-memcached模块连接memcache
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': '127.0.0.1:11211',
        }
    }
    
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': 'unix:/tmp/memcached.sock',
        }
    }   
    
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': [
                '172.19.26.240:11211',
                '172.19.26.242:11211',
            ]
        }
    }
    
    
    • Memcache缓存(pylibmc模块)
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': '127.0.0.1:11211',
        }
    }
    
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': '/tmp/memcached.sock',
        }
    }   
    
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
            'LOCATION': [
                '172.19.26.240:11211',
                '172.19.26.242:11211',
            ]
        }
    }
    
    • Redis缓存(依赖:pip3 install django-redis)
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
                "CONNECTION_POOL_KWARGS": {"max_connections": 100}
                # "PASSWORD": "密码",
            }
        }
    }
    
  • 使用缓存

# 视图中链接并操作
from django_redis import get_redis_connection
conn = get_redis_connection("default")
#整个页面做缓存
from django.views.decorators.cache import cache_page
@cache_page(10)	#10秒之内都会去缓存取数据
def cache(request):
    import time
    ctime = time.time()
    return render(request,'cache.html',{'ctime':ctime})
{# 局部缓存 #}
{% load cache %}
{% cache 5000 缓存key %}	{# 超时时间:单位s key #}
	缓存内容
{% endcache %}
#全站缓存
"""
 使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存
"""
import django.middleware.cache import UpdateCacheMiddleware
import django.middleware.cache import FetchFromCacheMiddleware
MIDDLEWARE = [
    'django.middleware.cache.UpdateCacheMiddleware',
    # 其他中间件...
    'django.middleware.cache.FetchFromCacheMiddleware',
]

CACHE_MIDDLEWARE_ALIAS = ""
CACHE_MIDDLEWARE_SECONDS = ""
CACHE_MIDDLEWARE_KEY_PREFIX = ""

5.信号

  • 信号(触发器)
#使用方法
# __init__.py中调用下面代码(建议封装成模块)
from django.core.signals import request_finished
from django.core.signals import request_started
from django.core.signals import got_request_exception

from django.db.models.signals import class_prepared
from django.db.models.signals import pre_init, post_init
from django.db.models.signals import pre_save, post_save
from django.db.models.signals import pre_delete, post_delete
from django.db.models.signals import m2m_changed
from django.db.models.signals import pre_migrate, post_migrate

from django.test.signals import setting_changed
from django.test.signals import template_rendered

from django.db.backends.signals import connection_created
    
def fun(sender,**kwargs):
    print('按照信号规则触发此函数')
pre_init.connect(fun)	#注册一个信号,django的modal执行其构造方法前,自动执行fun函数
Model signals
    pre_init                    # django的modal执行其构造方法前,自动触发
    post_init                   # django的modal执行其构造方法后,自动触发
    pre_save                    # django的modal对象保存前,自动触发
    post_save                   # django的modal对象保存后,自动触发
    pre_delete                  # django的modal对象删除前,自动触发
    post_delete                 # django的modal对象删除后,自动触发
    m2m_changed                 # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
    class_prepared              # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
Management signals
    pre_migrate                 # 执行migrate命令前,自动触发
    post_migrate                # 执行migrate命令后,自动触发
Request/response signals
    request_started             # 请求到来前,自动触发
    request_finished            # 请求结束后,自动触发
    got_request_exception       # 请求异常后,自动触发
Test signals
    setting_changed             # 使用test测试修改配置文件时,自动触发
    template_rendered           # 使用test测试渲染模板时,自动触发
Database Wrappers
    connection_created          # 创建数据库连接时,自动触发
  • 自定义信号
# 定义信号
import django.dispatch
pizza_done = django.dispatch(providing_args=["toppings","size"])

#注册信号
def fun(sender,**kwargs):
    print('按照信号规则触发此函数')
pizza_done.connect(fun)	#注册一个信号,django的modal执行其构造方法前,自动执行fun函数

#触发信号
pizza_done.send(sender='seven',toppings=123,size=456)

6.表单验证

  • form组件
# 生成HTML标签
# 验证用户数据(显示错误信息)
# HTML Form提交保留上次提交数据
# 初始化页面显示内容
from django.forms import widgets
from django.forms import fields
class stuForm(forms.Form):
    stu_id = fields.CharField(
        label='学号',
        min_length=11,
        max_length=11,
        widget=widgets.TextInput(attrs={'id': 'stu_id', 'class': 'stu_id'})	#这个地方可以设置控件的属性
    )

    passwd = fields.CharField(
        label='密码',
        widget=widgets.PasswordInput(attrs={'class': 'passwd'}, render_value=True)	#不会丢
    )
 
    sex = fields.ChoiceField(
        label='性别',
        choices=((0, '男'), (1, '女'),),
        initial=1,
        widget=widgets.RadioSelect
    )

    age  = fields.IntegerField(
        label='年龄',
        min_value=0,
        max_value=130,
    )

    stu_name = fields.CharField(
        label='真实姓名',
        min_length=2,
        max_length=5,
        widget=widgets.TextInput(attrs={'id': 'stu_name', 'class': 'stu_name'})
    )

    major_name = form_model.ModelChoiceField(
        label='专业班级',
        queryset=models.Major.objects.all(),
        widget=widgets.Select(),
        initial=0
    )

    phone_number = fields.CharField(
        label='手机号码',
        min_length=11,
        max_length=11,
        widget=widgets.TextInput(attrs={'id': 'phone_number', 'class': 'phone_number'})
    )
    
def register(request):
    view_content = {}
    view_content['college'] = models.College.objects.all()
    view_content['major'] = models.Major.objects.all()
	dic = {
        'user':'rrr',
        'passwd':'pppwww' ,
    }
    
    if request.method == 'GET':
        view_content['form'] = stuForm(initial=dic) #生成时初始化数
        return TemplateResponse(request,'loginApp/register.html',view_content)

    elif request.method == 'POST': 
        obj = stuForm(request.POST)
        view_content['form'] = obj
        if obj.is_valid():	#用户的表单正确
            #values = obj.clean()
            models.UserInfo.objects.create(**obj.cleaned_data)	#直接存入数据库
            print(values)
        else:
            print(obj.errors.as_json())
        #return render(request, 'index.html', {'form': obj})
        return TemplateResponse(request,'loginApp/register.html',view_content)
<form action="register.html" method="POST" enctype="multipart/form-data">
    <p>{{ form.stu_id.label }} : {{ form.stu_id }} {{ form.stu_id.errors }}</p>
    <p>{{ form.sex.label }} : {{ form.sex }} {{ form.sex.errors }}</p>
    <p>{{ form.passwd.label }} : {{ form.passwd }} {{ form.passwd.errors }}</p>
    <p>{{ form.age.label }} : {{ form.age }} {{ form.age.errors }}</p>
    <p>{{ form.stu_name.label }} : {{ form.stu_name }} {{ form.stu_name.errors }}</p>
    <p>{{ form.major_name.label }} : {{ form.major_name }} {{ form.major_name.errors }}</p>
    <p>{{ form.phone_number.label }} : {{ form.phone_number }} {{ form.phone_number.errors }}</p>
    
    {{ form.as_p }}
    {{ form.as_ul }}
    <table>{{ form.as_table }}</table>

    <input type="submit"/>
</form>
  • modelsForm
from django.forms import fields as Ffields
from django.forms import widgets as Fwidget
class UserInfoModelForm(forms.Form):
    is_rmb = Ffields.CharField(widget=Fwidgets.CheckboxInput())	#获取到值,检查是否失效
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'
            field.widget.attrs['placeholder'] = '请输入{}'.format(field.label)
    # 验证用户名
    def clean_username(self):
        # 校验数据前,都需要获取到被校验的数据
        username = self.cleaned_data['username']
        # 开始校验:判断数据库中是否已存在用户名
        xists = models.User.objects.filter(username=username).exists()
        if exists:
            raise ValidationError('用户名已存在')
            return username

    class Meta:
        model = models.UserInfo					#对应哪个字段
        fields = '__all__'						#取所有字段
        fields = ['username'] 					#展示哪个字段
        exclude = ['username']				  	#排除哪个字段
        labels = { 'username':'用户名'}		  #提示信息
        help_texts = {'username':'...'}			#帮助信息
        widgets = {'username':Fwidget.Textare(attrs={'class':'c1'})}	#控件
        error_messages = {
            'email':{'required':'邮箱不能为空'}	#字段对应字典,字典里一个个code设置字段信息
        }
        field_classes = {'email':Ffield.URLField}	#修改验证格式
        localized_fields = ('publish_date')		# 时区本地化
        """
        setting.py
        TIME_ZONE = 'Asia/Shanghai'
        USE_TZ = True
        
        数据库中 2016-12-27 04:10:57 -> 2016-12-27 12:10:57
        """`
        
def index(request):
    if request.method == 'GET':
        obj = UserInfoModelForm()
        return render(request,'index.html',{'obj':obj})
    if request.method == 'POST':
        obj = UserInfoModelForm(request.POST)
        if obj.is_valid():
            obj.save()	#直接保存到数据库了
        return render(request,'index.html',{'obj':obj})
    
obj = UserInfoModelForm(request.POST)        
instance = obj.save(False)	#返回数据库实例
instance.save()			 # 保存
instance.save_m2m()		# 保存多对多表

def user_edit(request,nid):
    if request.method == "GET":
        user_obj = models.UserInfo.objects.filter(id=nid).first()
        mf UserInfoModelForm(instance=user_obj)
        return render(request,'user_edit.html',{'mf',mf,'nid':nid})
    if request.method == "POST":
        user_obj = models.UserInfo.objects.filter(id=nid).first()
        mf = UserInfoModelForm(request.POST,instance=user_obj)	#表示修改数据
        if mf.is_valid():
            mf.save()
        else:
            print(mf.errors.as_json())
        return render(request,'user_edit.html',{'mf',mf,'nid':nid})    
  • 伪ajax请求
//iframe可以直接用来上传文件!!!view部分用标准文件获取写法
function iframeSubmit(){
	$('iframe').load(function(){
		var txt = $('#ifml').contents().find('body').text();	//取iframe中的数据
        var obj = JSON.parse(txt);
	})
}
<form action='/upload_file/' method='POST' enctype='multipart/form-data' target='ifm1'>
    <!--form中的taget在指定的iframe中打开-->
    <iframe id='ifm1' name='ifm1'></iframe>
    <input type='file' name='fafafa'/>
    <input type='submit' onclick='iframeSubmit();' value='提交'/>
</form>
  • ajax上传文件
<div class="form2">
    <input type="file" name="file" id="file_upload">
    <input type="button" value="上传" class="file_upload_submit">
</div>

{% load static %}
<script src="{% static 'js/jquery.js' %}"></script>
<script>

    $('.file_upload_submit').click(function(){

        var form_data = new FormData();
        var file_info = $('#file_upload')[0].files[0];
        var csrf = $('input[name=csrfmiddlewaretoken]').val();
        form_data.append('file',file_info);
        form_data.append('csrfmiddlewaretoken', csrf);

        //console.log($('#file_upload')[0].files)

        if(file_info==undefined){//暂且不许要判断是否有附件
            alert('你没有选择任何文件');
            return false
        }

        $.ajax({
            url:'/myapp2/fileUpload',
            type:'POST',
            data: form_data,
            processData: false,  // tell jquery not to process the data
            contentType: false, // tell jquery not to set contentType
            success: function(callback) {

                console.log('ok')
            },
            // header:{
            // 'X-CSRFToken': $.cookie('csrftoken'),
            // }
        });

    });
</script>
@csrf_exempt
def uploadFile(request):
    if request.method == 'POST':
        file_obj = request.FILES.get('file')
        name = file_obj.name
        size = file_obj.size
        if file_obj:
            import os
            f = open(os.path.join(settings.BASE_DIR, 'static', 'upload', file_obj.name), 'wb')
            for chunk in file_obj.chunks():
                f.write(chunk)
            f.close()
           
            print('11111')
    return TemplateResponse(request,'myapp2/madd.html')

with open(os.path.join(settings.BASE_DIR, 'static', 'upload', file_obj.name), 'wb) as f:
    for chunk in file_obj.chunks()
          f.write(chunk)

xss攻击

{{ str|safe }}								{# 这个字符串是安全的,不需要转义,可以直接执行 #}
from django.utils.safestring import mark_safe
page_str = "<h1>666</h1>"
page_str = make_safe(page_str)

8.验证码

"""
1.访问页面/login/
	- 创建一个图片给用户
	- session储存验证码
2.POST页面
	- 检查表单和session是否匹配
	
pip3 install Pillow
1.创建一张图片
2.在图片中写入随机字符串
3.将图片写入到定制文件
"""
<img src='/check_code.html' onclick="changeCheckCode(this);">
function changeCheckCode(this){
    this.src = this.src + '?';	//重新发请求
}

VSCODE

  • html没有提示
  1. 打开设置,在设置中搜索Files: Associations,可以看到这么一个选项,
  2. 在键中输入**/*.html,在值中输入html,点击勾完成,具体如下图所示
posted @ 2022-01-29 22:32  09w09  阅读(68)  评论(0)    收藏  举报