Skip to main content
 首页 » 编程设计

python之Django 如何按字段覆盖模型匹配

2025年05月04日31insus

我正在使用 django 1.5.5 和 python 2.6

我有这个模型

class Site(models.Model): 
    url = models.CharField(max_length = 512) 

我想自定义模型,以便 url 具有“www”前缀的网站和不具有“www”前缀的网站将返回相同的对象。
因此,如果我有一个网址为 url=' http://foo.com 的网站' 以下所有内容都将返回相同的对象

mysite = Site.objects.get(url__iexact='http://foo.com') 
mysite = Site.objects.get(url__iexact='http://www.foo.com') 
mysite = Site.objects.filter(url__iexact='http://foo.com') 
mysite = Site.objects.filter(url__iexact='http://www.foo.com') 

我正在考虑制作一个类似的类方法。

@classmethod 
def get_site(cls,url): 
    # search for site with url = url 
    if url.startswith('http://www'): 
         # search without www 
    else: 
         # search with www 
    return site 

但我确信有更好的方法可以继续使用 objects.get 和 objects.filter

更新:

按照 Gonzalo Delgado 的建议,我制作了一个自定义模型管理器

这是我的代码

def url_variants(url): 
prefixes = ['http://www.','https://www.','http://','https://',]  # order must be from longest to shortest 
for prefix in prefixes: 
    if url.startswith(prefix): 
        url = url[len(prefix):] 
        break 
return [ prefix+url for prefix in prefixes] 
 
class SiteManager(models.Manager): 
    def filter(self, *args, **kwargs):             
        if 'url' in kwargs: 
            variants = url_variants(url) 
            # in order to chain '__in' and '__iexact' Q is needed 
            q_list = [Q(url__iexact=n) for n in variants] 
            q_list = reduce(lambda a, b: a | b, q_list) 
            args =  (q_list,) + args             
            kwargs.pop("url", None) # remove original Field lookups 
        return super(SiteManager, self).filter(*args, **kwargs) 

这很好用,现在唯一的问题是,如果我使用任何类型的字段查找,那么它不会使用新逻辑。
所以任何类型的 url__in、url__contains 等都不起作用。
我确信有比在 django 中实现每个可用的归档查找更好的方法。

请您参考如下方法:

您需要创建一个 custom manager并扩展 get 和过滤 methods:

class SiteManager(models.Manager): 
    def get(self, *args, **kwargs): 
        if 'url' in kwargs: 
            # handle 'www' prefix here and update kwargs['url'] accordingly 
        return super(SiteManager, self).get(*args, **kwargs) 
 
 
    def filter(self, *args, **kwargs): 
        if 'url' in kwargs: 
            # handle 'www' prefix here and update kwargs['url'] accordingly 
        return super(SiteManager, self).filter(*args, **kwargs) 
 
class Site(models.Model): 
    url = models.CharField(max_length = 512) 
 
    objects = SiteManager()