django查询、反向查询和谓词中的DRY
在Django中,我常常不得不在自定义管理器上编写方法,这让我感到沮丧:django查询、反向查询和谓词中的DRY,django,dry,Django,Dry,在Django中,我常常不得不在自定义管理器上编写方法,这让我感到沮丧: class EntryManager(Manager): def filter_beatle(self, beatle): return self.filter(headline__contains=beatle) 。。。对于反向查询,在不同的管理器中重复几乎相同的方法: class BlogManager(Manager): def filter_beatle(self, beatle)
class EntryManager(Manager):
def filter_beatle(self, beatle):
return self.filter(headline__contains=beatle)
。。。对于反向查询,在不同的管理器中重复几乎相同的方法:
class BlogManager(Manager):
def filter_beatle(self, beatle):
return self.filter(entry__headline__contains=beatle)
。。。和一个入口谓词:
def headline_contains(self, beatle):
return self.headline.find(beatle) != -1
[请注意,Entry上的谓词将用于尚未保存的Entry对象。]
这感觉像是对DRY的违反。是否有某种方法可以一次性表达这一点,并在所有三个地方使用它
我希望能够写下如下内容:
q = Q(headline__contains="Lennon")
lennon_entries = Entry.objects.filter(q)
lennon_blogs = Blog.objects.filter(q.reverse(Entry))
is_lennon = entry.would_filter(q)
class Thing(Model):
class QuerySet(query.QuerySet):
def need_to_be_whacked():
# ... code ...
def needs_to_be_whacked(self):
return Thing.objects.need_to_be_whacked().filter(id=self.id).exists()
def whack(self):
assert self.needs_to_be_whacked()
for thing in Thing.objects.need_to_be_whacked():
thing.whack()
。。。其中,“headline\uu contains=“Lennon”只表达了一次“关于“Lennon”的条目”的含义,这可用于构造反向查询和谓词。您不应该很少执行以下操作:
if entry.headline.find('Lennon') >= 0:
因为过滤器应该注意将结果集限制为您感兴趣的实例
如果要多次使用同一个筛选器,可以创建一个或一个简单的类方法
class Entry(models.Model):
...
# this really should be on a custom manager, but this was quicker to demonstrate
@classmethod
def find_headlines(cls, text):
return cls.objects.filter(headline__contains=text)
entries = Entry.find_headlines('Lennon')
但实际上,Queryset API中已经包含了干燥度。您将每隔多久将字符串“Lennon”硬编码到查询中?通常,搜索参数将从GET或POST传递到视图中。干透了
那么,实际的问题是什么?除了探索queryset API之外,您是否曾经在像您的问题这样的多个查询中硬编码查找值?最好的地方是自定义管理器。根据django的指导原则,对于影响一个类的多个对象的代码,管理器类是最好的地方
class EntryManager(models.Manager):
def filter_lennons(self):
return self.get_query_set().filter(headline__contains='Lennon')
class Entry(models.Model):
headline = models.CharField(max_length=100)
objects = EntryManager()
lennons = Entry.objects.filter_lennons()
对于“反向筛选”情况,可以使用子查询:
Blog.objects.filter(entries__in=Entry.objects.filter_beatle("Lennon"))
重用或生成谓词(通常)是不可能的,因为有些谓词不能表示为查询,有些查询不能表示为没有db访问权限的谓词 我对谓词最常用的用法似乎是在断言中。通常类似于:
q = Q(headline__contains="Lennon")
lennon_entries = Entry.objects.filter(q)
lennon_blogs = Blog.objects.filter(q.reverse(Entry))
is_lennon = entry.would_filter(q)
class Thing(Model):
class QuerySet(query.QuerySet):
def need_to_be_whacked():
# ... code ...
def needs_to_be_whacked(self):
return Thing.objects.need_to_be_whacked().filter(id=self.id).exists()
def whack(self):
assert self.needs_to_be_whacked()
for thing in Thing.objects.need_to_be_whacked():
thing.whack()
我想确保没有其他代码在不需要重击的状态下调用whack()。它需要一次数据库访问,但它可以工作。你是说
Entry.objects.filter(…)
?此外,您还没有解释要执行的操作。将筛选器更改为在管理器上操作,而不是尝试构造无效对象。“谓词”仍然是错误的,我将把它留给OP来解决。感谢您的编辑和澄清问题。这不是关于硬编码“列侬”,而是关于必须用三种不同的方式表达“列侬·奈斯”(或者真正的“披头士·奈斯”)。例如,如果我有一个与一组条目匹配的查询,但我希望以红色显示所有列侬条目,那么可能需要一个谓词。子查询以另一次往返数据库为代价来解决这个问题。我这样做的目的是希望减轻负担。但是,如果有很多Lennon条目,我宁愿让数据库进行连接,而不是用Python创建一个中间结果。看看。在in子句中有足够的ID,您可以达到最大数据包大小的db限制(至少在MySQL中)。我决不会在in子句中使用任意数量的元素。它无法扩展。当它失败时,它会无声地失败,只是截断您的ID。我的问题是需要多次重新声明一个条件,并使用自定义管理器。我不确定这个答案想补充什么。