django基于动态property的查询()

发布于 2024-10-14 02:34:11 字数 640 浏览 1 评论 0原文

我想知道是否有一种方法可以使用 property() 动态生成的 python 属性在查询集上使用 Django 的 filter() 。我有每个用户的 first_namelast_name,我想根据他们的串联名称 first_name last_name 进行过滤。 (这背后的原因是,当我执行自动完成时,我会搜索查询是否与名字、姓氏或连接的一部分匹配。我希望 John S 匹配 John Smith<例如,

我创建了 name 的属性:

def _get_name(self):
    return self.first_name + " " + self.last_name
name = property(_get_name)

名称。

这样我可以调用 user.name 来获取连接的 code>User.objects.filter(name__istartswith=query) 我收到错误 无法将关键字“name”解析到字段中

关于如何执行此操作的任何想法?数据库中的字段存储全名?

I was wondering if there was a way to use Django's filter() on query sets using a dynamically generated python property using property(). I have first_name and last_name of every user, and I want to filter based on their concatenated name first_name last_name. (The reason behind this is that when I do autocomplete I search to see if the query matches first name, last name, or part of the concatenation. I want John S to match John Smith, for example.

I created a property of name:

def _get_name(self):
    return self.first_name + " " + self.last_name
name = property(_get_name)

This way I can call user.name to get the concatenated name.

However, if I try to do User.objects.filter(name__istartswith=query) I get the error Cannot resolve keyword 'name' into field.

Any ideas on how to do this? Do I have to create another field in the database to store the full name?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

空城之時有危險 2024-10-21 02:34:11

接受的答案并不完全正确。

在许多情况下,您可以在模型管理器中重写 get() 以从关键字参数中 pop 动态属性,然后将要查询的实际属性添加到 < code>kwargs 关键字参数字典。请务必返回 super,以便任何常规 get() 调用都会返回预期结果。

我只是粘贴我自己的解决方案,但对于 __startswith 和其他条件查询,您可以添加一些逻辑来分割双下划线并进行适当处理。

这是我的解决方法,允许通过动态属性进行查询:

class BorrowerManager(models.Manager):
    def get(self, *args, **kwargs):
        full_name = kwargs.pop('full_name', None)
        # Override #1) Query by dynamic property 'full_name'
        if full_name:
            names = full_name_to_dict(full_name)
            kwargs = dict(kwargs.items() + names.items())
        return super(BorrowerManager, self).get(*args, **kwargs)

在 models.py 中:

class Borrower(models.Model):
    objects = BorrowerManager()

    first_name = models.CharField(null=False, max_length=30)
    middle_name = models.CharField(null=True, max_length=30)
    last_name = models.CharField(null=False, max_length=30)
    created = models.DateField(auto_now_add=True)

在 utils.py 中(为了上下文):

def full_name_to_dict(full_name):
    ret = dict()
    values = full_name.split(' ')
    if len(values) == 1:
        raise NotImplementedError("Not enough names to unpack from full_name")
    elif len(values) == 2:
        ret['first_name'] = values[0]
        ret['middle_name'] = None
        ret['last_name'] = values[1]
        return ret
    elif len(values) >= 3:
        ret['first_name'] = values[0]
        ret['middle_name'] = values[1:len(values)-1]
        ret['last_name'] = values[len(values)-1]
        return ret
    raise NotImplementedError("Error unpacking full_name to first, middle, last names")

The accepted answer is not entirely true.

For many cases, you can override get() in the model manager to pop dynamic properties from the keyword arguments, then add the actual attributes you want to query against into the kwargs keyword arguments dictionary. Be sure to return a super so any regular get() calls return the expected result.

I'm only pasting my own solution, but for the __startswith and other conditional queries you could add some logic to split the double-underscore and handle appropriately.

Here was my work-around to allow querying by a dynamic property:

class BorrowerManager(models.Manager):
    def get(self, *args, **kwargs):
        full_name = kwargs.pop('full_name', None)
        # Override #1) Query by dynamic property 'full_name'
        if full_name:
            names = full_name_to_dict(full_name)
            kwargs = dict(kwargs.items() + names.items())
        return super(BorrowerManager, self).get(*args, **kwargs)

In models.py:

class Borrower(models.Model):
    objects = BorrowerManager()

    first_name = models.CharField(null=False, max_length=30)
    middle_name = models.CharField(null=True, max_length=30)
    last_name = models.CharField(null=False, max_length=30)
    created = models.DateField(auto_now_add=True)

In utils.py (for the sake of context):

def full_name_to_dict(full_name):
    ret = dict()
    values = full_name.split(' ')
    if len(values) == 1:
        raise NotImplementedError("Not enough names to unpack from full_name")
    elif len(values) == 2:
        ret['first_name'] = values[0]
        ret['middle_name'] = None
        ret['last_name'] = values[1]
        return ret
    elif len(values) >= 3:
        ret['first_name'] = values[0]
        ret['middle_name'] = values[1:len(values)-1]
        ret['last_name'] = values[len(values)-1]
        return ret
    raise NotImplementedError("Error unpacking full_name to first, middle, last names")
混吃等死 2024-10-21 02:34:11

认为在 django 中不可能过滤不作为数据库字段出现的属性,但是您可以执行以下操作来进行很酷的自动完成搜索:

if ' ' in query:
    query = query.split()
    search_results = list(chain(User.objects.filter(first_name__icontains=query[0],last_name__icontains=query[1]),
                                    User.objects.filter(first_name__icontains=query[1],last_name__icontains=query[0])))
else:
    search_results =  User.objects.filter(Q(first_name__icontains=query)| Q(last_name__icontains=query))

此代码为系统的用户提供了开始输入名字的灵活性或姓氏,用户将感谢您允许这样做。

Think it's not possible in django to filter on properties that does not present as a database filed, but what you can do to make cool autocomplete search is something like this:

if ' ' in query:
    query = query.split()
    search_results = list(chain(User.objects.filter(first_name__icontains=query[0],last_name__icontains=query[1]),
                                    User.objects.filter(first_name__icontains=query[1],last_name__icontains=query[0])))
else:
    search_results =  User.objects.filter(Q(first_name__icontains=query)| Q(last_name__icontains=query))

This code gives the user of your system a flexibility to start typing either first name or last name and the user will be thankful to you for allowing this.

彼岸花ソ最美的依靠 2024-10-21 02:34:11

filter() 在数据库级别运行(它实际上编写 SQL),因此不可能将它用于基于您的 python 代码的任何查询(问题中的动态属性)

这是本部门许多其他答案汇总的答案:)

filter() operates on the database level (it actually writes SQL), so it won't be possible to use it for any queries based on your python code (dynamic property in your question).

This is an answer put together from many other answers in this department : )

琉璃繁缕 2024-10-21 02:34:11

我有类似的问题,正在寻找解决方案。理所当然地认为搜索引擎是最好的选择(例如 django-haystackElasticsearch),这就是我仅使用 Django ORM 实现一些满足您需求的代码的方式(您可以将 icontains 替换为 istartswith):

from django.db.models import Value
from django.db.models.functions import Concat

queryset = User.objects.annotate(full_name=Concat('first_name', Value(' '), 'last_name')
return queryset.filter(full_name__icontains=value)

在我的例子中,我不知道用户是否会插入 'first_name last_name< /code>' 或反之亦然,所以我使用了以下代码。

from django.db.models import Q, Value
from django.db.models.functions import Concat

queryset = User.objects.annotate(first_last=Concat('first_name', Value(' '), 'last_name'), last_first=Concat('last_name', Value(' '), 'first_name'))
return queryset.filter(Q(first_last__icontains=value) | Q(last_first__icontains=value))

对于 Django <1.8,您可能需要使用 SQL CONCAT 函数来使用 extra,如下所示:

queryset.extra(where=['UPPER(CONCAT("auth_user"."last_name", \' \', "auth_user"."first_name")) LIKE UPPER(%s) OR UPPER(CONCAT("auth_user"."first_name", \' \', "auth_user"."last_name")) LIKE UPPER(%s)'], params=['%'+value+'%', '%'+value+'%'])

I had a similar problem and was looking for solution. Taking for granted that a search engine would be the best option (e.g. django-haystack with Elasticsearch), that's how I would implement some code for your needs using only the Django ORM (you can replace icontains with istartswith):

from django.db.models import Value
from django.db.models.functions import Concat

queryset = User.objects.annotate(full_name=Concat('first_name', Value(' '), 'last_name')
return queryset.filter(full_name__icontains=value)

In my case I didn't know whether the user would insert 'first_name last_name' or viceversa, so I used the following code.

from django.db.models import Q, Value
from django.db.models.functions import Concat

queryset = User.objects.annotate(first_last=Concat('first_name', Value(' '), 'last_name'), last_first=Concat('last_name', Value(' '), 'first_name'))
return queryset.filter(Q(first_last__icontains=value) | Q(last_first__icontains=value))

With Django <1.8, you would probably need to resort to extra with the SQL CONCAT function, something like the following:

queryset.extra(where=['UPPER(CONCAT("auth_user"."last_name", \' \', "auth_user"."first_name")) LIKE UPPER(%s) OR UPPER(CONCAT("auth_user"."first_name", \' \', "auth_user"."last_name")) LIKE UPPER(%s)'], params=['%'+value+'%', '%'+value+'%'])
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文