在没有外键的情况下在 Django 中跨度模型
Span models in Django without a ForeignKey
我正在使用 Python 2.7/Django 1.8,我有点菜鸟。我坚持使用这两个模型创建过滤器:
class A(models.Model):
name = models.TextField()
item_id = models.IntegerField()
class B(models.Model):
entry_id = models.AutoField(primary_key=True)
active = models.BooleanField()
id = models.IntegerField()
value = models.IntegerField()
这里的要点是 A
通过 id
引用 B
,但可能有多个条目具有相同的 ID,并且除一个条目外,其他条目都具有 active = False
.
我想做的基本上是:
SELECT a.Name, b.value
FROM A a
JOIN B b ON a.id = b.item_id and a.active = true
WHERE a.id = {some value}
我能弄明白的唯一方法是这样的:
a_models = A.objects.filter(id=some_value)
a_ids = [model.item_id for model in a_models]
return B.objects.filter(active=True, id__in=a_ids)
这有两次访问数据库的缺点,而且效率很低。有没有办法做到这一点?模型的设计应该有所不同吗?
根据 rofls 的建议,这些是真实的模型名称和预期用途:
class Authorization(models.Model):
active = models.BooleanField()
user_root = models.IntegerField()
name = models.TextField(max_length=64)
class Value(models.Model):
entry_id = models.AutoField(primary_key=True)
active = models.BooleanField()
id = models.IntegerField()
parent = models.IntegerField()
name = models.TextField(max_length=64)
value = models.TextField(max_length=2048)
目的是让值 table 以分层方式存储值。使用 parent -> id 维护层次结构。所以,例如:
name value
/ ''
+-> folder1 ''
+-> value 'some value'
在值中看起来像这样 table:
entry_id | active | id | parent | name | value
0 | True | 1 | 1 | / |
1 | True | 2 | 1 | folder1 |
2 | True | 3 | 2 | value1 | some value
请注意,id 与 entry_id 不同,它允许历史记录。例如,更改 id=3
的值是为 entry_id=2
设置活动 False 并使用 active=True, id=3, parent=2
输入新值的问题。数据库设计明智,这是非常基本的东西。
Django 变得复杂的地方(尤其是对于像我这样刚刚开始尝试围绕 ORM 的人来说),是因为用户角色是一种价值,我认为他们属于存储值的应用程序。因此,Authorization.user_root
指向一个 id,它是所有相关设置的父级。
对不起,文字墙。这是我试图解决的实际问题:给定一个用户授权名称,我怎么知道哪些用户拥有它?如果这是 SQL,查询将如下所示:
SELECT a.name, v.value
FROM Authorization a
JOIN Value v ON a.user_root = v.parent and v.active = 1
WHERE v.name = `{some name}` and v.active = 1
您的模型未通过 FK 链接,但您可以跳过此步骤:
A.objects.vales_list('item_id', flat=True).filter(id=some_value)
return B.objects.filter(active=True, id__in=a_ids)
是否应该对模型进行不同的设计?
是的。你应该在你的 A table 中有一个外键到你的 B table,像这样:
class A(models.Model):
name = models.TextField()
item_id = models.ForeignKey(B,through='id')
那么你应该可以做到:
A.objects.filter(b__active=True)
有关详细信息,请参阅 Django docs。
例如,多对多关系应该类似地工作:
class A(models.Model):
name = models.TextField()
item_id = models.ManyToManyField(B,to_field='id')
此外,您还不清楚要将 B
中的哪个 id
用作 ForeignKey
,因此您可能实际上想要这个:
class A(models.Model):
name = models.TextField()
item_id = models.ForeignKey(B,through='entry_id')
好吧,不确定这是否是 Django 的做法,但我最终是这样做的:
def get_values(self, env):
class ValUsers(models.Model):
name = models.TextField(max_length=64)
value = models.TextField(max_length=2048)
ret = EnvUsers.objects.raw(
'SELECT a.id, a.name, v.value '
' FROM Authorization a, Value v '
' WHERE a.user_root = v.parent '
' and a.active = %s '
' and v.active = %s '
' and v.name = %s ',
[True, True, env]
)
return {u.name: u.value for u in ret}
我正在使用 Python 2.7/Django 1.8,我有点菜鸟。我坚持使用这两个模型创建过滤器:
class A(models.Model):
name = models.TextField()
item_id = models.IntegerField()
class B(models.Model):
entry_id = models.AutoField(primary_key=True)
active = models.BooleanField()
id = models.IntegerField()
value = models.IntegerField()
这里的要点是 A
通过 id
引用 B
,但可能有多个条目具有相同的 ID,并且除一个条目外,其他条目都具有 active = False
.
我想做的基本上是:
SELECT a.Name, b.value
FROM A a
JOIN B b ON a.id = b.item_id and a.active = true
WHERE a.id = {some value}
我能弄明白的唯一方法是这样的:
a_models = A.objects.filter(id=some_value)
a_ids = [model.item_id for model in a_models]
return B.objects.filter(active=True, id__in=a_ids)
这有两次访问数据库的缺点,而且效率很低。有没有办法做到这一点?模型的设计应该有所不同吗?
根据 rofls 的建议,这些是真实的模型名称和预期用途:
class Authorization(models.Model):
active = models.BooleanField()
user_root = models.IntegerField()
name = models.TextField(max_length=64)
class Value(models.Model):
entry_id = models.AutoField(primary_key=True)
active = models.BooleanField()
id = models.IntegerField()
parent = models.IntegerField()
name = models.TextField(max_length=64)
value = models.TextField(max_length=2048)
目的是让值 table 以分层方式存储值。使用 parent -> id 维护层次结构。所以,例如:
name value
/ ''
+-> folder1 ''
+-> value 'some value'
在值中看起来像这样 table:
entry_id | active | id | parent | name | value
0 | True | 1 | 1 | / |
1 | True | 2 | 1 | folder1 |
2 | True | 3 | 2 | value1 | some value
请注意,id 与 entry_id 不同,它允许历史记录。例如,更改 id=3
的值是为 entry_id=2
设置活动 False 并使用 active=True, id=3, parent=2
输入新值的问题。数据库设计明智,这是非常基本的东西。
Django 变得复杂的地方(尤其是对于像我这样刚刚开始尝试围绕 ORM 的人来说),是因为用户角色是一种价值,我认为他们属于存储值的应用程序。因此,Authorization.user_root
指向一个 id,它是所有相关设置的父级。
对不起,文字墙。这是我试图解决的实际问题:给定一个用户授权名称,我怎么知道哪些用户拥有它?如果这是 SQL,查询将如下所示:
SELECT a.name, v.value
FROM Authorization a
JOIN Value v ON a.user_root = v.parent and v.active = 1
WHERE v.name = `{some name}` and v.active = 1
您的模型未通过 FK 链接,但您可以跳过此步骤:
A.objects.vales_list('item_id', flat=True).filter(id=some_value)
return B.objects.filter(active=True, id__in=a_ids)
是否应该对模型进行不同的设计?
是的。你应该在你的 A table 中有一个外键到你的 B table,像这样:
class A(models.Model):
name = models.TextField()
item_id = models.ForeignKey(B,through='id')
那么你应该可以做到:
A.objects.filter(b__active=True)
有关详细信息,请参阅 Django docs。
例如,多对多关系应该类似地工作:
class A(models.Model):
name = models.TextField()
item_id = models.ManyToManyField(B,to_field='id')
此外,您还不清楚要将 B
中的哪个 id
用作 ForeignKey
,因此您可能实际上想要这个:
class A(models.Model):
name = models.TextField()
item_id = models.ForeignKey(B,through='entry_id')
好吧,不确定这是否是 Django 的做法,但我最终是这样做的:
def get_values(self, env):
class ValUsers(models.Model):
name = models.TextField(max_length=64)
value = models.TextField(max_length=2048)
ret = EnvUsers.objects.raw(
'SELECT a.id, a.name, v.value '
' FROM Authorization a, Value v '
' WHERE a.user_root = v.parent '
' and a.active = %s '
' and v.active = %s '
' and v.name = %s ',
[True, True, env]
)
return {u.name: u.value for u in ret}