2010-02-04 36 views
11

Làm cách nào để tạo tùy chỉnh field lookups trong Django?Tạo các trường tra cứu tùy chỉnh ở Django

Khi lọc queryset, django cung cấp một tập hợp tra cứu mà bạn có thể sử dụng: __contains, __iexact, __in, v.v. Tôi muốn để có thể cung cấp một tra cứu mới cho người quản lý của tôi, vì vậy ví dụ, ai đó có thể nói:

twentysomethings = Person.objects.filter(age__within5=25) 

và lấy lại tất cả các đối tượng Person với độ tuổi từ 20 đến 30. Tôi có cần phải phân lớp QuerySet hoặc Manager lớp học để làm điều này? Làm thế nào nó sẽ được thực hiện?

+3

Những câu trả lời này rất hữu ích cho ví dụ của anh ấy (có thể hoặc không phải là điều anh ấy chỉ muốn nói ra)… nhưng tôi rất thích nếu ai đó trả lời câu hỏi thực sự được hỏi. – royal

+1

@royal Tôi nghĩ rằng câu trả lời của tôi bao gồm nó- Tôi đã có điều này xuất hiện trong một thư viện tôi đang làm việc. –

Trả lời

1

Kể từ Django 1.7, có một cách đơn giản để triển khai. Ví dụ của bạn là thực sự rất giống với một từ the documentation:

from django.db.models import Lookup 

class AbsoluteValueLessThan(Lookup): 
    lookup_name = 'lt' 

    def as_sql(self, qn, connection): 
     lhs, lhs_params = qn.compile(self.lhs.lhs) 
     rhs, rhs_params = self.process_rhs(qn, connection) 
     params = lhs_params + rhs_params + lhs_params + rhs_params 
     return '%s < %s AND %s > -%s' % (lhs, rhs, lhs, rhs), params 

AbsoluteValue.register_lookup(AbsoluteValueLessThan) 

Trong khi đăng ký, bạn chỉ có thể sử dụng Field.register_lookup(AbsoluteValueLessThan) để thay thế.

+0

tôi nên làm điều đó ở đâu? – user1735921

+0

@ user1735921 không thể nhớ nữa. Làm thế nào về trong models.py? – d33tah

+0

hey người đàn ông bạn có thể vui lòng kiểm tra câu hỏi của tôi tại: http://stackoverflow.com/questions/41186414/how-to-put-in-string-in-django, không ai có thể giải quyết nó – user1735921

6

Thay vì tạo ra một tra cứu lĩnh vực, thực hành tốt nhất là để tạo ra một phương pháp quản lý, điều đó có thể trông một chút như thế này:

class PersonManger(models.Manager): 
    def in_age_range(self, min, max): 
     return self.filter(age__gte=min, age__lt=max) 

class Person(models.Model): 
    age = #... 

    objects = PersonManager() 

sau đó sử dụng sẽ như vậy:

twentysomethings = Person.objects.in_age_range(20, 30) 
12

Cách linh hoạt hơn để thực hiện việc này là viết QuerySet tùy chỉnh cũng như trình quản lý tùy chỉnh. Làm việc từ mã của ozan:

class PersonQuerySet(models.query.QuerySet): 
    def in_age_range(self, min, max): 
     return self.filter(age__gte=min, age__lt=max) 

class PersonManager(models.Manager): 
    def get_query_set(self): 
     return PersonQuerySet(self.model) 

    def __getattr__(self, name): 
     return getattr(self.get_query_set(), name) 

class Person(models.Model): 
    age = #... 

    objects = PersonManager() 

Điều này cho phép bạn kết nối truy vấn tùy chỉnh của mình. Vì vậy, cả hai truy vấn này sẽ hợp lệ:

Person.objects.in_age_range(20,30) 

Person.objects.exclude(somefield = some_value).in_age_range(20, 30) 
+0

Thật tuyệt vời! Tôi vẫn hy vọng có một số API cho hooking vào cú pháp lĩnh vực tra cứu, để tận dụng lợi thế của logic traversal rlationship. Tôi sẽ tiếp tục đào bới. Trong khi chờ đợi, tôi nghĩ điều này giúp tôi tiến gần hơn một chút để hiểu được các mảnh ghép lại với nhau như thế nào. Cảm ơn! – jcdyer

+0

Bạn có thể làm điều này dễ dàng hơn/dễ dàng hơn với [django-qmethod] (https://github.com/zacharyvoase/django-qmethod). Dường như nó đã không được cập nhật trong một thời gian nhưng tôi đã sử dụng nó trong nhiều năm và không bao giờ gặp bất kỳ rắc rối nào. – Dave

6

Trước tiên, hãy để tôi nói rằng không có máy móc Django nào được đặt để công khai tạo điều kiện bạn muốn.

(Edit - thực sự kể từ Django 1,7 có: https://docs.djangoproject.com/en/1.7/howto/custom-lookups/)

Điều đó nói rằng, nếu bạn thực sự muốn thực hiện điều này, lớp con QuerySet và ghi đè lên các phương pháp _filter_or_exclude(). Sau đó, tạo trình quản lý tùy chỉnh chỉ trả về tùy chỉnh QuerySet (hoặc bản vá lỗi của khỉ Django's QuerySet, yuck). Chúng tôi làm điều này trong neo4django để tái sử dụng càng nhiều mã truy vấn ORM của Django càng tốt trong khi xây dựng các đối tượng Query cụ thể Neo4j.

Hãy thử một cái gì đó (gần) như thế này, được điều chỉnh từ câu trả lời của Zach. Tôi đã rời lỗi thực tế xử lý đối với các phân tích cú pháp tra cứu lĩnh vực như một bài tập cho người đọc :)

class PersonQuerySet(models.query.QuerySet): 
    def _filter_or_exclude(self, negate, *args, **kwargs): 
     cust_lookups = filter(lambda s: s[0].endswith('__within5'), kwargs.items()) 
     for lookup in cust_lookups: 
      kwargs.pop(lookup[0]) 
      lookup_prefix = lookup[0].rsplit('__',1)[0] 
      kwargs.update({lookup_prefix + '__gte':lookup[1]-5, 
          lookup_prefix + '__lt':lookup[1]+5}) 
     return super(PersonQuerySet, self)._filter_or_exclude(negate, *args, **kwargs) 

class PersonManager(models.Manager): 
    def get_query_set(self): 
     return PersonQuerySet(self.model) 

class Person(models.Model): 
    age = #... 

    objects = PersonManager() 

Cuối cùng nhận xét - rõ ràng, nếu bạn muốn tra cứu lĩnh vực chuỗi tùy chỉnh, điều này sẽ nhận được khá nhiều lông. Ngoài ra, tôi thường viết này một chút chức năng hơn và sử dụng itertools cho hiệu suất, nhưng nghĩ rằng nó là rõ ràng hơn để bỏ nó ra. Chúc vui vẻ!

+1

Cảm ơn dude, điều này rất hữu ích. – nisc

+0

Rất vui khi ai đó đang sử dụng nó! –

+1

Chỉ tìm thấy câu trả lời của bạn! Cảm ơn rất nhiều vì điều này. Đó là những gì tôi đang tìm kiếm. – jcdyer

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