我在我的Django应用程序中写了一些信号,它们应该在创建或修改特定模型实例时发送电子邮件,但信号接收器功能似乎没有响应; 无论如何,我没有收到任何电子邮件(虽然我已经检查过我能够使用我当前的配置发送电子邮件).
无论如何; 我想知道,是否有可能手动发送post_save信号用于调试目的,而不是每次都通过创建一个新的模型实例来尝试触发它?谢谢!
我读到了django信号(http://docs.djangoproject.com/en/dev/topics/signals/),但据我所知,信号永远不会转换为文字SQL触发器(http://en.wikipedia. org/wiki/Database_trigger).
如果我是正确的,信号和触发器是不同的,那么哪一个更好,以什么方式?什么是最佳做法?
....................
这是一个具体的例子,如果你想要一个:
class Location(models.Model):
name = models.CharField(max_length=30)
class Person(models.Model):
location = models.ForeignKey('Location')
class Team(models.Model):
locations = models.ManyToManyField('Location')
Run Code Online (Sandbox Code Playgroud)
我希望一个人能够加入一个团队,当且仅当该人的位置在该团队的一组位置内时.我不知道如何使用正常的关系约束来做到这一点,所以据我所知,我被迫使用触发器或信号.我的直觉说我应该使用触发器,但我想知道最佳实践.
我有这样的设置(简化为这个问题):
class Employee(models.Model):
name = models.CharField(name, unique=True)
class Project(models.Model):
name = models.CharField(name, unique=True)
employees = models.ManyToManyField(Employee)
Run Code Online (Sandbox Code Playgroud)
当员工即将被删除时,我想检查他是否已连接到任何项目.如果是这样,删除应该是不可能的.
我知道信号以及如何使用它们.我可以连接到pre_delete信号,并使它像一个例外ValidationError.这可以防止删除但是表单等不能正常处理.
这似乎是其他人会遇到的情况.我希望有人可以指出一个更优雅的解决方案.
总之,我对django信号有一个问题.
我有一个模型为了加快页面加载的响应速度,我正在卸载必须完成的一些密集处理,通过调用我们正在运行的第二个localhost网络服务器,两者都使用相同的数据库.我看到调用进程可以检索对象的行为,但被调用的进程不能.端口80和端口[端口]都指向运行在同一数据库中的django进程.
在models.py中
class A(models.Model):
stuff...
def trigger_on_post_save( sender, instance, create, raw, **keywords):
#This line works
A.objects.get( pk=instance.pk )
#then we call this
urlopen( r'http://127.0.0.1:[port]' +
reverse(some_view_url, args(instance_pk) ).read()
post_save.connect( trigger_on_post_save, A )
Run Code Online (Sandbox Code Playgroud)
在views.py中
def some_view_function( request, a_pk ):
#This line raises an object_not_found exception
A.objects.get( pk=a_pk )
Run Code Online (Sandbox Code Playgroud)
此外,在urlopen调用引发异常之后,该对象在数据库中不存在.据我所知,在保存对象后调用了post_save,并将其写入数据库.这是不正确的?
我有一个相当长的运行任务,需要在插入或更新特定模型后执行.
我决定使用post_save信号而不是重写save方法来减少耦合.由于Django信号不是异步的,我不得不做长期运行的工作作为Celery任务(我们已经在我们的堆栈中).
我的信号处理功能的简化版本如下:
@receiver(post_save, sender=MyModel)
def my_model_post_save(sender, instance, **kwargs):
handle_save_task.apply_async(args=(instance.pk,))
Run Code Online (Sandbox Code Playgroud)
此外,因为作业是异步完成的,所以我传递了对象的主键而不是实例本身.
@app.task(queue='elastic')
def handle_save_task(instance_pk):
try:
instance = MyModel.objects.get(pk=instance_pk)
except ObjectDoesNotExist:
# Abort
logger.warning("Saved object was deleted before this task get a chance to be executed [id = %d]" % instance_pk)
else:
# Do my things with instance
Run Code Online (Sandbox Code Playgroud)
实际问题是当执行芹菜任务时,它无法访问新保存的实例.就像它在保存之前执行一样!(不是叫做post_ save 的信号吗?多么讽刺)
通过"在保存之前执行"我的意思是如果它是一个新的实例,它被插入到DB中,在芹菜任务中我得到一个DoesNotExist异常,并且在实例已经在DB中并且调用save方法来更新它的一些属性我在celery任务中获取旧属性值的旧实例.
解决方法是在几秒钟的延迟时间内运行芹菜任务,但显然它不是一个好的解决方案,也无法保证在重负载或长网络延迟下的正确执行行为.
我这样做是完全错误还是稍作修改我可以使它工作?
我无法理解为什么我的m2m_changed信号没有被触发.
这是代码:
models.py
class Badge(TimeStampable, Expirable, Deactivable,
SafeDeleteModel):
_safedelete_policy = HARD_DELETE
owner = models.ForeignKey(settings.AUTH_USER_MODEL,
blank=True, null=True,
on_delete=models.PROTECT)
restaurants = models.ManyToManyField(Restaurant)
identifier = models.CharField(max_length=2048)
objects = SafeDeleteManager.from_queryset(BadgeQuerySet)()
Run Code Online (Sandbox Code Playgroud)
signals.py
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
from .models import Badge
@receiver(m2m_changed, sender=Badge.restaurants.through)
def my_callback(sender, **kwargs):
print("M2M has been changed!")
Run Code Online (Sandbox Code Playgroud)
apps.py(这篇帖子建议改变这个文件)
from django.apps import AppConfig
class BadgesConfig(AppConfig):
name = 'badges'
def ready(self):
import badges.signals
Run Code Online (Sandbox Code Playgroud)
去shell时:
m2m_changed.receivers 返回一个空列表(它不起作用,永远不会收到信号)我找到了类似的帖子,但没有找到答案.
为什么m2m_changed信号不起作用?
编辑
打开shell并导入时badges.signals,它可以正常工作.这意味着问题在于apps.py:
In [1]: from django.db.models.signals …Run Code Online (Sandbox Code Playgroud) 当对Django模型(.save())执行更新/创建时,我希望能够"介入"并将某些特定属性与之前设置的属性进行比较(如果它们以前存在的话).
我正在考虑预先保存信号,查看原始模型做的事情.objects.get(instance.id),但这感觉很浪费.还有,验证已经发生了pre_save()吗?
谢谢你的时间.
我在Django 1.4上,我有以下代码:它是我的Quest模型的重写保存方法.
@commit_on_success
def save(self, *args, **kwargs):
from ib.quest.models.quest_status_update import QuestStatusUpdate
created = not self.pk
if not created:
quest = Quest.objects.get(pk=self)
# CHECK FOR SOME OLD VALUE
super(Quest, self).save(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
我找不到这样做的聪明方法.对于我正在更新的对象进行新查询以找出旧的实例值,对我来说似乎非常愚蠢.
有一个更好的方法吗?
谢谢你们.
弗朗西斯科
有人能帮我理解update_fieldDjango信号的论点吗?
根据文件:
update_fields:要在save()方法中明确指定的要更新的字段集.如果在save()调用中未使用此参数,则为none.
我不清楚这意味着什么.我试图使用它来阻止信号功能执行,除非某些字段被更新:
@receiver(post_save, sender=SalesRecord)
def spawn_SaleSource_record(sender, update_fields, created, instance, **kwargs):
if created or update_fields is 'sale_item' or 'sales_qty':
*do function*
Run Code Online (Sandbox Code Playgroud)
但是,即使明确更新了未指定的字段,它似乎仍会在保存对象时在另一个信号进程中执行:
x = SalesRecord.objects.filter(paid_off=False, customer=instance.customer).first()
x.paid_off = True
x.save(update_fields=['paid_off'])
Run Code Online (Sandbox Code Playgroud)
我错了吗?
我有一个保持磁盘缓存的抽象模型.当我删除模型时,我需要它来删除缓存.我希望每个派生模型都能实现这一点.
如果我连接指定抽象模型的信号,则不会传播到派生模型:
pre_delete.connect(clear_cache, sender=MyAbstractModel, weak=False)
Run Code Online (Sandbox Code Playgroud)
如果我尝试在init中连接信号,在那里我可以获得派生类名,它可以工作,但我担心它会尝试清除缓存,就像我初始化派生模型一样多次,而不是一次.
我应该在哪里连接信号?