Fetching BasicInfo, BillingInfo, and DocumentInfo objects
The template method pattern seems useful here:


Create a base class with a get_by_profile_id method that calls a get_queryset method to get the at appropriate QuerySet object to work with
The base class doesn't implement get_queryset, it's the job of the subclasses. This is the template method


Something along the lines of this in the base class:

def get_by_profile_id(self, profile_id, is_versioned):
    queryset = self.get_queryset().filter(profile_id=profile_id).order_by('-version')
    if is_versioned:
        return queryset
    return queryset[:1][0]


And in the subclass for BasicInfo:

def get_queryset(self):
    return BasicInfo.objects


Notice that I eliminated another duplication in the following and ordering logic. Note that Djangi will not execute the query until elements are accessed in the query set, so when I initialize queryset before the if condition, that doesn't hit the database.