How To Use the Source Keyword Argument

The DRF serializer provides a keyword argument, called source , which is extremely smart and can help avoid a lot of common patterns.

Let’s write a serializer that can create serialized representations of a User .

Let’s use this serializer to serialize a user:

In [1]: from accounts.serializers import UserSerializer In [2]: from django.contrib.auth.models import User In [3]: user = User.objects.latest('pk') In [4]: serializer = UserSerializer(user)

Out[5]: {'username': 'akshar', 'email': ' In [5]: serializer.dataOut[5]: {'username': 'akshar', 'email': ' akshar@agiliq.com ', 'first_name': '', 'last_name': ''}

Suppose the front end or mobile app consuming this API wants the key to be user_name instead of username in the serialized representation.

We can add a CharField on the serializer with a source attribute to achieve this.

Ensure that username is replaced with user_name in Meta.fields . Restart the shell and instantiate the serializer.

In [6]: serializer = UserSerializer(user)

Out[7]: {'user_name': 'akshar', 'email': ' In [7]: serializer.dataOut[7]: {'user_name': 'akshar', 'email': ' akshar@agiliq.com ', 'first_name': '', 'last_name': ''}

In the last example, we saw how source works with a field of User . source can also work with methods on User .

User has a method called get_full_name . Let’s set first_name and last_name .

In [8]: user.first_name = 'akshar' In [9]: user.last_name = 'raaj' In [10]: user.save() In [11]: user.get_full_name()

Out[11]: 'akshar raaj'

Let’s add a field called full_name to the serializer. Set its source to use User.get_full_name .

Let’s restart the shell and get the serialized representation for a user.

In [3]: user = User.objects.latest('pk') In [4]: serializer = UserSerializer(user)

Out[5]: {'user_name': 'akshar', 'email': ' In [5]: serializer.dataOut[5]: {'user_name': 'akshar', 'email': ' akshar@agiliq.com ', 'first_name': 'akshar', 'last_name': 'raaj', 'full_name': 'akshar raaj'}

Notice how full_name gives the desired result. Under the hood, DRF used get_full_name to populate the full_name .

source can seamlessly work with relationships too, i.e. with ForeignKey , OneToOneField , and ManyToMany .

Assume there is a Profile model which has a OneToOne relationship with User .

The profile model looks like this:

Let’s say we want the street and city sent in serialized representation. We could add a street and city field on the serializer and set the appropriate source .

Let’s restart the shell and serialize the user.

In [4]: user = User.objects.latest('pk') In [5]: serializer = UserSerializer(user)

Out[6]: {'email': ' In [6]: serializer.dataOut[6]: {'email': ' akshar@agiliq.com ', 'first_name': 'akshar', 'last_name': 'raaj', 'street': 'Pennsylvania Avenue', 'city': 'Washington'}

source also works seamlessly with methods of related objects, similar to how it works with methods of the object.

We want to get the full address of users, accessible using user.profile.get_full_address() .

We can set source as profile.get_full_address in such cases.

Serialize the user again:

In [3]: user = User.objects.latest('pk') In [4]: serializer = UserSerializer(user)

Out[5]: {'email': ' In [5]: serializer.dataOut[5]: {'email': ' akshar@agiliq.com ', 'first_name': 'akshar', 'last_name': 'raaj', 'full_address': 'Pennsylvania Avenue, Washington'}

Let’s see how effortlessly source works with ManyToManyField .

We want to get associated groups of users in the serialized representation too. Let’s add few groups to the user first.

In [12]: g1 = Group.objects.create(name='BBC') In [13]: g2 = Group.objects.create(name='Sony') In [15]: user.groups.add(*[g1, g2])

We want the id and name for each group associated with the user.

We will need to write a GroupSerializer which can serialize a group instance.

It would look like this:

A naive way to add groups to serialized data would be to define a SerializerMethodField and add user.groups.all() in the method.

A DRFish way is to add an all_groups field on the serializer and set it to GroupSerializer .

Let’s serialize a user and verify that the associated group’s information is present in serialized data.

In [1]: from accounts.serializers import UserSerializer In [2]: from django.contrib.auth.models import User In [3]: user = User.objects.latest('pk') In [5]: serializer = UserSerializer(user)

Out[6]: {'email': ' In [6]: serializer.dataOut[6]: {'email': ' akshar@agiliq.com ', 'first_name': 'akshar', 'last_name': 'raaj', 'groups': [OrderedDict([('id', 2), ('name', 'BBC')]), OrderedDict([('id', 3), ('name', 'Sony')])]}

DRF is smart enough to call user.groups.all() , even though we just set source=groups . DRF infers that groups is a ManyRelatedManager and thus calls .all() on the manager to get all the associated groups.

If we don’t want to provide a group’s information during POST calls, we would have to add the keyword argument read_only=True on GroupSerializer .

Assume there is a model called Article , and Article has a ForeignKey to User . We can add articles of a user in the serialized representation with: