Django如何按字段重写模型匹配

时间:2021-11-03 06:13:30

I am using django 1.5.5 and python 2.6

我使用的是django 1.5.5和python 2.6。

I have this model

我有这个模型

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

I want to customize the model so that site with url that has 'www' prefix and site that dont will return the same object.
So if i have a site with url='http://foo.com' all of the following will return the same object

我想要自定义模型,使具有“www”前缀和不返回相同对象的站点具有url。因此,如果我有一个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')

I was thinking of making a class method like.

我想做一个类方法。

@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

But i am sure there is a better way so i can keep using objects.get and objects.filter

但是我确信有更好的方法,这样我就可以继续使用对象。获取和objects.filter

UPDATE:

As suggested by Gonzalo Delgado i made a custom model manager

正如冈萨洛·德尔加多(Gonzalo Delgado)所建议的,我做了一个定制模型经理

here is my code

这是我的代码

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)

This works good the only problem now is that if i use any kind of Field lookups then it does not use th new logic.
so any kind of url__in, url__contains, etc does not work.
I am sure there is a better way than to implement each of the filed lookups available in django.

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

2 个解决方案

#1


1  

You'll need to create a custom manager and extend the get and filter methods:

您需要创建一个自定义管理器并扩展get和filter方法:

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()

#2


0  

I think i found a solution. it is not perfect but i think it is good enough for me.
It is based on Gonzalo Delgado answer.

我想我找到解决办法了。它并不完美,但我认为它对我来说已经足够了。这是基于贡萨洛·德尔加多的回答。

here is my code:

这是我的代码:

from django.db import models
from django.db.models import Q

def url_variants(urls):
    if isinstance(urls, basestring): # if it is not a list then make a list
        urls = [urls]
    variants = []
    for url in urls:
        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
        variants += [ prefix+url for prefix in prefixes]
    return variants

def variants_filter(variants,lookup):        
    q_list = [Q(**{ lookup: n}) for n in variants]
    q_list = reduce(lambda a, b: a | b, q_list)
    return (q_list,)

class SiteManager(models.Manager):
    def filter(self, *args, **kwargs):
    # find keys that contain 'url'        
        for key in kwargs:
            if key.startswith('url'):                
                variants = url_variants(kwargs[key])                                            
                args = variants_filter(variants, key.replace('__in','')) + args # if there is an '__in' then remove it we already have list support            
                kwargs.pop(key, None)                
                break            
        return super(SiteManager, self).filter(*args, **kwargs)

#1


1  

You'll need to create a custom manager and extend the get and filter methods:

您需要创建一个自定义管理器并扩展get和filter方法:

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()

#2


0  

I think i found a solution. it is not perfect but i think it is good enough for me.
It is based on Gonzalo Delgado answer.

我想我找到解决办法了。它并不完美,但我认为它对我来说已经足够了。这是基于贡萨洛·德尔加多的回答。

here is my code:

这是我的代码:

from django.db import models
from django.db.models import Q

def url_variants(urls):
    if isinstance(urls, basestring): # if it is not a list then make a list
        urls = [urls]
    variants = []
    for url in urls:
        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
        variants += [ prefix+url for prefix in prefixes]
    return variants

def variants_filter(variants,lookup):        
    q_list = [Q(**{ lookup: n}) for n in variants]
    q_list = reduce(lambda a, b: a | b, q_list)
    return (q_list,)

class SiteManager(models.Manager):
    def filter(self, *args, **kwargs):
    # find keys that contain 'url'        
        for key in kwargs:
            if key.startswith('url'):                
                variants = url_variants(kwargs[key])                                            
                args = variants_filter(variants, key.replace('__in','')) + args # if there is an '__in' then remove it we already have list support            
                kwargs.pop(key, None)                
                break            
        return super(SiteManager, self).filter(*args, **kwargs)