Refactoring Massive Python/Django App
So far, I have been working on the company’s backend application(Tradir.io) without paying much attention to how I am going to help other future developers inherit the codebase. However, since I know that I am not going to be working on this codebase forever, and the application is going to last a very long time, I felt a strong need to make the codebase more comprehensible.
Although, there is no need to refactor every single app in the whole project. The only apps that need to be refactored are the ones with huge files that are hard to read.
The best example would be a bloated serializer class that spans over 260 lines.
class SomeSerializer(serializers.ModelSerializer):
field1 = serializers.SerializerMethodField()
field2 = serializers.SerializerMethodField()
field3 = serializers.StringRelatedField(read_only=True)
field4 = serializers.CharField(required=False, allow_null=True)
.
.
.
get_field1(self, instance):
return OtherSerializer(instance.otherfield).data
get_field2(self, instance):
return OtherSerializer2(instance.otherfield2).data
.
.
.
def create(self, validated_data):
with transaction.atomic():
user = self.context['request'].user
car= Car.objects.create(owner=user, **validated_data)
car.add_access(user)
if self.context['others_allowed_to_drive']:
for id in self.context['other_user_ids']:
added_user = User.objects.get(id=id)
car.add_access(added_user)
car.save()
.
.
.
In the above example, you can see that the create()
method, which is the most important method in SomeSerializer, has many lines of code. For now, it is now as big, but it certainly takes more than a few seconds to figure out what the last 6 lines of code do. We can create a separate method for those last 6 lines with appropriate naming.
def initialize_caraccess(self, car, user):
car.add_access(user)
if self.context['others_allowed_to_drive']:
for id in self.context['other_user_ids']:
added_user = User.objects.get(id=id)
car.add_access(added_user)
car.save()
Now, the create()
method can be shorter and more semantic.
def initialize_caraccess(self, car, user):
.
.
.
car.save()def create(self, validated_data):
with transaction.atomic():
user = self.context['request'].user
car= Car.objects.create(owner=user, **validated_data)
self.initialize_caraccess(self, car, user)