如何在Django-admin中查找存储为整数的自定义IP地址字段?

时间:2009-02-12 12:54:42

标签: python mysql django

在我的Django模型中,我创建了自定义MyIPAddressField,它在mysql后端存储为整数。为此,我实现了to_python,get_db_prep_value,get_iternal_type(返回PositiveIntegerField)和formfield方法(使用stock IPAddressField作为form_class)。

唯一的问题是在ModelAdmin中内置搜索的情况下进行字段查找。那么,问题是如何实现get_db_prep_lookup来执行基于字符串的查找类型,如'contains','regex','startswith','endswith'?

Mysql有特殊功能inet_ntoa()但我不知道如何指示ORM在管理搜索查询中使用它。例如:SELECT inet_ntoa(ip_address)as ip FROM table WHERE ip LIKE'%search_term%'。为什么自定义字段在python端执行类型转换而不在数据库端执行?

EDIT1:可能还有另一种解决搜索问题的方法 - 不要将整数列转换为字符串,而是将搜索参数拆分为子网/掩码,并执行一些二进制数学运算,将它们与整数IP值进行比较。

EDIT2:到目前为止,这是我的代码:

models.py:

class MyIPField(models.Field):
    empty_strings_allowed = False

    __metaclass__ = models.SubfieldBase

    def get_db_prep_value(self, value):
        if value is None: return None
        return unpack('!L', inet_aton(value))[0]

    def get_internal_type(self):
        return "PositiveIntegerField"

    def to_python(self, value):
         if type(value).__name__ in ('NoneType', 'unicode'): return value
         return inet_ntoa(pack('!L', value))

    def formfield(self, **kwargs):
        defaults = {'form_class': IPAddressField}
        defaults.update(kwargs)
        return super(MyIPField, self).formfield(**defaults)


class MyManager(models.Manager):
    def get_query_set(self):
        return super(MyManager, self).get_query_set().extra(select={'fakeip': "inet_ntoa(ip)"})

class Address(models.Model):
    # ... other fields are skipped (Note: there was several foreign keys)
    ip = MyIPField(u"IP address", unique=True)

    objects = AddressManager()

    def __unicode__(self):
            return self.ip

admin.py:

class AddressAdmin(admin.ModelAdmin):
    list_display = ('address',)  # ... some fields are skipped from this example
    list_display_links = ('address',)
    search_fields = ('fakeip', )

admin.site.register(Address, AddressAdmin)

但是当我使用admin changelist搜索框时,我收到错误“无法解析关键字'fakeip'到字段中。选项是:ip,id”。是否有可能欺骗Django并让它认为fakeip是一个真实的领域?

使用标准IPAddressField(基于字符串)不适合我的需要,也不适合切换到以适当格式存储的Postgres。

此外,我已经看过Django admin internals(options.py和views / main.py),我认为没有简单的方法可以自定义ChangeList类或搜索机制而无需大量复制/粘贴。我认为Django管理员比它更强大。

1 个答案:

答案 0 :(得分:2)

您可以指示ORM向SQL查询添加额外字段,如下所示:

IPAddressModel.objects.extra(select={'ip': "inet_ntoa(ip_address)"})

这会向查询添加SELECT inet_ntoa(ip_address) as ip,并为对象添加字段ip。您可以在WHERE子句中使用新的合成字段。

你确定你真的不想要WHERE (ip_address & 0xffff0000) = inet_aton('192.168.0.0')这样的东西吗?或者真的想要在日志中找到包含数字119的所有IP地址吗?

如果suffix是来自CIDR表示法的/ 24:

mask = 0xffffffff ^ 0xffffffff >> suffix

添加到WHERE子句:

(ip_address & mask) = (inet_aton(prefix) & mask)