【问题标题】:Django how do i override model match by fieldDjango如何按字段覆盖模型匹配
【发布时间】:2014-01-03 20:05:39
【问题描述】:

我正在使用 django 1.5.5 和 python 2.6

我有这个模型

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

我想自定义模型,以便具有“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')

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

@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.getobjects.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__inurl__contains 等都不起作用。
我确信有比实现 django 中可用的每个归档查找更好的方法。

【问题讨论】:

  • 您可能想要覆盖 get_absolute_url 方法而不是将其保存到数据库中。阅读更多关于它的信息here
  • 另外,Site.objects.filter(url__endswith='foo.com') 有什么问题?这将匹配两种情况
  • @yuvi 两者都会匹配,但也会匹配 'notmy_foo.com'
  • 可能有一种方法可以使用正则表达式。等等,我会挖一点

标签: python django orm model


【解决方案1】:

您需要创建一个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()

【讨论】:

  • 当关键字是 'url__exact' 或 'url__in' 时,你会怎么做?我如何涵盖所有选项?
  • 您无能为力,您可以遍历所有kwargs 键并检查url 和每个field lookup。此外,我的解决方案不涵盖使用 Q 对象的查询,因此如果您需要涵盖所有选项,它将无法工作。如果这是一个强烈的要求,我猜你需要创建一个自定义查询集子类来解析 *args 和 **kwargs(可能在 _filter_or_exclude 方法中)并使管理器返回该查询集子类的实例。
【解决方案2】:

我想我找到了解决方案。它并不完美,但我认为它对我来说已经足够了。
它基于 Gonzalo Delgado 的回答。

这是我的代码:

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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多