如何创建作为多个查询联合的 Django 模型字段,以实现覆盖字段?
Posted
技术标签:
【中文标题】如何创建作为多个查询联合的 Django 模型字段,以实现覆盖字段?【英文标题】:How do I create a Django model field that is the union of several queries, in order to implement overriding fields? 【发布时间】:2017-04-12 19:17:53 【问题描述】:我正在开发一个用户/组 API(对大多数事情使用 django-rest-framework),并且正忙于作为每个对象一部分的设置字典。
我的基本模型如下所示:
class Group(models.Model):
name = models.CharField(null=False, unique=True)
is_active = models.BooleanField(null=False, default=True)
description = models.CharField(max_length=512)
class User(models.Model):
name = models.CharField(null=False, unique=True, db_index=True)
group = models.ForeignKey('Group', related_name='users')
is_active = models.BooleanField(null=False, default=True)
返回的 JSON 很简单(省略了视图,唯一值得注意的是我没有为 users 字段使用 PrimaryKeyRelatedField)。
组:
"id": 1,
"name": "Basic Group",
"is_active": true,
"description": "Just a simple group",
"users": [
"Test User 1",
"Test User 2"
]
用户:
"id": 1,
"name": "Test User 1",
"group": 1,
"is_active": true
,
"id": 2,
"name": "Test User 2",
"group": 1,
"is_active": true
我想通过为 Group 和 User 对象类型设置一个“设置”字典来增强这一点,它们是覆盖的。也就是说,组的设置字典将包括所有全局设置,除非在组上设置了具有相同名称的设置。同样,用户的设置字典将具有所有全局设置,被组设置覆盖,除非已为用户设置了同名的设置。
想象一个大致如下的结构:
name | value | group | user
--- | --- | --- | ---
setting 1 | value 1 | |
setting 1 | value 2 | 1 |
setting 2 | SKFJDL | 1 |
setting 2 | ABCD | 1 | 2
这将导致为第 1 组返回以下 JSON:
"id": 1,
"name": "Basic Group",
"is_active": true,
"description": "Just a simple group",
"users": [
"Test User 1",
"Test User 2"
],
"settings":
"setting 1": "value 2",
"setting 2": "SKFJDL"
对于用户 1,设置字典如下所示:
"settings":
"setting 1": "value 2",
"setting 2": "SKFJDL"
对于用户 2,它看起来像:
"settings":
"setting 1": "value 2",
"setting 2": "ABCD"
这给我留下了两个问题。
问题 1:让我获得所需输出的 SQL 相对复杂。基本上:
SELECT DISTINCT(name) *
FROM (
(SELECT * FROM setting WHERE group IS NULL AND user IS NULL)
UNION ALL
(SELECT * FROM setting WHERE group = 1 AND user IS NULL)
UNION ALL
(SELECT * FROM setting WHERE group = 1 AND user = 2))
AS temp
我想我可能不得不接受它并编写一些自定义查询,而不是依赖 Django ORM 开箱即用提供给我的任何东西......但我很高兴能成为错了。
问题 2(更大的问题):我到底是怎么做这个的? ForeignKey 关系似乎没有给我一种方法来自定义在进行反向查找时拉入的字段,至少不是我在一天左右的跌跌撞撞中能够找到的任何方式。我有一种感觉,custom reverse manager 几乎肯定是我正在寻找的东西,但我实际上还没有弄清楚如何在模型的上下文中使用它,特别是一个模型,它的下游视图由自动生成DRF。
任何帮助表示赞赏!
【问题讨论】:
【参考方案1】:我对此进行了快速尝试。在 github 上抛出一个非常基本的 Django 项目(使用 testuser|testpass 登录管理员): https://github.com/junctionapps/so40854954
基本上,三个过滤器使用的 sql 几乎完全一样。然后用数据填充字典(或其他),根据优先级进行替换。所以输入默认值(null/null),然后是组,然后是用户。我重命名了模型 CustomUser、CustomGroup 和 CustomSettings,以免碰到保留的 Django 对象。
存储在模型中的设置(我想这就是你的意思,我可能误解了这一点)。
class CustomSettings(models.Model):
name = models.CharField(max_length=64)
value = models.CharField(max_length=64)
group = models.ForeignKey(CustomGroup, null=True, blank=True)
user = models.ForeignKey(CustomUser, null=True, blank=True)
并且在类似以下的视图中(对具有三个循环的部分并不完全满意;可能有一种更巧妙的方式)。
if user_id:
# a place to store the end values
custom_settings =
# get the user's group
custom_user = get_object_or_404(CustomUser, id=user_id)
# get the default values
# see some tips at http://***.com/a/844572/4872140
default_settings = CustomSettings.objects.filter(group__isnull=True,
user__isnull=True)
# get the group values
group_settings = CustomSettings.objects.filter(group=custom_user.group,
user__isnull=True)
# get the user values
user_settings = CustomSettings.objects.filter(group=custom_user.group,
user=custom_user)
# probably a more 'pythonic' way to conflate, however:
# set the values:
for s in default_settings:
custom_settings[s.name] = s.value
for s in group_settings:
custom_settings[s.name] = s.value
for s in user_settings:
custom_settings[s.name] = s.value
那么 custom_settings 应该有你想在某些序列化程序中在 DRF 中使用的值。
【讨论】:
【参考方案2】:class Hop(models.Model):
migration = models.ForeignKey('Migration')
host = models.ForeignKey(User, related_name='host_set')
class Meta:
unique_together = (("migration", "host"),)
https://docs.djangoproject.com/en/2.1/ref/models/options/#unique-together
【讨论】:
以上是关于如何创建作为多个查询联合的 Django 模型字段,以实现覆盖字段?的主要内容,如果未能解决你的问题,请参考以下文章