天天看点

Django重要知识点1、Django获取多个数据2、Django文件上传3、CBV和FBV4、DjangoORM5、视图获取用户请求相关信息以及请求头6、分页7、跨站请求伪造8、Cookie9、Session10、中间件11、缓存12、信号

目录

  • 1、Django获取多个数据
  • 2、Django文件上传
  • 3、CBV和FBV
  • 4、DjangoORM
    • 1.创建基本类型以及生成数据库结构
    • 2.外键操作
    • 3.获取单表单数据的三种方式
    • 4.获取一对多数据的的三种方式
    • 5.多对多关系
      • 创建多对多
    • 对第三张表的数据操作
  • 5、视图获取用户请求相关信息以及请求头
  • 6、分页
    • 自定义分页
  • 7、跨站请求伪造
    • 一、简介
    • 二、应用
  • 8、Cookie
    • 设置Cookie
    • 获取Cookie
  • 9、Session
  • 10、中间件
  • 11、缓存
  • 12、信号
    • 1、Django内置信号
    • 2、自定义信号

1、Django获取多个数据

在我们的view.py中获取前端form表单发来的一个数据比较简单,比如text、radio、password等

获取多个数据,比如checkbox、select

2、Django文件上传

前端代码

<form action="" method="post" enctype="multipart/form-data">
	<input type="file" name="fname" />
	<input type="submit" value="提交" />
</form>
           

view.py

obj = request.FILES.get("fname")
# obj.name 文件名
# obj.size 文件大小

print(obj, type(obj))

f = open(obj.name, "wb") # 当前路径新建一个文件,名叫源文件的名字
for i in obj.chunks(): # 本地下载,chunks生成器迭代器的知识点
	f.write(i)
f.close()
           

3、CBV和FBV

FBV:function base view

就是经常用的,urls.py中请求对应views.py中的函数

urls.py
	path('index/', views.index),
	
views.py
	def index(request):
		if	request.method == "POST":
    		return render(request, 'index.html')
    	elif request.method == "GET":
    		return render(request, 'index.html')
           

FBV装饰器

def auth(func): # FBV装饰器
    def inner(request, *args, **kwargs):
        username = request.COOKIES.get("username111")
        print("index", username)
        if not username:
            return render(request, 'login.html')
        return func(request, *args, **kwargs)
    return inner


@auth
def index(request):
    username = request.COOKIES.get("username111")
    # #username = request.COOKIES['username111']
    # print("index", username)
    # if not username:
    #     return render(request, 'login.html')
    return render(request, 'index.html', {"username": username})
           

CBV:class base view

urls.py中请求对应views.py中的类,和FBV不同点就是,不需要自己去判断提交方式是哪一种了

提交方式有:[‘get’, ‘post’, ‘put’, ‘patch’, ‘delete’, ‘head’, ‘options’, ‘trace’]

urls.py
	path('home/', views.Home.as_view()),
	
views.py
	from django.views import View
	class Home(View):
		# 请求来了,先调用dispatch,基于反射找到相应的提交方式
		#这样重写父类的dispatch可以定制功能
		def dispatch(self, request, *args, **kwargs):
	       #调用父类的dispatch
	        print('before')
	        result = super(Home, self).dispatch(request, *args, **kwargs)
	        print('after')
	        return result

	    def get(self, request):
	        print(request.method)
	        return render(request, 'home.html')
	
	    def post(self, request):
	        print(request.method)
	        return render(request, 'home.html')
           

CBV装饰器

def auth(func): # FBV装饰器
    def inner(request, *args, **kwargs):
        username = request.COOKIES.get("username111")
        print("index", username)
        if not username:
            return render(request, 'login.html')
        return func(request, *args, **kwargs)
    return inner

from django import views
from django.utils.decorators import method_decorator
@method_decorator(auth, name='dispatch') # 和下面的dispatch函数一样的功能
class Order(views.View): # CBV装饰器
    """
    @method_decorator(auth) # 装饰内部所有函数
    def dispatch(self, request, *args, **kwargs):
        return super(Order, self).dispatch(request, *args, **kwargs) """

    # @method_decorator(auth) # 装饰这一个函数
    def get(self, request):
        username = request.COOKIES.get("username111")
        return render(request, 'index.html', {"username": username})
    def post(self, request):
        username = request.COOKIES.get("username111")
        return render(request, 'index.html', {"username": username})
           

4、DjangoORM

1.创建基本类型以及生成数据库结构

创建类

1、根据类自动创建数据库表

在app下的models.py

from django.db import models

# Create your models here.

#app01_userinfo
class UserInfo(models.Model):
    #  隐含的,自动创建id列,自增,主键
    # 用户名列、字符串类型、指定长度
    # 字段:字符串、数字、时间、二进制、自增(primary_key)
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
           

Django提供了大量的字段,其中大部分字段都是给Django自带的后台管理系统使用的

字段
AutoField(Field)
   - int自增列,必须填入参数 primary_key=True

BigAutoField(AutoField)
   - bigint自增列,必须填入参数 primary_key=True

   注:当model中如果没有自增列,则自动会创建一个列名为id的列
   from django.db import models

   class UserInfo(models.Model):
       # 自动创建一个列名为id的且为自增的整数列
       username = models.CharField(max_length=32)

   class Group(models.Model):
       # 自定义自增列
       nid = models.AutoField(primary_key=True)
       name = models.CharField(max_length=32)

SmallIntegerField(IntegerField):
   - 小整数 -32768 ~ 32767

PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
   - 正小整数 0 ~ 32767
IntegerField(Field)
   - 整数列(有符号的) -2147483648 ~ 2147483647

PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
   - 正整数 0 ~ 2147483647

BigIntegerField(IntegerField):
   - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

自定义无符号整数字段

   class UnsignedIntegerField(models.IntegerField):
       def db_type(self, connection):
           return 'integer UNSIGNED'

   PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
       'AutoField': 'integer AUTO_INCREMENT',
       'BigAutoField': 'bigint AUTO_INCREMENT',
       'BinaryField': 'longblob',
       'BooleanField': 'bool',
       'CharField': 'varchar(%(max_length)s)',
       'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
       'DateField': 'date',
       'DateTimeField': 'datetime',
       'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
       'DurationField': 'bigint',
       'FileField': 'varchar(%(max_length)s)',
       'FilePathField': 'varchar(%(max_length)s)',
       'FloatField': 'double precision',
       'IntegerField': 'integer',
       'BigIntegerField': 'bigint',
       'IPAddressField': 'char(15)',
       'GenericIPAddressField': 'char(39)',
       'NullBooleanField': 'bool',
       'OneToOneField': 'integer',
       'PositiveIntegerField': 'integer UNSIGNED',
       'PositiveSmallIntegerField': 'smallint UNSIGNED',
       'SlugField': 'varchar(%(max_length)s)',
       'SmallIntegerField': 'smallint',
       'TextField': 'longtext',
       'TimeField': 'time',
       'UUIDField': 'char(32)',

BooleanField(Field)
   - 布尔值类型

NullBooleanField(Field):
   - 可以为空的布尔值

CharField(Field)
   - 字符类型
   - 必须提供max_length参数, max_length表示字符长度

TextField(Field)
   - 文本类型

EmailField(CharField):
   - 字符串类型,Django Admin以及ModelForm中提供验证机制

GenericIPAddressField(Field)
   - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
   - 参数:
       protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
       unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both"

URLField(CharField)
   - 字符串类型,Django Admin以及ModelForm中提供验证 URL

SlugField(CharField)
   - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)

CommaSeparatedIntegerField(CharField)
   - 字符串类型,格式必须为逗号分割的数字

UUIDField(Field)
   - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证

FilePathField(Field)
   - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
   - 参数:
           path,                      文件夹路径
           match=None,                正则匹配
           recursive=False,           递归下面的文件夹
           allow_files=True,          允许文件
           allow_folders=False,       允许文件夹

FileField(Field)
   - 字符串,路径保存在数据库,文件上传到指定目录
   - 参数:
       upload_to = ""      上传文件的保存路径
       storage = None      存储组件,默认django.core.files.storage.FileSystemStorage

ImageField(FileField)
   - 字符串,路径保存在数据库,文件上传到指定目录
   - 参数:
       upload_to = ""      上传文件的保存路径
       storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
       width_field=None,   上传图片的高度保存的数据库字段名(字符串)
       height_field=None   上传图片的宽度保存的数据库字段名(字符串)

DateTimeField(DateField)
   - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

DateField(DateTimeCheckMixin, Field)
   - 日期格式      YYYY-MM-DD

TimeField(DateTimeCheckMixin, Field)
   - 时间格式      HH:MM[:ss[.uuuuuu]]

DurationField(Field)
   - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

FloatField(Field)
   - 浮点型

DecimalField(Field)
   - 10进制小数
   - 参数:
       max_digits,小数总长度
       decimal_places,小数位长度

BinaryField(Field)
   - 二进制类型
           
参数
null                数据库中字段是否可以为空
db_column           数据库中字段的列名
db_tablespace
default             数据库中字段的默认值
primary_key         数据库中字段是否为主键
db_index            数据库中字段是否可以建立索引
unique              数据库中字段是否可以建立唯一索引
unique_for_date     数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month    数据库中字段【月】部分是否可以建立唯一索引
unique_for_year     数据库中字段【年】部分是否可以建立唯一索引

auto_now			创建时,自动生成时间
auto_now_add		更新时,自动更新时间
					如:
					ctime = models.DateTimeField(auto_now_add=True, null=True)
    				uptime = models.DateTimeField(auto_now=True, null=True)
    				自动更新时:1更新时间不会发生变化,2有效,所以要用第二种
    				写法一:
    				obj = UserGroup.objects.filter(id=1).update(caption="CEO")
    				写法二:
    				obj = UserGroup.objects.filter(id=1).first()
    				obj.caption="CEO"
    				obj.save()
    	

verbose_name        Admin中显示的字段名称
blank               Admin中是否允许用户输入为空
editable            Admin中是否可以编辑
help_text           Admin中该字段的提示信息
choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
                    如:
                    user_type_choices = (
				        (1, '超级用户'),
				        (1, '普通用户'),
				        (1, '普普通用户'),
				    )
				    user_type_id = models.IntegerField(choices=user_type_choices, default=1)

error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;
                    字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
                    如:{'null': "不能为空.", 'invalid': '格式错误'}

validators          自定义错误验证(列表类型),从而定制想要的验证规则
                    from django.core.validators import RegexValidator
                    from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
                    MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
                    如:
                       test = models.CharField(
                           max_length=32,
                           error_messages={
                               'c1': '优先错信息1',
                               'c2': '优先错信息2',
                               'c3': '优先错信息3',
                           },
                           validators=[
                               RegexValidator(regex='root_\d+', message='错误了', code='c1'),
                               RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
                               EmailValidator(message='又错误了', code='c3'), ]
                       )
           

写好类之后,查看settings.py中的app是否注册了,如果注册,执行命令,生成数据库表

#django生成表结构、修改表结构
python3 manage.py makemigrations
python3 manage.py migrate
           

2、根据类对数据库表中的数据进行操作

# 创建
    # 1、
    models.UserInfo.objects.create(username='root', password='123')
    #models.UserGroup.objects.create(caption="dba")
    
    # 2、
    dic = {'username': 'test', 'password': '123'}
    models.UserInfo.objects.create(**dic)

    # 3、
    obj = models.UserInfo(username='severen', password="123")
    obj.save()

    # 查
    result = models.UserInfo.objects.all()
    result是一个QuerySet类型,这个类型是Django提供的,我们去理解的就话,就当它是一个列表即可,列表内的元素是UserInfo的对象
    result = models.UserInfo.objects.filter(username='root')
    result = models.UserInfo.objects.filter(username='root', password='123')
    for row in result:
        print(row.id, row.username, row.password)
	obj = models.UserInfo.objects.filter(id=nid).first()
    # 取单条数据,如果不存在,就直接报错
    # models.UserInfo.objects.get(id=nid)

    # 删除
    models.UserInfo.objects.all().delete()
    models.UserInfo.objects.filter(id=4).delete()

    # 更新
    models.UserInfo.objects.all().update(password='666')
    # gte -> 大于等于;lte -> 小于等于
    models.UserInfo.objects.filter(id__gt=1).update(password='666') # id > 1
           

2.外键操作

# UserInfo表中的user_group字段关联UserGroup表的主键,建立外键关系
# 数据库里面会自动生成字段名user_group_id
user_group = models.ForeignKey("UserGroup", to_field='uid', default=1, on_delete=models.CASCADE) # 如果没有指定to_field字段,默认和关联表的主键关联
           

在进行操作的时候,可以取到obj.user_group和obj.user_group_id

obj.user_group是一个对象

obj.user_group_id是数值

3.获取单表单数据的三种方式

# way one
v1 = models.Business.objects.all()
# QuerySet, 内部元素是对象
# [obj(id, caption, code), obj, obj...]

# way two
v2 = models.Business.objects.all().values('id', 'caption')
# QuerySet, 内部元素是字典
# [{'id':1, 'caption':'运维部'},{},{}...]

# way three
v3 = models.Business.objects.all().values_list('id', 'caption')
# QuerySet, 内部元素是元组
# [(1, '运维部'),(2, '开发'),()...]
           
<h1>业务线列表(对象)</h1>
<ul>
    {% for row in v1 %}
        <li>
            {{row.id}}-{{row.caption}}-{{row.code}}
        </li>
    {% endfor %}
</ul>
<h1>业务线列表(字典)</h1>
<ul>
    {% for row in v2 %}
        <li>
            {{row.id}}-{{row.caption}}
        </li>
    {% endfor %}
</ul>
<h1>业务线列表(元组)</h1>
<ul>
    {% for row in v3 %}
        <li>
            {{row.0}}-{{row.1}}
        </li>
    {% endfor %}
</ul>
           

4.获取一对多数据的的三种方式

# way 1
v1 = models.Host.objects.all()

# 双下划线 way 2
v2 = models.Host.objects.filter(nid__gt=0).values('nid', 'hostname', 'ip', 'port', 'b_id', 'b__caption')
for row in v2:
    print(row["nid"],row["hostname"],row["ip"],row["port"],row["b_id"],row["b__caption"])
    
# way 3
v3 = models.Host.objects.filter(nid__gt=0).values_list('nid', 'hostname', 'ip', 'port', 'b_id', 'b__caption')
           
<h1>主机列表(对象)</h1>
<div>
    <input id='add_host' type='button' value='添加' />
</div>
<table border='1'>
    <thead>
        <tr>
            <th>序号</th>
            <th>主机名</th>
            <th>IP</th>
            <th>端口</th>
            <th>业务线名称</th>
            <th>操作</th>
        </tr>
    </thead>
    <tbody>
        {% for row in v1 %}
            <tr hid='{{row.nid}}' bid='{{row.b_id}}'>
                <td>{{forloop.counter}}</td> <!--forloop.counter: for循环的计数器,从1开始,循环一次记+1,counter0:从0开始, revcounter, revcounter0:反转, last, first:是否是最后一个,第一个-->
                <td>{{row.hostname}}</td>
                <td>{{row.ip}}</td>
                <td>{{row.port}}</td>
                <td>{{row.b.caption}}</td>
                <td>
                    <a class='edit'>编辑</a>|<a class='delete'>删除</a>
                </td>
            </tr>
        {% endfor %}
    </tbody>
</table>
<h1>主机列表(字典)</h1>
<table border='1'>
    <thead>
        <tr>
            <th>主机名</th>
            <th>IP</th>
            <th>端口</th>
            <th>业务线名称</th>
        </tr>
    </thead>
    <tbody>
        {% for row in v2 %}
            <tr hid='{{row.nid}}' bid='{{row.b_id}}'>
                <td>{{row.hostname}}</td>
                <td>{{row.ip}}</td>
                <td>{{row.port}}</td>
                <td>{{row.b__caption}}</td>
            </tr>
        {% endfor %}
    </tbody>
</table>
<h1>主机列表(元组)</h1>
<table border='1'>
    <thead>
        <tr>
            <th>主机名</th>
            <th>IP</th>
            <th>端口</th>
            <th>业务线名称</th>
        </tr>
    </thead>
    <tbody>
        {% for row in v3 %}
            <tr hid='{{row.0}}' bid='{{row.4}}'>
                <td>{{row.1}}</td>
                <td>{{row.2}}</td>
                <td>{{row.3}}</td>
                <td>{{row.5}}</td>
            </tr>
        {% endfor %}
    </tbody>
</table>
           

5.多对多关系

创建多对多

way1:自定义关系表

优点:想生成多少列数据就多少列

缺点:需要自己写第三张表

class Host(models.Model):
    nid = models.AutoField(primary_key=True)
    hostname = models.CharField(max_length=32, db_index=True) # db_index: 索引
    ip = models.GenericIPAddressField(protocol="both", db_index=True) # protocol: 支持ipv4还是ipv6
    port = models.IntegerField()
    b = models.ForeignKey("Business", to_field="id", on_delete=models.CASCADE)

class Application(models.Model):
    name = models.CharField(max_length=32)

class HostToApp(models.Model):
    hobj = models.ForeignKey(to="Host", to_field="nid", on_delete=models.CASCADE)
    aobj = models.ForeignKey(to='Application', to_field='id', on_delete=models.CASCADE)
           

way2:自动创建关系表

优点:不要自己写第三张表,自动生成

缺点:最多生成三列数据类型

class Host(models.Model):
    nid = models.AutoField(primary_key=True)
    hostname = models.CharField(max_length=32, db_index=True) # db_index: 索引
    ip = models.GenericIPAddressField(protocol="both", db_index=True) # protocol: 支持ipv4还是ipv6
    port = models.IntegerField()
    b = models.ForeignKey("Business", to_field="id", on_delete=models.CASCADE)

class Application(models.Model):
    name = models.CharField(max_length=32)
    r = models.ManyToManyField("Host") # django将自动帮助创建关联表
           

对第三张表的数据操作

way1

# 通过类来操作数据库
obj = models.HostToApp.objects.create(hobj=1, aobj=2)
           

way2

obj = models.Application.objects.get(id=1)
obj.name

# 添加
obj.r.add(1)
obj.r.add(2)
obj.r.add(3,4,5)
obj.r.add(*[1,2,3,4])

# 删除
obj.r.remove(1)
obj.r.remove(2,3)
obj.r.remove(*[1,2,3])

# 清空
obj.r.clear()

# 更新
obj.r.set([3,4,5]) # 清空之前的所有,变成我现在设置的这个

# 获取值
obj.r.all() # 所有相关的主机对象"列表" QuerySet
           

5、视图获取用户请求相关信息以及请求头

先查看request这个对象的类型

得到

导入这个类,进入这个类查看信息

from django.core.handlers.wsgi import WSGIRequest
           

会发现有一个environ变量,这个变量封装了所有了的请求信息

6、分页

为保证网页的安全性,防止别人恶搞我们的网站,比如给我们的网站评论一堆html代码,该代码能导致页面进入死循环,我们有两种方法,

1、前端

{{ page_msg|safe }}
           

2、后端

from django.utils.safestring import mark_safe

page_msg = """
	<a>dsdaad</a>
	<h1>dahuadhudahuad</h1>"""
page_msg = mark_safe(page_msg)
           

自定义分页

1、设定每页显示数据条数

2、用户输入页码(第一页、第二页…)

3、设定显示多少页号

4、获取当前数据总条数

5、根据设定显示多少页号和数据总条数计算出,总页数

6、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置

7、在数据表中根据起始位置取值,页面上输出数据

8、输出分页html,如:[上一页][1][2][3][4][5][下一页]

views.py

from django.utils.safestring import mark_safe

LIST = []
for i in range(199):
    LIST.append(i)

def usr_list(request):
    all_count = len(LIST)
    per_page_count = 20
    total_html_count = 9
    current_page = int(request.GET.get('p', 1))
    total_page_count, y = divmod(all_count, per_page_count) # 商和余数
    if y:
        total_page_count += 1
    print(total_page_count)

    if total_page_count < total_html_count: # 总页数<能显示的页数
        start_index = 1
        end_index = total_page_count + 1
    else:
        if current_page <= (total_html_count+1)/2: # 当前页<=能显示的页数+1的一半
            start_index = 1
            end_index = total_html_count + 1
        else:
            if current_page + (total_html_count-1)/2 > total_page_count: # 当前页+能显示的页数-1的一半 > 总页数
                start_index = total_page_count - total_html_count + 1
                end_index = total_page_count + 1
            else:
                start_index = current_page - (total_html_count-1)/2
                end_index = current_page + (total_html_count+1)/2
    print(start_index,end_index)

    start = (current_page - 1) * per_page_count
    end = current_page * per_page_count
    data = LIST[int(start): int(end)]

    page_list = []
    if current_page-1 < 1:
        prev = "<a class='page' href='/usr_list/?p=1'>上一页</a>"
    else:
        prev = "<a class='page' href='/usr_list/?p=%s'>上一页</a>" % str(current_page-1)
    page_list.append(prev)
    for i in range(int(start_index), int(end_index)):
        if current_page == i:
            temp = "<a class='page active' href='/usr_list/?p=%s'>%s</a>" % (i, i)
        else:
            temp = "<a class='page' href='/usr_list/?p=%s'>%s</a>" % (i, i)
        page_list.append(temp)

    if current_page+1 > total_page_count:
        nex = "<a class='page' href='/usr_list/?p=%s'>下一页</a>" % total_page_count
    else:
        nex = "<a class='page' href='/usr_list/?p=%s'>下一页</a>" % str(current_page + 1)
    page_list.append(nex)

    jump = """<input type='text' /> <a οnclick="jumpTo(this, '/usr_list/?p=');">Go</a>
                <script>
                    function jumpTo(ths, base){
                        var val = ths.previousElementSibling.value;
                        location.href = base + val;
                    }
                </script>
        """
    page_list.append(jump)
    page_str = "".join(page_list)
    page_str = mark_safe(page_str)


    return render(request, "usr_list.html", {'data': data, 'page_str': page_str})
           

usr_list.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>usr_list</title>
    <style>
        .pagination .page{
            display: inline-block;
            padding: 5px;
            margin: 5px;
            background-color: yellow;
        }
        .pagination .page.active{
            background-color: red;
        }
    </style>
</head>
<body>
<ul>
    {% for i in data %}
        <li>{{i}}</li>
    {% endfor %}
</ul>
<div class="pagination">
    {{ page_str }}
</div>
</body>
</html>
           

不够规范,把上面的内容封装到一个类里面去

在app同级目录下新建一个文件夹utils

新建文件pagination.py,把类放进去

# Author: 73

from django.utils.safestring import mark_safe

class Page:
    def __init__(self, current_page, all_count, per_page_count=20, total_html_count=9):
        self.current_page = current_page
        self.all_count = all_count
        self.per_page_count = per_page_count
        self.total_html_count = total_html_count

    @property
    def total_page_count(self):
        x, y = divmod(self.all_count, self.per_page_count)  # 商和余数
        if y:
            x += 1
        print(x)
        return x
    @property
    def start(self):
        start = (self.current_page - 1) * self.per_page_count
        return start
    @property
    def end(self):
        end = self.current_page * self.per_page_count
        return end

    def page_str(self):
        if self.total_page_count < self.total_html_count:  # 总页数<能显示的页数
            start_index = 1
            end_index = self.total_page_count + 1
        else:
            if self.current_page <= (self.total_html_count + 1) / 2:  # 当前页<=能显示的页数+1的一半
                start_index = 1
                end_index = self.total_html_count + 1
            else:
                if self.current_page + (self.total_html_count - 1) / 2 > self.total_page_count:  # 当前页+能显示的页数-1的一半 > 总页数
                    start_index = self.total_page_count - self.total_html_count + 1
                    end_index = self.total_page_count + 1
                else:
                    start_index = self.current_page - (self.total_html_count - 1) / 2
                    end_index = self.current_page + (self.total_html_count + 1) / 2
        print(start_index, end_index)

        page_list = []
        if self.current_page - 1 < 1:
            prev = "<a class='page' href='/usr_list/?p=1'>上一页</a>"
        else:
            prev = "<a class='page' href='/usr_list/?p=%s'>上一页</a>" % str(self.current_page - 1)
        page_list.append(prev)
        for i in range(int(start_index), int(end_index)):
            if self.current_page == i:
                temp = "<a class='page active' href='/usr_list/?p=%s'>%s</a>" % (i, i)
            else:
                temp = "<a class='page' href='/usr_list/?p=%s'>%s</a>" % (i, i)
            page_list.append(temp)

        if self.current_page + 1 > self.total_page_count:
            nex = "<a class='page' href='/usr_list/?p=%s'>下一页</a>" % self.total_page_count
        else:
            nex = "<a class='page' href='/usr_list/?p=%s'>下一页</a>" % str(self.current_page + 1)
        page_list.append(nex)

        jump = """<input type='text' /> <a οnclick="jumpTo(this, '/usr_list/?p=');">Go</a>
                    <script>
                        function jumpTo(ths, base){
                            var val = ths.previousElementSibling.value;
                            location.href = base + val;
                        }
                    </script>
            """
        page_list.append(jump)
        page_str = "".join(page_list)
        page_str = mark_safe(page_str)

        return page_str
           

views.py导入这个类

from utils import pagination

def usr_list(request):
    all_count = len(LIST)
    current_page = int(request.GET.get('p', 1))

    page_obj = pagination.Page(current_page, all_count)

    data = LIST[int(page_obj.start): int(page_obj.end)]

    return render(request, "usr_list.html", {'data': data, 'page_str': page_obj.page_str()})
           

7、跨站请求伪造

一、简介

django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。

全局:中间件 django.middleware.csrf.CsrfViewMiddleware

局部:

@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。

@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

注:from django.views.decorators.csrf import csrf_exempt,csrf_protect

二、应用

1、普通表单

veiw中设置返回值:
     return render(request, 'xxx.html', data)
  
html中设置Token:
  {% csrf_token %}
           

2、Ajax

$(function(){
    /*$.ajaxSetup({ // 全局配置, 对整个页面所有的ajax做一个配置
        beforeSend: function(xhr, settings){ //表示在发送ajax请求之前,先执行这个函数
        	//xht:xmlhttprequest对象,所有的ajax操作的底层都是它来做的
            xht.setRequestHeader("X-CSRFtoken", $.cookie('csrftoken'));
        }
    })*/

	function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        }
    });

    $('#btn').click(function(){
        $.ajax({
            url: '/login/',
            type: "POST",
            data: {"user": "root", "pwd": "123"},
            //headers: {"X-CSRFtoken": $.cookie('csrftoken')}, // 请求头中不能有下划线
            success: function(arg){

            }
        });
    });
});
           

8、Cookie

cookie的原理就是保存在用户浏览器的键值对

django提供Cookie的一些方法

request.COOKIES
request.COOKIES['username111']
request.COOKIES.get('username111')

response = render(request, 'index.html')
response = redirect('/index/')

# 设置cookie,关闭浏览器失效
response.set_cookie("key", 'value')

# 设置cookie,N秒之后失效
response.set_cookie("key", 'value', max_age=10)

# 设置cookie,截止时间失效
import datetime
current_time = datetime.datetime.utcnow()
current_time = current_time + datetime.timedelta(seconds=10)
response.set_cookie('key', 'value', expires=current_time)

# 带签名的cookie
obj = HttpResponse('ok')
obj.set_signed_cookie('username', 'kangbazi', salt='asdfasdf') # 加密
request.get_signed_cookie('username', salt="asdfasdf") # 解密
           

设置Cookie

rep = HttpResponse(...) 或 rep = render(request, ...)
 
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐',...)
    参数:
        key,              键
        value='',         值
        max_age=None,     超时时间
        expires=None,     超时时间,以datetime为单位
        path='/',         Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问
        domain=None,      Cookie生效的域名
        secure=False,     https传输
        httponly=False    只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
           
def login(request):
    if request.method == "GET":
        return render(request, 'login.html')
    if request.method == "POST":
        u = request.POST.get('username')
        p = request.POST.get('pwd')
        print("login", u)
        dic = user_info.get(u)
        if not dic:
            return render(request, 'login.html')
        elif dic['p'] == p:
            res = redirect('/index/') # ⚠️这行代码
            res.set_cookie('username111', u, max_age=10) # 设置,max_age:超时时间
            return res
        else:
            return render(request, 'login.html')
           

获取Cookie

request.COOKIES.get('key')
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
    参数:
        default: 默认值
           salt: 加密盐
        max_age: 后台控制过期时间
           
def index(request):
    username = request.COOKIES.get("username111") # 获取
    print("index", username)
    if not username:
        return render(request, 'login.html')
    return render(request, 'index.html', {"username": username})
           

9、Session

session是保存在服务器端的键值对

session原理

首先在服务器端有一个字典

用户来登陆,登陆成功之后,会生成一个随机的字符串给用户

同时会把这个随机生成的字符串当作key放在大字典里,它的value是一个字典,该字典内容是用户的敏感信息,比如密码

⚠️注意:在Django中使用session前,需要migrate一下,因为Django默认情况下会把session保存在数据库里面

例子:

def login(request):
    if request.method == "GET":
        return render(request, "login.html")
    elif request.method == "POST":
        user = request.POST.get("username")
        pwd = request.POST.get("pwd")
        if user == 'root' and pwd == '123':
            # 生成随机字符串
            # 写到用户浏览器cookie
            # 保存到session中
            # 在随机字符串对应的字典中设置相关内容。。。
            request.session['username'] = user
            request.session['is_login'] = True
            return redirect('/index/')
        else:
            return render(request, "login.html")

@csrf_exempt # 表示这个功能不需要验证了
def index(request):
    # session中获取值
    if request.session.get("is_login", None):
        return render(request, "index.html")
    else:
        return HttpResponse("gun")
           

session可以保存在很多地方,比如说数据库、内存、缓存、文件等,具体保存在哪,在Django中可以进行配置。

1、数据库Session

Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
 
a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
     
    SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
    SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
    SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
    SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
    SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
    SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
    SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认),这个设置为True,超时时间从当前操作开始算
 
 
 
b. 使用
 
    def index(request):
        # 获取、设置、删除Session中数据
        request.session['k1']
        request.session.get('k1',None)
        request.session['k1'] = 123
        request.session.setdefault('k1',123) # 存在则不设置
        del request.session['k1']
 
        # 所有 键、值、键值对
        request.session.keys()
        request.session.values()
        request.session.items()
        request.session.iterkeys()
        request.session.itervalues()
        request.session.iteritems()
 
 
        # 用户session的随机字符串
        request.session.session_key
 
        # 将所有Session失效日期小于当前日期的数据删除
        request.session.clear_expired()
 
        # 检查 用户session的随机字符串 在数据库中是否
        request.session.exists("session_key")
 
        # 删除当前用户的所有Session数据
        request.session.delete("session_key")

		request.session.clear() == request.session.delete(request.session.session_key)
 
        request.session.set_expiry(value)
            * 如果value是个整数,session会在value秒数后失效。
            * 如果value是个datatime或timedelta,session就会在这个时间后失效。
            * 如果value是0,用户关闭浏览器session就会失效。
            * 如果value是None,session会依赖全局session失效策略。
           

2、缓存Session

a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
    SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
 
 
    SESSION_COOKIE_NAME = "sessionid"                        # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
    SESSION_COOKIE_PATH = "/"                                # Session的cookie保存的路径
    SESSION_COOKIE_DOMAIN = None                              # Session的cookie保存的域名
    SESSION_COOKIE_SECURE = False                             # 是否Https传输cookie
    SESSION_COOKIE_HTTPONLY = True                            # 是否Session的cookie只支持http传输
    SESSION_COOKIE_AGE = 1209600                              # Session的cookie失效日期(2周)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                   # 是否关闭浏览器使得Session过期
    SESSION_SAVE_EVERY_REQUEST = False                        # 是否每次请求都保存Session,默认修改之后才保存
 
 
 
b. 使用
 
    同上
           

3、文件Session

a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
    SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()                                                            # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T
 
 
    SESSION_COOKIE_NAME = "sessionid"                          # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
    SESSION_COOKIE_PATH = "/"                                  # Session的cookie保存的路径
    SESSION_COOKIE_DOMAIN = None                                # Session的cookie保存的域名
    SESSION_COOKIE_SECURE = False                               # 是否Https传输cookie
    SESSION_COOKIE_HTTPONLY = True                              # 是否Session的cookie只支持http传输
    SESSION_COOKIE_AGE = 1209600                                # Session的cookie失效日期(2周)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                     # 是否关闭浏览器使得Session过期
    SESSION_SAVE_EVERY_REQUEST = False                          # 是否每次请求都保存Session,默认修改之后才保存
 
b. 使用
 
    同上
           

4、缓存+数据库Session

数据库用于做持久化,缓存用于提高效率
 
a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎
 
b. 使用
 
    同上
           

5、加密cookie Session

a. 配置 settings.py
     
    SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎
 
b. 使用
 
    同上
           

10、中间件

在django中,中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。

在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件,如下

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',
    'Middle.m1.Row1',
    'Middle.m1.Row2',
    'Middle.m1.Row3',
]
           

中间件中可以定义四个方法,分别是:

  • process_request(self,request)
  • process_view(self, request, callback, callback_args, callback_kwargs)
  • process_exception(self, request, exception):出现异常执行
  • process_response(self, request, response)

    process_template_response(self,request,response):如果views中的函数返回的对象中,具有render方法时执行

    比如

    Django重要知识点1、Django获取多个数据2、Django文件上传3、CBV和FBV4、DjangoORM5、视图获取用户请求相关信息以及请求头6、分页7、跨站请求伪造8、Cookie9、Session10、中间件11、缓存12、信号

以上方法的返回值可以是None和HttpResonse对象,如果是None,则继续按照django定义的规则向下执行,如果是HttpResonse对象,则直接将该对象返回给用户。

django1.10之前的版本流程:

Django重要知识点1、Django获取多个数据2、Django文件上传3、CBV和FBV4、DjangoORM5、视图获取用户请求相关信息以及请求头6、分页7、跨站请求伪造8、Cookie9、Session10、中间件11、缓存12、信号

django1.10之后的版本流程:

Django重要知识点1、Django获取多个数据2、Django文件上传3、CBV和FBV4、DjangoORM5、视图获取用户请求相关信息以及请求头6、分页7、跨站请求伪造8、Cookie9、Session10、中间件11、缓存12、信号

=自定义中间件

1、创建中间件类

先在manage.py同级目录下创建一个文件夹,取名随意,比如Middle

在该文件夹下新建一个python文件,比如m1.py

from django.utils.deprecation import MiddlewareMixin

class Row1(MiddlewareMixin):
    def process_request(self, request):
        print("第一排")

    def process_view(self, request, view_func, view_func_args, view_func_kwargs):
        print('13')

    def process_response(self, request, response):
        print("第一排2")
        return response

# from django.shortcuts import HttpResponse
class Row2(MiddlewareMixin):
    def process_request(self, request):
        print("第二排")
        # return HttpResponse("走")

    def process_view(self, request, view_func, view_func_args, view_func_kwargs):
        print('23')

    def process_response(self, request, response):
        print("第二排2")
        return response

class Row3(MiddlewareMixin):
    def process_request(self, request):
        print("第三排")

    def process_view(self, request, view_func, view_func_args, view_func_kwargs):
        print('33')

    def process_response(self, request, response):
        print("第三排2")
        return response
           

2、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',
    'Middle.m1.Row1',
    'Middle.m1.Row2',
    'Middle.m1.Row3',
]
           

11、缓存

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

Django中提供了6种缓存方式:

  • 开发调试
  • 内存
  • 文件
  • 数据库
  • Memcache缓存(python-memcached模块)
  • Memcache缓存(pylibmc模块)

1、配置

a、开发调试

# 此为开始调试用,实际内部不做任何操作
# 配置:
    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(默认3)
            },
            'KEY_PREFIX': '',                                             # 缓存key的前缀(默认空)
            'VERSION': 1,                                                 # 缓存key的版本(默认1)
            'KEY_FUNCTION' 函数名                                          # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
        }
    }
           

b、内存

# 此缓存将内容保存至内存的变量中
# 配置:
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            'LOCATION': 'unique-snowflake',
        }
    }

# 注:其他配置同开发调试版本
           

c、文件

# 此缓存将内容保存至文件
# 配置:

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
            'LOCATION': '/var/tmp/django_cache', # 'LOCATION': os.path.join(BASE_DIR, 'cache'), 需要在项目中新建一个cache文件夹
        }
    }
# 注:其他配置同开发调试版本
           

d、数据库

# 此缓存将内容保存至数据库

    # 配置:
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
                'LOCATION': 'my_cache_table', # 数据库表
            }
        }

    # 注:执行创建表命令 python manage.py createcachetable
           

e、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',
        ]
    }
}
           

f、Memcache缓存(pylibmc模块)

# 此缓存使用pylibmc模块连接memcache

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',
        ]
    }
}
           

g. Redis缓存(依赖:pip3 install django-redis)

settings.py
	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": "密码",
	        }
	    }
	}

views.py
	from django_redis import get_redis_connection
	conn = get_redis_connection("default")
           

2、应用

a. 全站使用

适合场景:个人博客页面,不允许别人点赞、评论

使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存

MIDDLEWARE = [
    'django.middleware.cache.UpdateCacheMiddleware',
    # 其他中间件...
    'django.middleware.cache.FetchFromCacheMiddleware',
]
           

b. 单独视图缓存

方式一:
    from django.views.decorators.cache import cache_page

    @cache_page(60 * 15)
    def my_view(request):
        ...

方式二:
    from django.views.decorators.cache import cache_page

    urlpatterns = [
        re_path('^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),
    ]
           

c、局部视图使用

a. 引入TemplateTag

    {% load cache %}

b. 使用缓存

    {% cache 5000 缓存key %}
        缓存内容
    {% endcache %}
           

12、信号

通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者。

应用场景:对所有在数据库创建数据的前后给它记录一条记录

1、Django内置信号

Model signals
    pre_init                    # django的models执行其构造方法前,自动触发
    post_init                   # django的models执行其构造方法后,自动触发
    pre_save                    # django的models对象保存前,自动触发
    post_save                   # django的models对象保存后,自动触发
    pre_delete                  # django的models对象删除前,自动触发
    post_delete                 # django的models对象删除后,自动触发
    m2m_changed                 # django的models中使用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          # 创建数据库连接时,自动触发
           
from django.core.signals import request_finished
from django.dispatch import receiver

@receiver(request_finished)
def my_callback(sender, **kwargs):
    print("Request finished!")
           

对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数。

在项目中新建一个sg.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 callback(sender, **kwargs):
    print("xxoo_callback")
    #print(sender,kwargs)

# xxoo.connect(callback)
# xxoo指上述导入的内容

pre_init.connect(callback)
           

在项目名目录下的__init__.py导入sg

import sg
           

2、自定义信号

  1. 定义信号
    import django.dispatch
    pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
               
  2. 注册信号
    def callback(sender, **kwargs):
        print("callback")
        print(sender,kwargs)
     
    pizza_done.connect(callback)
               
  3. 触发信号
    from sg import pizza_done
    
    pizza_done.send(sender="severen", toppings=123, size=456)
               

项目地址:

https://github.com/Stark-Xue/test2

https://github.com/Stark-Xue/test3