在https://docs.djangoproject.com/en/dev/topics/db/models/#many-to-many-relationships上,Django 团队描述了比萨和浇头上下文中的多对多关系。我以他们为例,试图找到解决我在建模自己的数据时遇到的概念问题的方法。
为了准确解释这篇 Stack Overflow 帖子我要完成的工作,请考虑以下(假设的)语法:
TOPPING = {'a'..'z' | 'A'..'Z'};
FLOAT = {'0'..'9'}, ['.', {'0'..'9'}]; (* Non-negative numbers only *)
QUERY = "Hey Django, find me all of the pizzas in the database such that:",
{ "(", (TOPPING), (">=" | "<=" | "=="), FLOAT, ") &&" },
"(", (TOPPING, (">=" | "<=" | "=="), FLOAT, ")";
这是我试图观察的从发出类似于 QUERY 的内容返回的 QuerySet。正如其内容所暗示的,我希望 Django 向我返回一个 QuerySet,其中包含满足我筛选标准的所有 Pizza 对象。我对 models.py 的外观或实际查询的外观没有信心。我只有足够的经验来解释上面的伪查询和我希望返回的结果。
提前感谢您,我期待着阅读您的建议。
额外信息
请注意,在我的特定情况下,加载初始数据后永远不会写入数据库。阅读总是非常频繁。
我愿意使用原始 SQL,但我更喜欢使用 Django QuerySet API
在解决方案中尝试 1:
我在模型中添加了一个 ToppingAmount(models.Model) 类。我给了它一个金额的浮点数,还有两个外键将披萨和浇头与金额相关联。
from django.db import models
class Topping(models.Model):
name = models.CharField(max_length=128)
# On Python 3: def __str__(self):
def __unicode__(self):
return self.name
class Pizza(models.Model):
name = models.CharField(max_length=128)
toppings = models.ManyToManyField(Topping, through='ToppingAmount')
# On Python 3: def __str__(self):
def __unicode__(self):
return self.name
class ToppingAmount(models.Model):
topping = models.ForeignKey(Topping)
pizza = models.ForeignKey(Pizza)
amount = models.FloatField()
units = models.CharField(max_length=4) # e.g. 'g' | 'kg' | 'lb' | 'oz' ...
上面的模型将允许一个人(例如)执行 aToppingAmount.objects.filter(topping__name='foo').filter(amount__gt=bar)
并找到所有的ToppingAmount对象,这样它的比萨饼上至少有 bar 克的 foo 。
不幸的是,它也允许反常的ToppingAmount.objects.filter(pizza__name='pippo').filter(amount__gt=bar)
人找到所有的ToppingAmount对象,这样它的顶部至少有 bar 克的“pippo”。
这是我能用这个模型和简单的过滤器得到的。我们无法根据其他配料(或比萨饼;))和金额过滤生成的 QuerySet,因为此 QuerySet 仅包含ToppingAmount对象。此 QuerySet 中的每个 ToppingAmount 对象将具有相同的浇头或相同的比萨饼。它们的所有数量(无论是解释为描述浇头还是比萨饼)也将符合filter(amount...)
.
此外,我观察到在这里使用多对多关系没有明显的好处。从 Pizza 类中删除浇头,同时保持其他所有内容相同会导致数据库具有相同的 json 转储和来自可能查询的相同实用程序。
尝试2:
创建一个 Pizza 表,其中包含所有 146 种可能的配料的列。
from django.db import models
class Topping(models.Model):
name= modles.CharField(max_length=128)
units = models.CharField(max_length=4) # e.g. 'g' | 'kg' | 'lb' | 'oz' ...
class Pizza(models.Model):
name = models.CharField(max_length=128)
topping1_amount = models.FloatField()
.
.
.
topping146_amount = models.FloatField()
# On Python 3: def __str__(self):
def __unicode__(self):
return self.name
我很谨慎地相信这个解决方案,因为它创建了一个我认为不必要或不可取的大而稀疏的表(赞赏评论)。另一方面,为这个模型编写查询非常简单:Pizza.objects.filter(topping1_amount__gt=20).filter(topping2_amount__lt=15)...