Đây cũng là một thủ thuật thú vị:
class DefaultSelectOrPrefetchManager(models.Manager):
def __init__(self, *args, **kwargs):
self._select_related = kwargs.pop('select_related', None)
self._prefetch_related = kwargs.pop('prefetch_related', None)
super(DefaultSelectOrPrefetchManager, self).__init__(*args, **kwargs)
def get_queryset(self, *args, **kwargs):
qs = super(DefaultSelectOrPrefetchManager, self).get_queryset(*args, **kwargs)
if self._select_related:
qs = qs.select_related(*self._select_related)
if self._prefetch_related:
qs = qs.prefetch_related(*self._prefetch_related)
return qs
class Sandwich(models.Model):
bread = models.ForeignKey(Bread)
extras = models.ManyToManyField(Extra)
# ...
objects = DefaultSelectOrPrefetchManager(select_related=('bread',), prefetch_related=('extras',))
Sau đó, bạn có thể tái sử dụng người quản lý dễ dàng giữa các lớp mô hình. Ví dụ như trường hợp sử dụng, điều này sẽ phù hợp nếu bạn có phương thức __unicode__
trên mô hình đã hiển thị chuỗi bao gồm một số thông tin từ mô hình liên quan (hoặc bất kỳ điều gì khác có nghĩa là mô hình liên quan gần như luôn yêu cầu).
... và nếu bạn thực sự muốn nhận được lập dị, đây là phiên bản tổng quát hơn. Nó cho phép bạn gọi bất kỳ chuỗi phương thức nào trên truy vấn mặc định với bất kỳ kết hợp nào của args
hoặc kwargs
. Có thể có một số lỗi trong mã, nhưng bạn có ý tưởng.
from django.db import models
class MethodCalls(object):
"""
A mock object which logs chained method calls.
"""
def __init__(self):
self._calls = []
def __getattr__(self, name):
c = Call(self, name)
self._calls.append(c)
return c
def __iter__(self):
for c in self._calls:
yield tuple(c)
class Call(object):
"""
Used by `MethodCalls` objects internally to represent chained method calls.
"""
def __init__(self, calls_obj, method_name):
self._calls = calls_obj
self.method_name = method_name
def __call__(self, *method_args, **method_kwargs):
self.method_args = method_args
self.method_kwargs = method_kwargs
return self._calls
def __iter__(self):
yield self.method_name
yield self.method_args
yield self.method_kwargs
class DefaultQuerysetMethodCallsManager(models.Manager):
"""
A model manager class which allows specification of a sequence of
method calls to be applied by default to base querysets.
`DefaultQuerysetMethodCallsManager` instances expose a property
`default_queryset_method_calls` to which chained method calls can be
applied to indicate which methods should be called on base querysets.
"""
def __init__(self, *args, **kwargs):
self.default_queryset_method_calls = MethodCalls()
super(DefaultQuerysetMethodCallsManager, self).__init__(*args, **kwargs)
def get_queryset(self, *args, **kwargs):
qs = super(DefaultQuerysetMethodCallsManager, self).get_queryset(*args, **kwargs)
for method_name, method_args, method_kwargs in self.default_queryset_method_calls:
qs = getattr(qs, method_name)(*method_args, **method_kwargs)
return qs
class Sandwich(models.Model):
bread = models.ForeignKey(Bread)
extras = models.ManyToManyField(Extra)
# Other field definitions...
objects = DefaultQuerysetMethodCallsManager()
objects.default_queryset_method_calls.filter(
bread__type='wheat',
).select_related(
'bread',
).prefetch_related(
'extras',
)
Đối tượng python-mock lấy cảm hứng MethodCalls
là một nỗ lực làm cho API tự nhiên hơn. Một số có thể thấy rằng một chút bối rối. Nếu có, bạn có thể chỉ ra mã đó cho số __init__
arg hoặc kwarg mà chỉ chấp nhận một bộ thông tin cuộc gọi phương thức.
Làm cách nào để thực hiện điều này khi sử dụng models.QuerySet? –
Tôi nghĩ bạn cần cung cấp thêm thông tin, hoặc một số mã ví dụ, theo ý bạn; nếu bạn có QuerySet, thì bạn có thể gọi trực tiếp 'select_related'. –
Tôi có nghĩa là nếu bạn sử dụng các đối tượng = MyQuerySet.as_manager() –