我有一个模型,它具有与之关联的任意键/值对(属性)。我想选择按这些动态属性进行排序。这就是我想出的:
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,))
答案 0 :(得分: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
将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
。prefetch_related
后执行queryset
,这意味着正在为prefetch_related调用中的每个子句执行第二个查询。在某种程度上,这可能是好的,因为这会使主queryset
不受过滤器的影响,因此不需要像.distinct()
这样的附加条款。prefetch_related
总是把返回的对象放到一个列表中,当你有预取每个对象返回1个元素时,它不是很方便使用。因此,需要额外的模型方法才能愉快地使用。