gpt4 book ai didi

django - 在Django中将两个不相关的表/模型与相同的主键合并

转载 作者:行者123 更新时间:2023-12-04 13:36:11 28 4
gpt4 key购买 nike

我有两个具有相同主键的不相关表。

ip            mac
11.11.11.11 48-C0-09-1F-9B-54
33.33.33.33 4E-10-A3-BC-B8-9D
44.44.44.44 CD-00-60-08-56-2A
55.55.55.55 23-CE-D3-B1-39-A6

ip type owner
22.22.22.22 laptop John Doe
33.33.33.33 server XYZ Department
44.44.44.44 VM Mary Smith
66.66.66.66 printer ZWV Department

第一个表每分钟自动刷新一次。我无法更改数据库结构或填充它的脚本。

两个表都有 ip作为PRIMARY KEY。

在 View 中,我想显示一个这样的表:
ip           mac               type    owner          Alert
11.11.11.11 48-C0-09-1F-9B-54 Unauthorized
55.55.55.55 23-CE-D3-B1-39-A6 Unauthorized
22.22.22.22 laptop John Doe Down
66.66.66.66 printer ZWV Department Down
33.33.33.33 4E-10-A3-BC-B8-9D server XYZ Department OK
44.44.44.44 CD-00-60-08-56-2A VM Mary Smith OK

我该如何建模?我是否应该将两个主键中的一个作为外键?

代码运行后,将有大量数据,因此,我想确保它足够快。

检索数据最快的方法是什么?

更新:

我尝试对第二张表使用 OneToOneField

这有助于我获得两个表中的记录以及未授权设备的记录(第二个表中缺少IP):
ip           mac               type    owner          Alert
11.11.11.11 48-C0-09-1F-9B-54 Unauthorized
55.55.55.55 23-CE-D3-B1-39-A6 Unauthorized
33.33.33.33 4E-10-A3-BC-B8-9D server XYZ Department OK
44.44.44.44 CD-00-60-08-56-2A VM Mary Smith OK

但我无法关闭掉的设备(第一张表中缺少IP):
22.22.22.22                    laptop  John Doe       Down
66.66.66.66 printer ZWV Department Down

我要求帮助 here,但似乎无法使用 OneToOneField完成

最佳答案

大概的概念

您可以使用qs.union:

  • 创建2个模型,它们之间没有任何关系。别忘了使用class Meta: managed = False
  • 从第一个模型中选择,用子查询进行注释,并用第二个模型进行联合:

  • from django.db import models
    from django.db.models import F, OuterRef, Subquery, Value
    from django.db.models.functions import Coalesce

    # OperationalDevice fields: ip, mac
    # AllowedDevice fields: ip, type, owner

    USE_EMPTY_STR_AS_DEFAULT = True

    null_char_field = models.CharField(null=True)
    if USE_EMPTY_STR_AS_DEFAULT:
    default_value = ''
    else:
    default_value = None

    # By default Expressions treat strings as "field_name" so if you want to use
    # empty string as a second argument for Coalesce, then you should wrap it in
    # `Value()`.
    # `None` can be used there without wrapping in `Value()`, but in
    # `.annotate(type=NoneValue)` it still should be wrapped, so it's easier to
    # just "always wrap".
    default_value = Value(default_value, output_field=null_char_field)

    operational_devices_subquery = OperationalDevice.objects.filter(ip=OuterRef('ip'))


    qs1 = (
    AllowedDevice.objects
    .all()
    .annotate(
    mac=Coalesce(
    Subquery(operational_devices_subquery.values('mac')[:1]),
    default_value,
    output_field=null_char_field,
    ),
    )
    )

    qs2 = (
    OperationalDevice.objects
    .exclude(
    ip__in=qs1.values('ip'),
    )
    .annotate(
    type=default_value,
    owner=default_value,
    )
    )

    final_qs = qs1.union(qs2)


    多个领域的通用方法

    一种更复杂但“通用”的方法可以使用 Model._meta.get_fields() 。对于“第二”模型具有多于1个额外字段的情况(不仅是 ip,mac),将更易于使用。示例代码(未经测试,但具有一般印象):

    # One more import:
    from django.db.models.fields import NOT_PROVIDED

    common_field_name = 'ip'

    # OperationalDevice fields: ip, mac, some_more_fields ...
    # AllowedDevice fields: ip, type, owner

    operational_device_fields = OperationalDevice._meta.get_fields()
    operational_device_fields_names = {_f.name for _f in operational_device_fields} # or set((_f.name for ...))

    allowed_device_fields = AllowedDevice._meta.get_fields()
    allowed_device_fields_names = {_f.name for _f in allowed_device_fields} # or set((_f.name for ...))

    operational_devices_subquery = OperationalDevice.objects.filter(ip=OuterRef(common_field_name))

    left_joined_qs = ( # "Kind-of". Assuming AllowedDevice to be "left" and OperationalDevice to be "right"
    AllowedDevice.objects
    .all()
    .annotate(
    **{
    _f.name: Coalesce(
    Subquery(operational_devices_subquery.values(_f.name)[1]),
    Value(_f.get_default()), # Use defaults from model definition
    output_field=_f,
    )
    for _f in operational_device_fields
    if _f.name not in allowed_device_fields_names
    # NOTE: if fields other than `ip` "overlap", then you might consider
    # changing logic here. Current implementation keeps fields from the
    # AllowedDevice
    }
    # Unpacked dict is partially equivalent to this:
    # mac=Coalesce(
    # Subquery(operational_devices_subquery.values('mac')[:1]),
    # default_for_mac_eg_fallback_text_value,
    # output_field=null_char_field,
    # ),
    # other_field = Coalesce(...),
    # ...
    )
    )

    lonely_right_rows_qs = (
    OperationalDevice.objects
    .exclude(
    ip__in=AllowedDevice.objects.all().values(common_field_name),
    )
    .annotate(
    **{
    _f.name: Value(_f.get_default(), output_field=_f), # Use defaults from model definition
    for _f in allowed_device_fields
    if _f.name not in operational_device_fields_names
    # NOTE: See previous NOTE
    }
    )
    )

    final_qs = left_joined_qs.union(lonely_right_rows_qs)


    将OneToOneField用于“更好的” SQL

    从理论上讲,您可以在 device_info = models.OneToOneField(OperationalDevice, db_column='ip', primary_key=True, related_name='status_info')中使用 AllowedDevice:。在这种情况下,可以不使用 Subquery来定义您的第一个QS:

    from django.db.models import F

    # Now 'ip' is not in field names ('device_info' is there), so add it:
    allowed_device_fields_names.add(common_field_name)

    # NOTE: I think this approach will result in a more compact SQL query without
    # multiple `(SELECT "some_field" FROM device_info_table ... ) as "some-field"`.
    # This also might result in better query performance.
    honest_join_qs = (
    AllowedDevice.objects
    .all()
    .annotate(
    **{
    _f.name: F(f'device_info__{_f.name}')
    for _f in operational_device_fields
    if _f.name not in allowed_device_fields_names
    }
    )
    )

    final_qs = honest_join_qs.union(lonely_right_rows_qs)
    # or:
    # final_qs = honest_join_qs.union(
    # OperationalDevice.objects.filter(status_info__isnull=True).annotate(**missing_fields_annotation)
    # )
    # I'm not sure which approach is better performance-wise...
    # Commented one will use something like:
    # `SELECT ... FROM "device_info_table" LEFT OUTER JOIN "status_info_table" ON ("device_info_table"."ip" = "status_info_table"."ip") WHERE "status_info_table"."ip" IS NULL
    #
    # So it might be a little better than first with `union(QS.exclude(ip__in=honest_join_qs.values('ip'))`.
    # Because later uses SQL like this:
    # `SELECT ... FROM "device_info_table" WHERE NOT ip IN (SELECT ip FROM "status_info_table")`
    #
    # But it's better to measure timings of both approaches to be sure.
    # @GrannyAching, can you compare them and tell in the comments which one is better ?

    P.S.要自动执行模型定义,您可以使用 manage.py inspectdb

    P.P.S.也许使用自定义 OneToOneField(..., parent_link=True)multi-table inheritance可能比使用 union对您更有用。

    关于django - 在Django中将两个不相关的表/模型与相同的主键合并,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55655526/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com