Repository: django-json-api/django-rest-framework-json-api

Issue:
ResourceRelatedField.source doesn't allow nested paths
Compared to how DRF allow nested paths in source:
https://github.com/encode/django-rest-framework/blob/master/rest_framework/fields.py#L401

DJA doesn't allow it:
https://github.com/django-json-api/django-rest-framework-json-api/blob/51daed1a738aadb1536a6a3cbe787cbd88daaf05/rest_framework_json_api/utils.py#L204

Examples here: https://www.django-rest-framework.org/api-guide/fields/#source
`EmailField(source='user.email')`

Discussion:
sliverc: Thanks for working on this. I am not sure if this is the right fix though. We have to play around with it a little. So best is to add a test case first and then see what works. Best add a new test case for nested M2M source in the file `tests/test_utils.py`. There are already some tests at the bottom of the file starting with `test_get_related_resource_type`.

And then what might work and simplify the code is replacing the following code bit:
```python
        if parent_model is not None:
            if relation.source:
                if relation.source != "*":
                    parent_model_relation = getattr(parent_model, relation.source)
                else:
                    parent_model_relation = getattr(parent_model, relation.field_name)
            else:
                parent_model_relation = getattr(
                    parent_model, parent_serializer.field_name
                )

```

with something like this 
```python
 if parent_model is not None:
    parent_model_relation = relation.get_attribute(parent_model)
```

Needs to be tested whether this works.

bashlyss: I've tried that and it doesn't work as far as I can tell, at least for the `ManyRelatedField` relation that I'm currently working with. When I have time to play around with it I will get the test in and see. Like I said, it may actually be a bit of a different (but similar) bug. 

In my environment this code resolves the ManyRelatedField issue (unsure if there is a larger global one in the current version). That is using against 4.2.1 rather than 5.0.0 and DRF 3.12.4.
bashlyss: @sliverc either I didn't fully understand what you intended there (get_attribute isn't a method as it happens) or it doesn't work. Adjusted my code slightly and got the tests in.

Since this is an internal, I assume no changelog/docs changes are necessary?

Patch:
diff --git a/AUTHORS b/AUTHORS
index fa91e9b..419144b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -2,6 +2,7 @@ Adam Wróbel <https://adamwrobel.com>
 Adam Ziolkowski <adam@adsized.com>
 Alan Crosswell <alan@columbia.edu>
 Anton Shutik <shutikanton@gmail.com>
+Ashley Loewen <github@ashleycodes.tech>
 Asif Saif Uddin <auvipy@gmail.com>
 Beni Keller <beni@matraxi.ch>
 Boris Pleshakov <koordinator.kun@gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a1b7200..bb0ec74 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ any parts of the framework not mentioned in the documentation should generally b
 ### Fixed
 
 * Fixed invalid relationship pointer in error objects when field naming formatting is used.
+* Properly resolved related resource type when nested source field is defined.
 
 ## [5.0.0] - 2022-01-03
 
diff --git a/rest_framework_json_api/utils.py b/rest_framework_json_api/utils.py
index 8d2dfa7..8317c10 100644
--- a/rest_framework_json_api/utils.py
+++ b/rest_framework_json_api/utils.py
@@ -211,15 +211,15 @@ def get_related_resource_type(relation):
         relation_model = relation.model
     elif hasattr(relation, "get_queryset") and relation.get_queryset() is not None:
         relation_model = relation.get_queryset().model
-    elif (
-        getattr(relation, "many", False)
-        and hasattr(relation.child, "Meta")
-        and hasattr(relation.child.Meta, "model")
-    ):
-        # For ManyToMany relationships, get the model from the child
-        # serializer of the list serializer
-        relation_model = relation.child.Meta.model
-    else:
+    elif hasattr(relation, "child_relation"):
+        # For ManyRelatedField relationships, get the model from the child relationship
+        try:
+            return get_related_resource_type(relation.child_relation)
+        except AttributeError:
+            # Some read only relationships fail to get it directly, fall through to
+            # get via the parent
+            pass
+    if not relation_model:
         parent_serializer = relation.parent
         parent_model = None
         if isinstance(parent_serializer, PolymorphicModelSerializer):


Test patch:
diff --git a/tests/models.py b/tests/models.py
index 6ad9ad6..63718fc 100644
--- a/tests/models.py
+++ b/tests/models.py
@@ -39,3 +39,14 @@ class ForeignKeySource(DJAModel):
     target = models.ForeignKey(
         ForeignKeyTarget, related_name="sources", on_delete=models.CASCADE
     )
+
+
+class NestedRelatedSource(DJAModel):
+    m2m_source = models.ManyToManyField(ManyToManySource, related_name="nested_source")
+    fk_source = models.ForeignKey(
+        ForeignKeySource, related_name="nested_source", on_delete=models.CASCADE
+    )
+    m2m_target = models.ManyToManyField(ManyToManySource, related_name="nested_source")
+    fk_target = models.ForeignKey(
+        ForeignKeySource, related_name="nested_source", on_delete=models.CASCADE
+    )
diff --git a/tests/test_utils.py b/tests/test_utils.py
index efb1325..9b47e9f 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -25,6 +25,7 @@ from tests.models import (
     ForeignKeyTarget,
     ManyToManySource,
     ManyToManyTarget,
+    NestedRelatedSource,
 )
 from tests.serializers import BasicModelSerializer
 
@@ -313,6 +314,48 @@ def test_get_related_resource_type(model_class, field, output):
     assert get_related_resource_type(field) == output
 
 
+@pytest.mark.parametrize(
+    "field,output,related_field_kwargs",
+    [
+        (
+            "m2m_source.targets",
+            "ManyToManyTarget",
+            {"many": True, "queryset": ManyToManyTarget.objects.all()},
+        ),
+        (
+            "m2m_target.sources.",
+            "ManyToManySource",
+            {"many": True, "queryset": ManyToManySource.objects.all()},
+        ),
+        (
+            "fk_source.target",
+            "ForeignKeyTarget",
+            {"many": True, "queryset": ForeignKeyTarget.objects.all()},
+        ),
+        (
+            "fk_target.source",
+            "ForeignKeySource",
+            {"many": True, "queryset": ForeignKeySource.objects.all()},
+        ),
+    ],
+)
+def test_get_related_resource_type_from_nested_source(
+    db, field, output, related_field_kwargs
+):
+    class RelatedResourceTypeSerializer(serializers.ModelSerializer):
+        relation = serializers.ResourceRelatedField(
+            source=field, **related_field_kwargs
+        )
+
+        class Meta:
+            model = NestedRelatedSource
+            fields = ("relation",)
+
+    serializer = RelatedResourceTypeSerializer()
+    field = serializer.fields["relation"]
+    assert get_related_resource_type(field) == output
+
+
 @pytest.mark.parametrize(
     "related_field_kwargs,output",
     [