diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md
index 0f355c76d2..9e2e53d97f 100644
--- a/docs/api-guide/serializers.md
+++ b/docs/api-guide/serializers.md
@@ -580,6 +580,22 @@ This option is a dictionary, mapping field names to a dictionary of keyword argu
 
 Please keep in mind that, if the field has already been explicitly declared on the serializer class, then the `extra_kwargs` option will be ignored.
 
+It is also possible to create new serializer fields from any related model fields using the `extra_kwargs` option. For example:
+
+    class UserProfile(models.Model):
+        birthdate = models.DateField()
+        user = models.ForeignKey(User, on_delete=models.CASCADE)
+
+    class UserProfileSerializer(serializers.ModelSerializer):
+        class Meta:
+            model = UserProfile
+            fields = ['date_of_birth', 'first_name', 'last_name']
+            extra_kwargs = {
+                'date_of_birth': {'source': 'birthdate'},
+                'first_name': {'source': 'user.first_name'},
+                'last_name': {'source': 'user.last_name'}
+            }
+
 ## Relational fields
 
 When serializing model instances, there are a number of different ways you might choose to represent relationships.  The default representation for `ModelSerializer` is to use the primary keys of the related instances.
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 77c181b6cc..274017fdb4 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -1117,9 +1117,36 @@ def get_fields(self):
             if source == '*':
                 source = field_name
 
+            # Get the right model and info for source with attributes
+            source_attrs = source.split('.')
+            source_info = info
+            source_model = model
+
+            if len(source_attrs) > 1:
+                attr_info = info
+                attr_model = model
+
+                for attr in source_attrs[:-1]:
+                    if attr not in attr_info.relations:
+                        break
+
+                    attr_model = attr_info.relations[attr].related_model
+                    attr_info = model_meta.get_field_info(attr_model)
+                else:
+                    attr = source_attrs[-1]
+                    if (
+                        attr in attr_info.fields_and_pk
+                        or attr in attr_info.relations
+                        or hasattr(attr_model, attr)
+                        or attr == self.url_field_name
+                    ):
+                        source = attr
+                        source_info = attr_info
+                        source_model = attr_model
+
             # Determine the serializer field class and keyword arguments.
             field_class, field_kwargs = self.build_field(
-                source, info, model, depth
+                source, source_info, source_model, depth
             )
 
             # Include any kwargs defined in `Meta.extra_kwargs`
diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py
index 20d0319fcb..6178c85fed 100644
--- a/tests/test_model_serializer.py
+++ b/tests/test_model_serializer.py
@@ -13,6 +13,7 @@
 
 import django
 import pytest
+from django.contrib.auth.models import User
 from django.core.exceptions import ImproperlyConfigured
 from django.core.serializers.json import DjangoJSONEncoder
 from django.core.validators import (
@@ -735,6 +736,46 @@ class Meta:
         """)
         self.assertEqual(repr(TestSerializer()), expected)
 
+    def test_source_with_attributes(self):
+        class UserProfile(models.Model):
+            age = models.IntegerField()
+            birthdate = models.DateField()
+            user = models.ForeignKey(User, on_delete=models.CASCADE)
+
+        class UserProfileSerializer(serializers.ModelSerializer):
+            class Meta:
+                model = UserProfile
+                fields = ('username', 'email', 'first_name', 'last_name', 'age', 'birthdate')
+                extra_kwargs = {
+                    'username': {
+                        'source': 'user.username',
+                    },
+                    'email': {
+                        'source': 'user.email',
+                    },
+                    'first_name': {
+                        'source': 'user.first_name',
+                    },
+                    'last_name': {
+                        'source': 'user.last_name',
+                    }
+                }
+
+        # In Django 3.0, the maximum length of first_name is 30, whereas it is 150
+        # in later versions, so we can't hard-code the value in the expected variable.
+        max_length = User.first_name.field.max_length
+
+        expected = dedent(f"""
+            UserProfileSerializer():
+                username = CharField(help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, source='user.username', validators=[<django.contrib.auth.validators.UnicodeUsernameValidator object>, <UniqueValidator(queryset=User.objects.all())>])
+                email = EmailField(allow_blank=True, label='Email address', max_length=254, required=False, source='user.email')
+                first_name = CharField(allow_blank=True, max_length={max_length}, required=False, source='user.first_name')
+                last_name = CharField(allow_blank=True, max_length=150, required=False, source='user.last_name')
+                age = IntegerField()
+                birthdate = DateField()
+        """)
+        self.assertEqual(repr(UserProfileSerializer()), expected)
+
 
 class DisplayValueTargetModel(models.Model):
     name = models.CharField(max_length=100)