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

时间:2022-09-16 13:34:39

In my Django model I've created custom MyIPAddressField which is stored as integer in mysql backend. To do that I've implemented to_python, get_db_prep_value, get_iternal_type (returns PositiveIntegerField) and formfield methods (uses stock IPAddressField as form_class).

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

The only problem is field lookup in cases like builtin search in ModelAdmin. So, the question is how to implement get_db_prep_lookup to perform string-based lookup types like 'contains', 'regex', 'startswith', 'endswith'?

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

Mysql has special function inet_ntoa() but I don't know how to instruct ORM to use it in admin search queries. For example: SELECT inet_ntoa(ip_address) as ip FROM table WHERE ip LIKE '%search_term%'. Why custom fields perform type casting on python side but not on database side?

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

EDIT1: probably there is another way to solve search problem - don't transform integer column to string, but instead split search argument into subnet/mask and perform some binary math to compare them against integer IP value.

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

EDIT2: this is my code so far:

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)

But when I use admin changelist search box, I get error "Can not resolve keyword 'fakeip' into field. Choices are: ip, id". Is it possible to fool Django and make it think that fakeip is a real field?

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

Using standard IPAddressField (string based) is not appropriate for my needs, as well as switching to Postgres where it stored in proper format.

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

Also I've looked to Django admin internals (options.py and views/main.py), and I see no easy way to customize ChangeList class or search mechanics without massive copy/pasting. I thought that Django admin is more powerful than it is.

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

1 个解决方案

#1


You could instruct the ORM to add an extra field to your SQL queries, like so:

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

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

This adds SELECT inet_ntoa(ip_address) as ip to the query and a field ip to your objects. You can use the new synthesized field in your WHERE clause.

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

Are you sure you don't really want something like WHERE (ip_address & 0xffff0000) = inet_aton('192.168.0.0')? Or do you really want to find all ip addresses in your log that contain the number 119 somewhere?

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

If suffix is the /24 from CIDR notation:

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

mask = 0xffffffff ^ 0xffffffff >> suffix

Add to the WHERE clause:

添加到WHERE子句:

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

#1


You could instruct the ORM to add an extra field to your SQL queries, like so:

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

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

This adds SELECT inet_ntoa(ip_address) as ip to the query and a field ip to your objects. You can use the new synthesized field in your WHERE clause.

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

Are you sure you don't really want something like WHERE (ip_address & 0xffff0000) = inet_aton('192.168.0.0')? Or do you really want to find all ip addresses in your log that contain the number 119 somewhere?

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

If suffix is the /24 from CIDR notation:

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

mask = 0xffffffff ^ 0xffffffff >> suffix

Add to the WHERE clause:

添加到WHERE子句:

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