我有两个Django模型,如下所示,MyModel1
& MyModel2
:
class MyModel1(CachingMixin, MPTTModel):
name = models.CharField(null=False, blank=False, max_length=255)
objects = CachingManager()
def __str__(self):
return "; ".join(["ID: %s" % self.pk, "name: %s" % self.name, ] )
class MyModel2(CachingMixin, models.Model):
name = models.CharField(null=False, blank=False, max_length=255)
model1 = models.ManyToManyField(MyModel1, related_name="MyModel2_MyModel1")
objects = CachingManager()
def __str__(self):
return "; ".join(["ID: %s" % self.pk, "name: %s" % self.name, ] )
MyModel2
有一个名为MyModel1
的{{1}}的ManyToMany字段
现在看看当我向这个ManyToMany字段添加一个新条目时会发生什么。据Django说,它没有效果:
model1
为什么呢?它似乎绝对是一个缓存问题,因为我看到数据库表myapp_mymodel2_mymodel1中有一个新条目,用于>>> m1 = MyModel1.objects.all()[0]
>>> m2 = MyModel2.objects.all()[0]
>>> m2.model1.all()
[]
>>> m2.model1.add(m1)
>>> m2.model1.all()
[]
&之间的此链接。 m2
。我应该怎么解决?
答案 0 :(得分:9)
MyModel1.objects.all()[0]
大致翻译为
SELECT * FROM app_mymodel LIMIT 1
这样的查询总是很快。无论是从缓存还是从数据库中获取速度,速度都不会有显着差异。
当你使用缓存管理器时,你实际上在这里增加了一些开销,这可能会使事情变得有点慢。大多数情况下,这种努力将被浪费,因为可能没有缓存命中,如下一节所述。
每当您运行查询时,
CachingQuerySet
都会尝试查找该查询 在缓存中。查询由{prefix}:{sql}
键控。如果它在那里,我们 返回缓存的结果集,每个人都很高兴。如果查询不是 在缓存中,执行运行数据库查询的正常代码路径。 随着结果集中的对象被迭代,它们被添加到 一旦迭代完成就会缓存的列表。
来源:https://cache-machine.readthedocs.io/en/latest/
因此,在您的问题中执行的两个查询相同的情况下,缓存管理器将从memcache获取第二个结果集,前提是缓存未被无效。
相同的链接解释了缓存密钥的无效方式。
为了支持轻松缓存失效,我们使用“刷新列表”来标记 对象所属的缓存查询。那样,所有的查询都在哪里 当该对象发生更改时,找到的对象将失效。红晕 列表将对象键映射到查询键列表。
保存或删除对象时,其刷新列表中的所有查询键 将被删除。另外,其外键的刷新列表 关系将被清除。为了避免陈旧的外键关系,任何 当对象的外键时,缓存的对象将被刷新 指向无效。
很明显,保存或删除对象会导致缓存中的许多对象无效。因此,您通过使用缓存管理器来减慢这些操作。另外值得注意的是,失效文档根本没有提到多个字段。对此有一个open issue,根据您对该问题的评论,很明显您也发现了它。
查克缓存机。缓存所有查询几乎都不值得。它导致各种难以发现的错误和问题。最好的方法是优化表格并微调查询。如果您发现某个特定查询太慢,请手动缓存它。
答案 1 :(得分:1)
这是我的解决方案:
>>> m1 = MyModel1.objects.all()[0]
>>> m1
<MyModel1: ID: 8887972990743179; name: my-name-blahblah>
>>> m2 = MyModel2.objects.all()[0]
>>> m2.model1.all()
[]
>>> m2.model1.add(m1)
>>> m2.model1.all()
[]
>>> MyModel1.objects.invalidate(m1)
>>> MyModel2.objects.invalidate(m2)
>>> m2.save()
>>> m2.model1.all()
[<MyModel1: ID: 8887972990743179; name: my-name-blahblah>]
答案 2 :(得分:1)
您是否考虑过挂入模型信号以在添加对象时使缓存无效?对于您的情况,您应该查看M2M Changed Signal
没有解决问题的小例子,但workaround you gave before与我的信号解决方案方法有关(我不知道django-cache-machine):
def invalidate_m2m(sender, **kwargs):
instance = kwargs.get('instance', None)
action = kwargs.get('action', None)
if action == 'post_add':
Sender.objects.invalidate(instance)
m2m_changed.connect(invalidate_m2m, sender=MyModel2.model1.through)
答案 3 :(得分:0)
A。 J. Parr的回答是最正确的,但是您忘记了post_remove,也可以将其绑定到每个ManytoManyfield上,如下所示:
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
@receiver(m2m_changed, )
def invalidate_cache_m2m(sender, instance, action, reverse, model, pk_set, **kwargs ):
if action in ['post_add', 'post_remove'] :
model.objects.invalidate(instance)