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

时间:2011-05-14 00:40:13

标签: python django django-models django-queryset

我有一个自定义模型字段YearWithSurenessField,它由python中的自定义数据类型YearWithSureness表示。 YearWithSureness的构造函数为YearWithSureness(year='', is_certain=False),其中year''或四位数年份(作为字符串),is_certain为bool代表我是否确定给定的年份是正确的。此类型的模型字段以 year / is_certain 的形式存储在我的数据库中,例如“2008 / True”,“2011 / False”,“/ False”等

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

我希望能够使用

之类的东西
Member.objects.filter(grad_year__year=2011)

获得所有成员QuerySet,其中grad_year为“2011 / True”或“2011 / False”。同样,我希望能够使用像

这样的东西
Member.objects.filter(grad_year__range=(2000, 2011))

获取其所有成员的QuerySet,其中grad_year的范围是2000至2011年,无论grad_year.is_certain是真还是假。

这可能吗?我知道我可以使用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 :(得分:3)

我无法理解你为什么需要这样的自定义字段。据我所知,'year'和'is_certain'非常适合存储在2个独立的字段中。通过这样做,首先,按年份或年份范围搜索更容易。其次,搜索也将显着提高效率,尤其是在存在大量数据时。最后但并非最不重要的是,您不再需要打扰如何正确实现自定义字段。

因此,我建议您解释为什么需要将这两种天然不同类型的数据存储到数据库表中的单个列中的根本原因。也许我们可以指出一种更简单的方法来实现你的真正目标。

答案 1 :(得分:3)

这里有一些我觉得有用的东西: Creating custom Field Lookups in Django

更灵活的方法是编写自定义QuerySet以及自定义管理器。使用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)

答案 2 :(得分:0)

您是否尝试更改get_prep_lookup的行为,以便在lookup_type =='year'时仅返回年份值?你可以返回int(value.split('/')[0])

我不确定实现这样的自定义字段是最好的选择,是否有充分的理由避免将值拆分为两个独立的字段?