定制Django自定义模型字段的字段查找功能

3 投票
3 回答
1443 浏览
提问于 2025-04-16 17:37

我有一个自定义的模型字段叫做 YearWithSurenessField,在 Python 中用一个叫 YearWithSureness 的自定义数据类型来表示。这个 YearWithSureness 的构造函数是 YearWithSureness(year='', is_certain=False),其中 year 可以是空字符串 '' 或者一个四位数的年份(以字符串形式),而 is_certain 是一个布尔值,用来表示我是否确定这个年份是正确的。这个类型的模型字段在我的数据库中以 year/is_certain 的形式存储,比如 "2008/True"、"2011/False"、"/False" 等等。

举个例子,在 Member 模型中,我有一个字段 grad_year = YearWithSurenessField(...),它用来存储一个成员的毕业年份,以及我是否确定我存储的年份是正确的。

我想要做的是使用类似下面的代码

Member.objects.filter(grad_year__year=2011)

来获取所有毕业年份为 "2011/True" 或 "2011/False" 的成员的 QuerySet。同样,我也想用类似下面的代码

Member.objects.filter(grad_year__range=(2000, 2011))

来获取所有毕业年份在 2000 到 2011 之间的成员,不管 grad_year.is_certain 是 True 还是 False。

这样做可以吗?我知道我可以用 Member.objects.filter(grad_year__contains="2011") 来获取第一个结果,但我想使用 __year。

以下是相关的类,去掉了一些多余的代码:

class YearWithSureness(object):
    def __init__(self, year='', is_certain=False):
        # ...

    def __str__(self):
        return "{year}/{is_certain}".format(year=self.year,
                                            is_certain=self.is_certain)

class YearWithSurenessField(models.Field):
    __metaclass__ = models.SubfieldBase

    def __init__(self, *args, **kwargs):
        # ...

    def to_python(self, value):
        # ...

    def get_prep_value(self, value):
        # ...

    def get_prep_lookup(self, lookup_type, value):
        if lookup_type in ('month', 'day'):
            raise TypeError('Lookup type {0} not supported.'.format(lookup_type))
        else:
            return super(YearWithSurenessField, self).get_prep_lookup(lookup_type, value)

    def value_to_string(self, obj):
        # ...

3 个回答

0

你有没有尝试过修改 get_prep_lookup 的行为,让它在 lookup_type 等于 'year' 时只返回年份的值?你可以用 int(value.split('/')[0]) 这个方法来实现。

我不太确定实现这样一个自定义字段是否是最好的选择,真的有很好的理由去避免把值分成两个单独的字段吗?

3

我发现了一个很有用的内容:

在Django中创建自定义字段查找

一种更灵活的方法是编写一个自定义的查询集和一个自定义的管理器。我们可以参考ozan的代码:

class PersonQuerySet(models.query.QuerySet):
    def in_age_range(self, min, max):
        return self.filter(age__gte=min, age__lt=max)

class PersonManager(models.Manager):
    def get_query_set(self):
         return PersonQuerySet(self.model)

    def __getattr__(self, name):
        return getattr(self.get_query_set(), name)

class Person(models.Model):
    age = #...
    objects = PersonManager()

这样你就可以把你的自定义查询连接在一起使用。所以这两个查询都是有效的:

Person.objects.in_age_range(20,30)
Person.objects.exclude(somefield = some_value).in_age_range(20, 30)
3

我不太明白你为什么需要这么一个自定义字段。根据我的理解,'year'(年份)和'is_certain'(是否确定)这两个信息其实可以分开存放在两个不同的字段里。这样做有几个好处:首先,按年份或者年份范围来搜索会更简单;其次,搜索的效率也会大大提高,特别是在数据量很大的时候;最后,你也不用再为如何正确实现一个自定义字段而烦恼了。

所以我建议你解释一下,为什么你需要把这两种本来不相关的数据放在数据库表的一个列里。也许我们可以帮你找到一个更简单的方法来实现你的真正目标。

撰写回答