7

SQLAlchemy中,混合属性是应用于 ORM 映射类的属性或方法,

class Interval(Base):
    __tablename__ = 'interval'

    id = Column(Integer, primary_key=True)
    start = Column(Integer, nullable=False)
    end = Column(Integer, nullable=False)

    def __init__(self, start, end):
        self.start = start
        self.end = end

    @hybrid_property
    def length(self):
        return self.end - self.start

    @hybrid_method
    def contains(self,point):
        return (self.start <= point) & (point < self.end)

    @hybrid_method
    def intersects(self, other):
        return self.contains(other.start) | self.contains(other.end)

这允许在类和实例级别有不同的行为,从而使使用相同代码评估 SQL 语句变得更简单,

>>> i1 = Interval(5, 10)
>>> i1.length
5

>>> print Session().query(Interval).filter(Interval.length > 10)
SELECT interval.id AS interval_id, interval.start AS interval_start,
interval."end" AS interval_end
FROM interval
WHERE interval."end" - interval.start > :param_1

现在在Django中,如果我在模型上有一个属性,

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

    def _get_full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)
    full_name = property(_get_full_name)

据我了解,我不能执行以下操作,

Person.objects.filter(full_name="John Cadengo")

Django中是否有相当于SQLAlchemy的混合属性?如果没有,是否有一个常见的解决方法?

4

2 回答 2

3

没错,您不能基于 python 属性应用 django 查询集过滤器,因为过滤器在数据库级别上运行。似乎 Django 中没有与 SQLAlchemy 的混合属性等效的东西。

请看这里这里,可能会帮助您找到解决方法。但是,我认为没有通用的解决方案。

于 2012-10-23T23:10:55.787 回答
3

一种可能的快速解决方法是实现一些描述符,该描述符将通过注释应用表达式。像这样的东西:

from django.db import models
from django.db.models import functions


class hybrid_property:
    def __init__(self, func):
        self.func = func
        self.name = func.__name__
        self.exp = None

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return self.func(instance)

    def __set__(self, instance, value):
        pass

    def expression(self, exp):
        self.exp = exp
        return self


class HybridManager(models.Manager):
    def get_queryset(self):
        qs = super().get_queryset()
        for name, value in vars(qs.model).items():
            if isinstance(value, hybrid_property) and value.exp is not None:
                qs = qs.annotate(**{name: value.exp(qs.model)})
        return qs


class TestAttribute(models.Model):
    val1 = models.CharField(max_length=256)
    val2 = models.CharField(max_length=256)

    objects = HybridManager()

    @hybrid_property
    def vals(self):
        return f"{self.val1} {self.val2}"

    @vals.expression
    def vals(cls):
        return functions.Concat(models.F("val1"), models.Value(" "), models.F("val2"))


class HybridTests(TestCase):
    def setUp(self) -> None:
        self.test_attr = TestAttribute.objects.create(val1="val1", val2="val2")

    def test_access(self):
        self.assertTrue(TestAttribute.objects.exists())
        self.assertEqual(self.test_attr.vals, f"{self.test_attr.val1} {self.test_attr.val2}")
        self.assertTrue(TestAttribute.objects.filter(vals=f"{self.test_attr.val1} {self.test_attr.val2}").exists())
        self.assertTrue(TestAttribute.objects.filter(vals__iexact=f"{self.test_attr.val1} {self.test_attr.val2}").exists())


于 2021-04-03T23:45:07.137 回答