在没有原始SQL的情况下加入Django中的注释

时间:2016-10-27 22:54:58

标签: django django-models

我有一个模型,它具有与之关联的任意键/值对(属性)。我想选择按这些动态属性进行排序。这就是我想出的:

read()

这样可行,但它里面有一些原始SQL,所以它让我的同事们保持警惕。没有原始SQL可以做到这一点吗?我可以使用Django的ORM来简化这个吗?

我希望这样的东西可以工作,但事实并非如此:

class Item(models.Model):
  pass

class Attribute(models.Model):
  item = models.ForeignKey(Item, related_name='attributes')
  key = models.CharField()
  value = models.CharField()

def get_sorted_items():
  return Item.objects.all().annotate(
      first=models.select_attribute('first'),
      second=models.select_attribute('second'),
  ).order_by('first', 'second')

def select_attribute(attribute):
  return expressions.RawSQL("""
      select app_attribute.value from app_attribute
      where app_attribute.item_id = app_item.id
      and app_attribute.key = %s""", (attribute,))

1 个答案:

答案 0 :(得分:1)

方法1

使用Djagno 1.8+ Conditional Expressions (另见Query Expressions

items = Item.objects.all().annotate(
    first=models.Case(models.When(attribute__key='first', then=models.F('attribute__value')), default=models.Value('')),
    second=models.Case(models.When(attribute__key='second', then=models.F('attribute__value')), default=models.Value(''))
).distinct()

for item in items:
    print item.first, item.second

方法2

prefetch_related与自定义models.Prefetch对象

一起使用
keys = ['first', 'second']
items = Item.objects.all().prefetch_related(
    models.Prefetch('attributes',
        queryset=Attribute.objects.filter(key__in=keys),
        to_attr='prefetched_attrs'),
)

这样,item中的每个queryset都会在.prefetched_attrs属性下包含一个列表。

此列表将包含所有与过滤项目相关的属性。

现在,因为您想获得attribute.value,您可以实现以下内容:

class Item(models.Model):
    #...
    def get_attribute(self, key, default=None):
        try:
            return next((attr.value for attr in self.prefetched_attrs if attr.key == key), default)
        except AttributeError:
            raise AttributeError('You didnt prefetch any attributes')

#and the usage will be:
for item in items:
    print item.get_attribute('first'), item.get_attribute('second')

关于使用这两种方法的差异的一些注释。

  • 您可以使用自定义Prefetch对象的方法更好地控制过滤过程。条件表达式方法是一个难以优化的想法恕我直言。
  • 使用prefetch_related,您将获得整个属性对象,而不仅仅是您感兴趣的value
  • Django在评估prefetch_related后执行queryset,这意味着正在为prefetch_related调用中的每个子句执行第二个查询。在某种程度上,这可能是好的,因为这会使主queryset不受过滤器的影响,因此不需要像.distinct()这样的附加条款。
  • prefetch_related总是把返回的对象放到一个列表中,当你有预取每个对象返回1个元素时,它不是很方便使用。因此,需要额外的模型方法才能愉快地使用。