django中的DRY查询,反向查询和谓词

时间:2011-04-06 22:57:34

标签: django dry

我很沮丧,在Django中,我经常最终不得不在自定义管理器上编写方法:

class EntryManager(Manager):
    def filter_beatle(self, beatle):
        return self.filter(headline__contains=beatle)

...并在不同的Manager中重复使用相同的方法进行反向查询:

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)

...其中'headline__contains =“Lennon”'恰好表达了一个关于“Lennon”的条目,这可用于构建反向查询和谓词。

4 个答案:

答案 0 :(得分:1)

最好的地方是自定义经理。根据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()

答案 1 :(得分:0)

你应该从不很少做以下事情:

if entry.headline.find('Lennon') >= 0:

因为过滤器应该将结果集限制在您感兴趣的实例中。

如果您要多次使用相同的过滤器,则可以创建custom manager或简单的类方法。

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')

但实际上,DRYness已经包含在Queryset API中。你有多少经常将字符串'Lennon'硬编码到查询中?通常,搜索参数将从GET或POST传递到视图中。完全干。

那么,实际问题是什么?除了探索queryset API之外,您是否曾经不得不在多个查询中硬编码查找值,例如您的问题?

答案 2 :(得分:0)

对于“反向过滤”情况,您可以使用子查询:

Blog.objects.filter(entries__in=Entry.objects.filter_beatle("Lennon"))

不可能(通常)重用或生成谓词,因为有些谓词不能表示为无法在没有数据库访问的情况下表示为谓词的查询和查询。

答案 3 :(得分:0)

我对谓词的最常见用法似乎是断言。通常是这样的:

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()。它需要花费数据库,但它确实有效。