簡體   English   中英

Django我如何通過字段覆蓋模型匹配

[英]Django how do i override model match by field

我正在使用Django 1.5.5和python 2.6

我有這個模特

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

我想自定義模型,以便帶有url帶有“ 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.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和filter 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()

我想我找到了解決方案。 它並不完美,但我認為對我來說已經足夠了。
它基於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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM