Repository: encode/django-rest-framework

Issue:
limit_choices_to and Q objects
After upgrading from 3.8 -> 3.9 I've encountered an issue with the new limit_choices_to foreign key changes.

```
filter() argument after ** must be a mapping, not Q
```

It seems the issue was documented in the original merge and a fix was proposed but I can't find an existing PR that has addressed it - nor does the proposed fix exist in master

https://github.com/encode/django-rest-framework/pull/6371#issuecomment-455626461


## Checklist

- [ ] I have verified that that issue exists against the `master` branch of Django REST framework.
- [X] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [X] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [X] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](https://www.django-rest-framework.org/community/third-party-packages/#about-third-party-packages) where possible.)
- [X] I have reduced the issue to the simplest possible case.
- [ ] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)

## Steps to reproduce

model with FK that contains a limit_choices_to with Q object reference ( instead of Dictionary)

## Expected behavior

no errors, code works as expected if a dictionary is passed instead

## Actual behavior

error thrown, 
```
filter() argument after ** must be a mapping, not Q
```



Discussion:
Okay - we'd happily consider a pull request based on https://github.com/usetaptap/django-rest-framework/commit/607059def41dcf8563979629514c3b8d31f0f3d2

Resolving patch:
diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py
--- a/rest_framework/utils/field_mapping.py
+++ b/rest_framework/utils/field_mapping.py
@@ -251,7 +251,9 @@ def get_relation_kwargs(field_name, relation_info):
 
     limit_choices_to = model_field and model_field.get_limit_choices_to()
     if limit_choices_to:
-        kwargs['queryset'] = kwargs['queryset'].filter(**limit_choices_to)
+        if not isinstance(limit_choices_to, models.Q):
+            limit_choices_to = models.Q(**limit_choices_to)
+        kwargs['queryset'] = kwargs['queryset'].filter(limit_choices_to)
 
     if has_through_model:
         kwargs['read_only'] = True


Test patch:
diff --git a/tests/models.py b/tests/models.py
--- a/tests/models.py
+++ b/tests/models.py
@@ -59,6 +59,13 @@ class ForeignKeySourceWithLimitedChoices(RESTFrameworkModel):
                                on_delete=models.CASCADE)
 
 
+class ForeignKeySourceWithQLimitedChoices(RESTFrameworkModel):
+    target = models.ForeignKey(ForeignKeyTarget, help_text='Target',
+                               verbose_name='Target',
+                               limit_choices_to=models.Q(name__startswith="limited-"),
+                               on_delete=models.CASCADE)
+
+
 # Nullable ForeignKey
 class NullableForeignKeySource(RESTFrameworkModel):
     name = models.CharField(max_length=100)
diff --git a/tests/test_relations_pk.py b/tests/test_relations_pk.py
--- a/tests/test_relations_pk.py
+++ b/tests/test_relations_pk.py
@@ -5,10 +5,11 @@
 
 from rest_framework import serializers
 from tests.models import (
-    ForeignKeySource, ForeignKeySourceWithLimitedChoices, ForeignKeyTarget,
-    ManyToManySource, ManyToManyTarget, NullableForeignKeySource,
-    NullableOneToOneSource, NullableUUIDForeignKeySource, OneToOnePKSource,
-    OneToOneTarget, UUIDForeignKeyTarget
+    ForeignKeySource, ForeignKeySourceWithLimitedChoices,
+    ForeignKeySourceWithQLimitedChoices, ForeignKeyTarget, ManyToManySource,
+    ManyToManyTarget, NullableForeignKeySource, NullableOneToOneSource,
+    NullableUUIDForeignKeySource, OneToOnePKSource, OneToOneTarget,
+    UUIDForeignKeyTarget
 )
 
 
@@ -378,6 +379,18 @@ def test_queryset_size_with_limited_choices(self):
         queryset = ForeignKeySourceWithLimitedChoicesSerializer().fields["target"].get_queryset()
         assert len(queryset) == 1
 
+    def test_queryset_size_with_Q_limited_choices(self):
+        limited_target = ForeignKeyTarget(name="limited-target")
+        limited_target.save()
+
+        class QLimitedChoicesSerializer(serializers.ModelSerializer):
+            class Meta:
+                model = ForeignKeySourceWithQLimitedChoices
+                fields = ("id", "target")
+
+        queryset = QLimitedChoicesSerializer().fields["target"].get_queryset()
+        assert len(queryset) == 1
+
 
 class PKNullableForeignKeyTests(TestCase):
     def setUp(self):