2016-10-13 13 views
9

Tôi có hai kiểu: Citybí danhCityAlias. Mô hình CityAlias chứa tất cả các tên trong số City, cộng với bí danh. Những gì tôi muốn là bất cứ khi nào City được tìm kiếm theo name, mô hình CityAlias sẽ được truy vấn. Đây là những gì tôi đã đưa ra:Tra cứu chuỗi thông qua bộ truy vấn

class CityQuerySet(models.QuerySet): 
    """ If City is searched by name, search it in CityAlias """ 
    def _search_name_in_alias(self, args, kwargs): 
     for q in args: 
      if not isinstance(q, models.Q): continue 
      for i, child in enumerate(q.children): 
       # q.children is a list of tuples of queries: 
       # [('name__iexact', 'calcutta'), ('state__icontains', 'bengal')] 
       if child[0].startswith('name'): 
        q.children[i] = ('aliases__%s' % child[0], child[1]) 

     for filter_name in kwargs: 
      if filter_name.startswith('name'): 
       kwargs['aliases__%s' % filter_name] = kwargs.pop(filter_name) 

    def _filter_or_exclude(self, negate, *args, **kwargs): 
     # handles 'get', 'filter' and 'exclude' methods 
     self._search_name_in_alias(args=args, kwargs=kwargs) 
     return super(CityQuerySet, self)._filter_or_exclude(negate, *args, **kwargs) 


class City(models.Model): 
    name = models.CharField(max_length=255, db_index=True) 
    state = models.ForeignKey(State, related_name='cities') 
    objects = CityQuerySet.as_manager() 

class CityAlias(models.Model): 
    name = models.CharField(max_length=255, db_index=True) 
    city = models.ForeignKey(City, related_name='aliases') 

Ví dụ: Kolkata sẽ có một mục trong City mô hình, và nó sẽ có hai mục trong mô hình CityAlias: KolkataCalcutta. Trên QuerySet cho phép sử dụng tra cứu trên trường name. Vì vậy, hai truy vấn sau sẽ trả về cùng một mục:

City.objects.get(name='Kolkata')  # <City: Kolkata> 
City.objects.get(name__iexact='calcutta') # <City: Kolkata> 

Cho đến nay rất tốt. Nhưng vấn đề nảy sinh khi City là một ForeignKey trong một số mô hình khác:

class Trip(models.Model): 
    destination = models.ForeignKey(City) 
    # some other fields.... 

Trip.objects.filter(destination__name='Kolkata').count() # some non-zero number 
Trip.objects.filter(destination__name='Calcutta').count() # always returns zero 

Django nội bộ xử lý những tham gia khác nhau, và không gọi phương thức get_queryset của người quản lý City 's. Cách khác là để gọi các truy vấn trên như sau:

Trip.objects.filter(destination=City.objects.get(name='Calcutta')) 

Câu hỏi của tôi là tôi có thể làm điều gì đó, vì vậy mà tuy nhiên mô hình City được tìm kiếm bởi name, nó luôn luôn tìm kiếm trong bảng CityAlias để thay thế? Hoặc có cách nào khác tốt hơn để triển khai chức năng tôi yêu cầu không?

+0

Bạn có quá phức tạp với vấn đề của mình không? Bạn có thể chỉ đơn giản là có Trip.destination là một ngoại ngữ cho thành phố? – dkarchmer

+0

Mô hình 'Thành phố' cũng sẽ có các trường khác (như định vị địa lý) .. do đó, nó có nghĩa là sử dụng 'CityAlias' khi lọc theo tên và' Thành phố' khi các bộ lọc khác được áp dụng. – jatinderjit

+0

Đã thử điều này: Trip.objects.filter (destination__aliases__name = 'Calcutta'). Count() –

Trả lời

-2

Tôi tin rằng bạn có thể sử dụng select_related() một cái gì đó như thế này:

def search_city(name): 
    """ 
    input: name (str) 
    output: queryset of City objects 
    """ 
    return CityAlias.filter(name=name).selected_related('city') 
+0

'select_related' là một tối ưu hóa .. queryset đầu ra sẽ chỉ có' ​​CityAlias' (nhưng nó sẽ tìm nạp 'City' fields cũng trong cùng một truy vấn cơ sở dữ liệu). – jatinderjit

2

Tôi đã cố gắng sử dụng Custom Lookups nhưng dường như bạn không thể thêm một bảng vào tham gia danh sách. (Vâng, bạn có thể thêm thêm ({"table": ...}) vào trình quản lý của mô hình nhưng nó không phải là một giải pháp thanh lịch).

Vì vậy, tôi muốn đề nghị bạn:

1) Giữ luôn tên thành phố 'chính/ưa thích' của bạn cũng như một CityAlias. Vì vậy, siêu dữ liệu của thành phố sẽ ở trong Thành phố ... nhưng tất cả thông tin đặt tên sẽ nằm trong CityAlias. (và có thể thay đổi tên)

Bằng cách này, tất cả các tra cứu sẽ xảy ra trong bảng đó. Bạn có thể có một boolean để đánh dấu cá thể nào là bản gốc/ưa thích.

class City(models.Model): 
    state = models.ForeignKey(State, related_name='cities') 
    [...] 

class CityAlias(models.Model): 
    city = models.ForeignKey(City, related_name='aliases') 
    name = models.CharField(max_length=255, db_index=True) 

2) Nếu bạn đang nghĩ về bản dịch ... Bạn có nghĩ về ứng dụng django-modeltranslation không?

Trong trường hợp này, nó sẽ tạo ra một trường cho mỗi ngôn ngữ và nó sẽ luôn tốt hơn là tham gia.

3) Hoặc, nếu bạn đang sử dụng PostgreSQL và bạn đang nghĩ về "các bản dịch khác nhau cho cùng tên thành phố" (và tôi đang suy nghĩ với phiên âm từ tiếng Hy Lạp hoặc tiếng Nga), có thể bạn sử dụng PostgreSQL dictionaries , trigrams với các điểm giống nhau, v.v. Hoặc thậm chí trong trường hợp này, cách tiếp cận thứ nhất.

+0

1) Boolean cho trường ưa thích là giống nhau (và kém hiệu quả hơn) khi có trường đó trong chính mô hình Thành phố. Tất cả các tra cứu trong mô hình CityAlias ​​là chính xác những gì muốn thực hiện, mà không có ai khác lo lắng về bảng Alias. 2) Không thực sự tìm kiếm bản dịch 3) sẽ xem xét những điều này, nhưng không chắc chắn làm thế nào họ sẽ giúp .. – jatinderjit

4

Tôi nghĩ rằng đó là tốt hơn (và pythonic hơn) để được rõ ràng những gì bạn yêu cầu trong suốt thay vì cố gắng để làm ảo thuật trong quản lý và do đó:

City.objects.get(aliases__name__iexact='calcutta') # side note: this can return many (same in original) so you need to catch that 

Và:

Trip.objects.filter(destination__aliases__name='Calcutta').count() 
+0

Nếu giải pháp "ma thuật" là có thể, tại sao không! Giải pháp này là những gì tôi đã cố gắng để tránh, nhưng cảm ơn tôi đã thoái lui với điều này ngay bây giờ .. – jatinderjit

+0

Tại sao không? Bởi vì nó sẽ làm cho mã khó hiểu và duy trì. Giữ nó đơn giản và làm việc với khung của bạn không xoay quanh nó. Phần mềm tốt là về giải quyết các vấn đề kinh doanh không hiển thị các thủ thuật. –

1

Nói về việc giữ nó đơn giản. Tại sao không chỉ cho mô hình Thành phố một trường char 'CityAlias' có chứa chuỗi? Nếu tôi hiểu câu hỏi của bạn một cách chính xác, đây là giải pháp đơn giản nhất nếu bạn chỉ cần một bí danh cho mỗi thành phố. Nó chỉ nhìn tôi như thể bạn đang làm phức tạp một vấn đề đơn giản.

class City(models.Model): 
    name = models.CharField(max_length=255, db_index=True) 
    state = models.ForeignKey(State, related_name='cities') 
    alias = models.CharField(max_length=255) 

c = City.objects.get(alias='Kolkata') 

>>>c.name 
Calcutta 
>>>c.alias 
Kolkata 
+0

Ngoại trừ một 'Thành phố' có thể có nhiều bí danh ... và tôi muốn bộ truy vấn trả lại cùng một bộ truy vấn nếu mô hình được tìm kiếm bởi bất kỳ bí danh nào (hoặc tên gốc). 'City.objects.get (name = x)' sẽ trả lại kết quả tương tự nếu giá trị của 'x' là Calcutta hoặc Kolkata (hoặc bất kỳ bí danh nào khác nếu có). – jatinderjit

+0

Không có bộ lọc City.CityAlias_set.all(). (Name__iexact = x) sẽ không làm gì? – Xeberdee

Các vấn đề liên quan