Skip to content

Commit f245293

Browse files
committed
Allow no queryset when get_queryset overridden
The user may wish to provide a dynamic queryset on a `RelatedField` based on the `context`. The way to do that is to create a subclass of `RelatedField` (or a child) and override the `get_queryset` method. However, this is undocumented, and instantiating that field without a `queryset` argument (because it's not needed) will raise an assertion error. Document `.get_queryset(self)` as an official part of the API of `RelatedField`, and don't enforce the use of `queryset` when `get_queryset` is overridden.
1 parent 2704036 commit f245293

File tree

3 files changed

+14
-4
lines changed

3 files changed

+14
-4
lines changed

docs/api-guide/relations.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@ To implement a custom relational field, you should override `RelatedField`, and
330330

331331
If you want to implement a read-write relational field, you must also implement the `.to_internal_value(self, data)` method.
332332

333+
To provide a dynamic queryset based on the `context`, you can also override `.get_queryset(self)` instead of specifying `.queryset` on the class or when initializing the field.
334+
333335
## Example
334336

335337
For example, we could define a relational field to serialize a track to a custom string representation, using its ordering, title, and duration.

rest_framework/relations.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,6 @@ def __init__(self, **kwargs):
6262
self.queryset = kwargs.pop('queryset', self.queryset)
6363
self.html_cutoff = kwargs.pop('html_cutoff', self.html_cutoff)
6464
self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text)
65-
assert self.queryset is not None or kwargs.get('read_only', None), (
66-
'Relational field must provide a `queryset` argument, '
67-
'or set read_only=`True`.'
68-
)
6965
assert not (self.queryset is not None and kwargs.get('read_only', None)), (
7066
'Relational fields should not provide a `queryset` argument, '
7167
'when setting read_only=`True`.'
@@ -112,6 +108,10 @@ def run_validation(self, data=empty):
112108

113109
def get_queryset(self):
114110
queryset = self.queryset
111+
assert queryset is not None, (
112+
'Relational field must provide a `queryset` argument, '
113+
'or set read_only=`True`.'
114+
)
115115
if isinstance(queryset, (QuerySet, Manager)):
116116
# Ensure queryset is re-evaluated whenever used.
117117
# Note that actually a `Manager` class may also be used as the

tests/test_relations.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,14 @@ def test_representation(self):
176176
representation = self.field.to_representation(self.instance)
177177
assert representation == self.instance.name
178178

179+
def test_no_queryset_init(self):
180+
class NoQuerySetSlugRelatedField(serializers.SlugRelatedField):
181+
def get_queryset(this):
182+
return self.queryset
183+
184+
field = NoQuerySetSlugRelatedField(slug_field='name')
185+
field.to_internal_value(self.instance.name)
186+
179187

180188
class TestManyRelatedField(APISimpleTestCase):
181189
def setUp(self):

0 commit comments

Comments
 (0)