使用elasticsearch dsl py高级库的模型和助手的django elasticsearch包装器。
django-es的Python项目详细描述
django es
==
目录::目录
:深度:2
purpose
==
django es是
`elasticsearch dsl py<;https://github.com/elasticsearch/elasticsearch dsl py>;`\br/>
你会发现很多共同点。
最大的变化是它使用注册管理作为一种理念,而不是django管理器。
所以很多代码被删除了,并且有很多更改。
没有别名,没有管理命令。
关于使用ElasticSearch 5.x及其限制(与一个唯一映射定义相关的唯一字段名)。
CRUD操作主要由ElasticSearch DSL库完成,以获得更多的控制和可维护性。
features
==
-django model mapping
-非常简单映射(无谎言)。
-自动模型映射(并通过返回"elasticsearch dsl py"的"result"实例来支持未定义的模型)。
-django admin register like
-将模型注册为django admin contribution
.
-django信号
-连接到elasticsearch的pre-save、post-save和pre-delete信号
索引,以便随时正确地反映数据库。
-requirements
-django>;=1.8
-python 2.7(**还不支持python 3**)
安装
==
正在理货。直接向我报告说,安装django es可能会升级您的django版本,尽管我自己还无法确认。django es依赖于django 1.8及以上版本。
es搜索索引
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
搜索索引定义django es如何序列化每个
模型的对象。它有效地定义了对象的序列化方式以及elasticsearch索引的结构。
现在,您应该第一次运行服务器,以允许autodiscover
模块生成映射并与elasticsearch
服务器通信。
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————/>将"django-es"添加到"installed-apps"中。
您可以在自己的代码中定义一个用于连接ElasticSearch实例的"es-client"参数,
默认情况下,"es-client"是"elasticSearch()`
示例
----
代码::python
from django.db import models
from django.core.urlsolvers import reverse
from autoslug import autoslugfield
from wall.models import wall
from category.models import category
AME=models.charfield(max_length=128,null=true,blank=true)
created=models.datetimefield(auto_now_add=true)
wall=models.foreignkey(wall,related_name='mymodels',null=true,blank=true)
slug=autoslugfield(populate撸from='populate_slug',unique=true)
Last_modified=models.datetimefield(auto_now_add=true)
is_finalized=models.booleanfield(默认值=false)
is_recorded=models.booleanfield(默认值=false)
desc=models.charfield(最大长度=4096,空值=true,空值=true)
diff_date=models.datetimefield()
duration=models.durationfield(空值=true,blank=true)
category=models.foreignkey(category)
def str(self):
return self.name
def get_u absolute_u url(self):
return reverse('video',kwargs={'slug':self.slug})
从父类
def populate_slug(self):
返回self.name或'mymodel'
class meta(media.meta):
app_label='media'
modelnindex
~~~~~~~~
elds
从"mymodel"开始,减去"mymodelmindex.meta.exclude"中定义的值。
生成映射时,每个字段都将是最合适的elds
"elasticsearch core
类型<;https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping types.html>;`\uu,
with defa结果属性(在django_es.fields中定义)。
代码::python
from django_es import mapping
from django_es.fields导入字符串、日期、整数
from django_es.index导入模型index
from media.models导入mymodel
from elasticsearch_dsl.analysis导入分析器
from utils.fields导入comp删除
class mymodelmodelindex(modelindex):
description=string(analyzer='snowball','u model='desc')
created=日期('u model=attr='created')
category=整数('u eval=as='obj.category.id')
img=string()
author=string()
建议=完成(
分析器=分析器('simple'),
搜索分析器=分析器('simple'),
保留位置增量=false,
保留分隔符=false,
有效载荷=true,
上下文={
'type':{
'type':'category',
'path':''u type'
}
}
class meta:
index='django'es';可选,但建议使用,默认值为'django'es',在dex`method
exclude=('last_modified','is_finalized','is_recorded','diff_date','duration',)
def prepare_img(self,obj):
返回myModelSerializer。_img(obj,'48x48')获取相关的图像传递res
def prepare_author(self,obj):
返回obj.wall.profile.get_full_name()
def prepare_suggest(self,obj):
在elasticsearch中,返回{
"input":[obj.name,obj.desc,obj.wall.profile.get_full_name()],
"output":obj.name+'-'+obj.wall.profile.get_full_name(),
"payload":{
"slug":obj.slug,
"img":self.prepare_img(obj),
"category":obj.category.id
}
}
mapping.register(mymodel,mymodelmodelnindex)
最后一行很重要,它允许django es创建与此模型相关的映射
并将其放到ElasticSearch服务器上。
模型字段
来自elasticsearch dsl.fields。
如果elasticsearch dsl
或此贡献尚未提供,则可以创建自己的字段。
。代码::python
'最大输入长度':{'type':'integer'}
}
name='完成'
def_empty(self):
return'
现在,要生成映射和索引,您需要第一次启动服务器。
降低这些
`elasticsearch mappings rules<;https://www.elastic.co/blog/changing mapping with zero dowtime>;` ` ` ` ` `,
r/>但对于面向api的网站或django表单,您可以直接使用
elasticsearch dsl方法,也可以简单地使用"utils"中定义的函数:
"update"index"和"delete"index"示例:
。代码::python
/>"`update-index``函数使用elasticsearch的``bulk`/``bulk-index``方法来连续执行
多个操作。
我们的文档使用ElasticSearchDSL方法。这是更简单的方法。
示例:
…代码::python
从elasticsearch导入elasticsearch
从elasticsearch导入dsl()
s=search()。使用(client)
fields=["name^2.0","description^1.5","author^1.0"]
s.query(multimatch(query=searchstr,type='best戋fields',fields=fields,tie戋breaker=0.3))
或
s.query('query戋string',query='和'.join(searchstr.split中x的x+'~2'(")",使用"dis-max=true,fields=fields,tie-breaker=0.3))
s.aggs.bucket('list','filter',term={''u type':'mymodel'})\
.metric('obj',
'top-hits',
**{''u source':['name','slug','img'],
'from':(int(page)-1)*20,"
"大小":20
}
)
s=s[:0]仅获取聚合结果
响应=s.execute()
count=response.aggregations.list.obj.hits.total
res=[x.\u source.to.\u dict()for x in response.aggregations.list.obj.hits.hits]
您还可以使用前面定义的"建议"字段:
。代码::python
from elasticsearch import elasticsearch
from elasticsearch\dsl import search
client=elasticsearch()
s=search()。使用(client)\
。建议('lives',searchstr completion={'字段":'suggest','fuzzy':true,'size':5,'context':{'type':'mymodel'})
s=s[:0]仅获取建议结果
response=s.execute()
results=[]
x in options:
d=x['payload']。to_dict()
d.更新(name=x.text)
结果。附加(d)
返回结果
lives=格式化结果(response.suggest.lives[0]['options'])
django设置
~~~~~~~~~~~~~~~
在signals中的代码和find为您的业务逻辑提供了灵感,
或者使用经典的"basedjangoessignalprocessor",它将在创建/更新/删除elasticsearch doctype对象之前使用100个对象的缓冲区。代码::python
django-es={
"信号类":"basedjangoessignalprocessor";默认值
}
r/>给定的django模型。也可以直接创建映射而不需要
模型,只需传递doctype即可。
django es要管理的任何django模型都必须具有已定义的
modelnindex子类。此子类必须包含名为
`` meta``的子类。
类属性
~~~~~~~~~~~~~~~~~~~~
>如下面所述,文档类型映射将包含与其相关的
模型中的字段。但是,通常需要为那些
对应于模型字段的串联或一些
逻辑操作的字段建立索引。
django es使这变得非常简单:只需将类属性定义为
任何核心类型,并将"eval"设置为"构造函数"参数
单行python语句。对象被引用为``obj``(不是
``self``也不是``object```,只是``obj```)。
强制要求(参见下文)。
…代码::python
from django_es.fields从django_es.index导入日期、字符串、整数ated')
category=integer(_eval_as='obj.category.id')
img=string()
def prepare_img(self,obj):
rn mymodelserializer.\u img(obj,'48x48')
这里,``img``将是doc
类型映射的一部分,但不会被反向映射,因为模型中不存在这些字段。
``description``和``created`date``使用``u model`attr`链接重新定义字段名。
``category``将被计算为与类别外键相关的整数。
代码::python
some_field_name=string(_eval_as=',".join([item for item in obj.some_foreign_relation.values_list("some_field",flat=true))(如果是obj.some_foreign_relation else"")
操作:
返回','.join([对象中项的项。某些外部关系。值列表("某些字段",flat=true))
返回'
生成。
带有动态日期的示例:
它将每天创建一个新索引。
…代码::python
def populate\u index(self):
return"我的索引名-%(现在)s"%{'now().strftime("%y.%m.%d")}
匹配"索引"条件
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
重写此函数以指定是否应索引项。这在定义给定模型的多个索引(和modelindex
类)。此方法的签名和超级类代码如下所示,并允许对所有项进行索引。代码::python
def匹配索引条件(self,item):
return true
例如,如果给定的elasticsearch索引只应包含标题以"awesome"开头的item
,则可以按如下方式重写此方法。代码::python
def与索引条件(self,item)匹配:
return item.title.startswith("awesome")
meta子类属性
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**注意**:在下面,定义为"list"的任何变量也可以是"tuple"。
fields
^^^^
*可选:*当
序列化ElasticSearch对象或将
对象从ElasticSearch反向映射回Django模型实例时,必须获取的字段(或列)列表。默认情况下,
将获取所有字段。设置此*将*限制可以获取哪些字段,并且在序列化对象时可能导致错误。建议改用``exclude``属性(参见下文)。
exclude
^^^^^
*可选:*在序列化或反序列化对象时不能获取的字段(或列)列表。
修补程序e键是索引字段,其值
是定义"core type
attributes<;https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping types.html>;`<;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/analysis analyzers.html>;` `
设置为
` ` ` `雪球'`<;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/analysis snowball analyzer.html>;`
(`{analyzer':`snowball'}`)。
传统字段
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*可选:*要获取用于映射的其他字段,可能是用于
``eval`a s`/``prepare`s``字段,也可能是在从数据库返回对象时。
*可选:*要用作唯一ID的模型字段r elasticsearch的
元数据"id"。默认为"id`"(也称为
``pk`<;https://docs.djangoproject.com/en/dev/topics/db/models/automatic primary key fields>;`` uu)。
settings
--
将"django es"添加到已安装的应用程序中。
~是一个字典(即使是空的),并将
连接到注册的*所有*模型的``pre-save``,``post-save`,``pre-delete``模型函数。
还可以通过将模块路径的字符串值放在一个键
cal下定义一个信号处理器类,以实现更自定义的功能。led ``signal_class``定义``setup``和``teardown``方法,
将``model`作为唯一参数。这些方法连接并断开信号
处理类到django信号的连接(信号连接到每个注册的模型上)。
代码::python
django-es={
'信号类':'.signals.djangoSignalProcessor'
}
代码::python
from django.db.models导入信号
y创建索引如果创建
如果创建:
更新索引([实例],发件人,批量大小=1)
@staticmethod
def pre_delete_connector(发件人,实例,**kwargs):
从django_es.utils导入delete_index_item
delete_index_item(instance,sender)
def setup(self,model):
signals.post_save.connect(self.post_save_connector,sender=model)
signals.pre_delete.connect(self.pre_delete_connector,sender=model)
def teardown(self,model):
信号。预删除。断开连接(self.pre_u delete_u connector,sender=model)
信号。后保存。断开连接(self.post_u save_u connector,sender=model)
缓冲区大小
^^^^^^^^^^^^^^^^^^
*可选:*表示在进行大容量索引更新之前要缓冲的项数的整数,默认为"100"。
**警告**:如果在缓冲区被清空之前关闭应用程序,则任何缓冲实例*都不会*在elasticsearch上被索引。因此,一个可能更好的实现是在芹菜任务中包装
``post-save`u connector``和`pre-delete`u connector``from
``django`es.signals``。为了不需要"芹菜",这里没有实现
。
==
目录::目录
:深度:2
purpose
==
django es是
`elasticsearch dsl py<;https://github.com/elasticsearch/elasticsearch dsl py>;`\br/>
你会发现很多共同点。
最大的变化是它使用注册管理作为一种理念,而不是django管理器。
所以很多代码被删除了,并且有很多更改。
没有别名,没有管理命令。
关于使用ElasticSearch 5.x及其限制(与一个唯一映射定义相关的唯一字段名)。
CRUD操作主要由ElasticSearch DSL库完成,以获得更多的控制和可维护性。
features
==
-django model mapping
-非常简单映射(无谎言)。
-自动模型映射(并通过返回"elasticsearch dsl py"的"result"实例来支持未定义的模型)。
-django admin register like
-将模型注册为django admin contribution
.
-django信号
-连接到elasticsearch的pre-save、post-save和pre-delete信号
索引,以便随时正确地反映数据库。
-requirements
-django>;=1.8
-python 2.7(**还不支持python 3**)
安装
==
正在理货。直接向我报告说,安装django es可能会升级您的django版本,尽管我自己还无法确认。django es依赖于django 1.8及以上版本。
es搜索索引
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
搜索索引定义django es如何序列化每个
模型的对象。它有效地定义了对象的序列化方式以及elasticsearch索引的结构。
现在,您应该第一次运行服务器,以允许autodiscover
模块生成映射并与elasticsearch
服务器通信。
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————/>将"django-es"添加到"installed-apps"中。
您可以在自己的代码中定义一个用于连接ElasticSearch实例的"es-client"参数,
默认情况下,"es-client"是"elasticSearch()`
示例
----
代码::python
from django.db import models
from django.core.urlsolvers import reverse
from autoslug import autoslugfield
from wall.models import wall
from category.models import category
AME=models.charfield(max_length=128,null=true,blank=true)
created=models.datetimefield(auto_now_add=true)
wall=models.foreignkey(wall,related_name='mymodels',null=true,blank=true)
slug=autoslugfield(populate撸from='populate_slug',unique=true)
Last_modified=models.datetimefield(auto_now_add=true)
is_finalized=models.booleanfield(默认值=false)
is_recorded=models.booleanfield(默认值=false)
desc=models.charfield(最大长度=4096,空值=true,空值=true)
diff_date=models.datetimefield()
duration=models.durationfield(空值=true,blank=true)
category=models.foreignkey(category)
def str(self):
return self.name
def get_u absolute_u url(self):
return reverse('video',kwargs={'slug':self.slug})
从父类
def populate_slug(self):
返回self.name或'mymodel'
class meta(media.meta):
app_label='media'
modelnindex
~~~~~~~~
elds
从"mymodel"开始,减去"mymodelmindex.meta.exclude"中定义的值。
生成映射时,每个字段都将是最合适的elds
"elasticsearch core
类型<;https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping types.html>;`\uu,
with defa结果属性(在django_es.fields中定义)。
代码::python
from django_es import mapping
from django_es.fields导入字符串、日期、整数
from django_es.index导入模型index
from media.models导入mymodel
from elasticsearch_dsl.analysis导入分析器
from utils.fields导入comp删除
class mymodelmodelindex(modelindex):
description=string(analyzer='snowball','u model='desc')
created=日期('u model=attr='created')
category=整数('u eval=as='obj.category.id')
img=string()
author=string()
建议=完成(
分析器=分析器('simple'),
搜索分析器=分析器('simple'),
保留位置增量=false,
保留分隔符=false,
有效载荷=true,
上下文={
'type':{
'type':'category',
'path':''u type'
}
}
class meta:
index='django'es';可选,但建议使用,默认值为'django'es',在dex`method
exclude=('last_modified','is_finalized','is_recorded','diff_date','duration',)
def prepare_img(self,obj):
返回myModelSerializer。_img(obj,'48x48')获取相关的图像传递res
def prepare_author(self,obj):
返回obj.wall.profile.get_full_name()
def prepare_suggest(self,obj):
在elasticsearch中,返回{
"input":[obj.name,obj.desc,obj.wall.profile.get_full_name()],
"output":obj.name+'-'+obj.wall.profile.get_full_name(),
"payload":{
"slug":obj.slug,
"img":self.prepare_img(obj),
"category":obj.category.id
}
}
mapping.register(mymodel,mymodelmodelnindex)
最后一行很重要,它允许django es创建与此模型相关的映射
并将其放到ElasticSearch服务器上。
模型字段
来自elasticsearch dsl.fields。
如果elasticsearch dsl
或此贡献尚未提供,则可以创建自己的字段。
。代码::python
'最大输入长度':{'type':'integer'}
}
name='完成'
def_empty(self):
return'
现在,要生成映射和索引,您需要第一次启动服务器。
降低这些
`elasticsearch mappings rules<;https://www.elastic.co/blog/changing mapping with zero dowtime>;` ` ` ` ` `,
r/>但对于面向api的网站或django表单,您可以直接使用
elasticsearch dsl方法,也可以简单地使用"utils"中定义的函数:
"update"index"和"delete"index"示例:
。代码::python
/>"`update-index``函数使用elasticsearch的``bulk`/``bulk-index``方法来连续执行
多个操作。
我们的文档使用ElasticSearchDSL方法。这是更简单的方法。
示例:
…代码::python
从elasticsearch导入elasticsearch
从elasticsearch导入dsl()
s=search()。使用(client)
fields=["name^2.0","description^1.5","author^1.0"]
s.query(multimatch(query=searchstr,type='best戋fields',fields=fields,tie戋breaker=0.3))
或
s.query('query戋string',query='和'.join(searchstr.split中x的x+'~2'(")",使用"dis-max=true,fields=fields,tie-breaker=0.3))
s.aggs.bucket('list','filter',term={''u type':'mymodel'})\
.metric('obj',
'top-hits',
**{''u source':['name','slug','img'],
'from':(int(page)-1)*20,"
"大小":20
}
)
s=s[:0]仅获取聚合结果
响应=s.execute()
count=response.aggregations.list.obj.hits.total
res=[x.\u source.to.\u dict()for x in response.aggregations.list.obj.hits.hits]
您还可以使用前面定义的"建议"字段:
。代码::python
from elasticsearch import elasticsearch
from elasticsearch\dsl import search
s=search()。使用(client)\
。建议('lives',searchstr completion={'字段":'suggest','fuzzy':true,'size':5,'context':{'type':'mymodel'})
s=s[:0]仅获取建议结果
response=s.execute()
x in options:
d=x['payload']。to_dict()
d.更新(name=x.text)
结果。附加(d)
返回结果
lives=格式化结果(response.suggest.lives[0]['options'])
django设置
~~~~~~~~~~~~~~~
在signals中的代码和find为您的业务逻辑提供了灵感,
或者使用经典的"basedjangoessignalprocessor",它将在创建/更新/删除elasticsearch doctype对象之前使用100个对象的缓冲区。代码::python
django-es={
"信号类":"basedjangoessignalprocessor";默认值
}
r/>给定的django模型。也可以直接创建映射而不需要
模型,只需传递doctype即可。
django es要管理的任何django模型都必须具有已定义的
modelnindex子类。此子类必须包含名为
`` meta``的子类。
类属性
~~~~~~~~~~~~~~~~~~~~
>如下面所述,文档类型映射将包含与其相关的
模型中的字段。但是,通常需要为那些
对应于模型字段的串联或一些
逻辑操作的字段建立索引。
django es使这变得非常简单:只需将类属性定义为
任何核心类型,并将"eval"设置为"构造函数"参数
单行python语句。对象被引用为``obj``(不是
``self``也不是``object```,只是``obj```)。
强制要求(参见下文)。
…代码::python
from django_es.fields从django_es.index导入日期、字符串、整数ated')
category=integer(_eval_as='obj.category.id')
img=string()
def prepare_img(self,obj):
rn mymodelserializer.\u img(obj,'48x48')
这里,``img``将是doc
类型映射的一部分,但不会被反向映射,因为模型中不存在这些字段。
``description``和``created`date``使用``u model`attr`链接重新定义字段名。
``category``将被计算为与类别外键相关的整数。
代码::python
some_field_name=string(_eval_as=',".join([item for item in obj.some_foreign_relation.values_list("some_field",flat=true))(如果是obj.some_foreign_relation else"")
操作:
返回','.join([对象中项的项。某些外部关系。值列表("某些字段",flat=true))
返回'
生成。
带有动态日期的示例:
它将每天创建一个新索引。
…代码::python
def populate\u index(self):
return"我的索引名-%(现在)s"%{'now().strftime("%y.%m.%d")}
匹配"索引"条件
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
重写此函数以指定是否应索引项。这在定义给定模型的多个索引(和modelindex
类)。此方法的签名和超级类代码如下所示,并允许对所有项进行索引。代码::python
def匹配索引条件(self,item):
return true
例如,如果给定的elasticsearch索引只应包含标题以"awesome"开头的item
,则可以按如下方式重写此方法。代码::python
def与索引条件(self,item)匹配:
return item.title.startswith("awesome")
meta子类属性
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**注意**:在下面,定义为"list"的任何变量也可以是"tuple"。
fields
^^^^
*可选:*当
序列化ElasticSearch对象或将
对象从ElasticSearch反向映射回Django模型实例时,必须获取的字段(或列)列表。默认情况下,
将获取所有字段。设置此*将*限制可以获取哪些字段,并且在序列化对象时可能导致错误。建议改用``exclude``属性(参见下文)。
exclude
^^^^^
*可选:*在序列化或反序列化对象时不能获取的字段(或列)列表。
修补程序e键是索引字段,其值
是定义"core type
attributes<;https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping types.html>;`<;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/analysis analyzers.html>;` `
设置为
` ` ` `雪球'`<;http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/analysis snowball analyzer.html>;`
(`{analyzer':`snowball'}`)。
传统字段
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*可选:*要获取用于映射的其他字段,可能是用于
``eval`a s`/``prepare`s``字段,也可能是在从数据库返回对象时。
*可选:*要用作唯一ID的模型字段r elasticsearch的
元数据"id"。默认为"id`"(也称为
``pk`<;https://docs.djangoproject.com/en/dev/topics/db/models/automatic primary key fields>;`` uu)。
settings
--
将"django es"添加到已安装的应用程序中。
连接到注册的*所有*模型的``pre-save``,``post-save`,``pre-delete``模型函数。
还可以通过将模块路径的字符串值放在一个键
cal下定义一个信号处理器类,以实现更自定义的功能。led ``signal_class``定义``setup``和``teardown``方法,
将``model`作为唯一参数。这些方法连接并断开信号
处理类到django信号的连接(信号连接到每个注册的模型上)。
代码::python
django-es={
'信号类':'.signals.djangoSignalProcessor'
}
代码::python
from django.db.models导入信号
y创建索引如果创建
如果创建:
更新索引([实例],发件人,批量大小=1)
@staticmethod
def pre_delete_connector(发件人,实例,**kwargs):
从django_es.utils导入delete_index_item
delete_index_item(instance,sender)
def setup(self,model):
signals.post_save.connect(self.post_save_connector,sender=model)
signals.pre_delete.connect(self.pre_delete_connector,sender=model)
def teardown(self,model):
信号。预删除。断开连接(self.pre_u delete_u connector,sender=model)
信号。后保存。断开连接(self.post_u save_u connector,sender=model)
缓冲区大小
^^^^^^^^^^^^^^^^^^
*可选:*表示在进行大容量索引更新之前要缓冲的项数的整数,默认为"100"。
**警告**:如果在缓冲区被清空之前关闭应用程序,则任何缓冲实例*都不会*在elasticsearch上被索引。因此,一个可能更好的实现是在芹菜任务中包装
``post-save`u connector``和`pre-delete`u connector``from
``django`es.signals``。为了不需要"芹菜",这里没有实现
。