'How to select single entity from multiple entity with maximum value of a field in django?

models.py

def return_timestamped_id():
        last_unique_id = RMLocation.objects.all().order_by('id').last()
        if not last_unique_id:
            return 'RM0001'
        else:
            timestamped_id = last_unique_id.unique_id
            timestamped_id = int(timestamped_id.split('RM')[-1])
            width = 4
            new_unique_int = timestamped_id + 1
            formatted = (width - len(str(new_unique_int))) * "0" + str(new_unique_int)
            new_unique_id = 'RM' + str(formatted)
            return new_unique_id


class RMLocation(models.Model):
    warehouse = models.ForeignKey(RMWarehouse, on_delete=models.CASCADE, related_name='l_warehouse')
    cabinet = models.CharField(max_length=255, blank=True, null=True)
    rack = models.CharField(max_length=255, blank=True, null=True)
    shelf = models.CharField(max_length=255, blank=True, null=True)
    file_name = models.CharField(max_length=255, blank=True, null=True)
    file_no = models.CharField(max_length=255, blank=True, null=True)
    unique_id = models.CharField(default=return_timestamped_id, max_length=255, blank=True, null=True)    # Auto generated.
    uuid = models.CharField(max_length=40)
    record_extension = models.ForeignKey(DocumentExtension, on_delete=models.CASCADE, related_name='rec_ext', blank=True, null=True)
    company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='record_company')
    is_ocr = models.BooleanField(default=False)
    ocr_text_file = models.CharField(max_length=255, blank=True, null=True)
    is_trashed = models.BooleanField(default=False)
    trashed_on = models.DateTimeField(blank=True, null=True)
    created_at = models.DateTimeField(default=timezone.now)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True, related_name='l_created_by')
    updated_at = models.DateTimeField(blank=True, null=True)
    updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True, related_name='l_updated_by')
    deleted_at = models.DateTimeField(blank=True, null=True)
    deleted_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True, related_name='l_deleted_by')

    def __str__(self):
        return str(self.id)

    class Meta:
        db_table = 'record_location'


class RMRecordVersion(models.Model):
    VER_TYPE_CHOICE = (
        ('major', 'major'),
        ('minor', 'minor')
    )
    record = models.ForeignKey(RMLocation, on_delete=models.CASCADE, related_name='rv_rec')
    scanned_file_location = models.TextField(blank=True, null=True)
    size_in_kb = models.FloatField(default=0.0)
    version_no = models.IntegerField(default=1)
    version_code = models.CharField(default='1.0', max_length=10)
    version_type = models.CharField(choices=VER_TYPE_CHOICE, max_length=10, default='major')
    created_at = models.DateTimeField(auto_now_add=True, null=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='rv_rm_user', null=True, blank=True)

    def __str__(self):
        return str(self.id)

    class Meta:
        db_table = 'record_versions'

views.py

In the Older Response below you can see there are items with the same id. For example, For id = 69 we got two items with version_no 1 and 2 respectively. But I want to have only one item per id with maximum version_no. So, for id = 69 we gonna get one item whose version_no is 2.

I tried .aggregate(Max('rv_rec__version_no')). Currently trying Subquery. Which is giving this:

This queryset contains a reference to an outer query and may only be used in a subquery.
class RMLocationListView(APIView):
    permission_classes = [IsAuthenticated]
    authentication_classes = [TokenAuthentication]
    queryset = RMLocation.objects.values()

    def get_filter(self, instance):
        filter = {}
        unique_id = self.request.query_params.get('unique_id', None)
        warehouse = self.request.query_params.get('warehouse_id', None)
        cabinet = self.request.query_params.get('cabinet', None)
        rack = self.request.query_params.get('rack', None)
        shelf = self.request.query_params.get('shelf', None)

        from django.db.models import Max
        from django.db.models import Subquery, OuterRef
        filter['is_trashed'] = False
        filter['company'] = instance.user.company
        if unique_id:
            filter['unique_id'] = unique_id
        if warehouse:
            filter['warehouse'] = warehouse
        if warehouse and cabinet:
            filter['cabinet'] = cabinet
        if warehouse and cabinet and rack:
            filter['rack'] = rack
        if warehouse and cabinet and rack and shelf:
            filter['shelf'] = shelf

        queryset = self.queryset.filter(**filter, rv_rec__version_no__gte = Subquery(RMRecordVersion.objects.filter(record = OuterRef('id')).values('version_no').last().get("version_no"))).order_by('-id')
        queryset=queryset.annotate(
                                    warehouse_id = F('warehouse'),
                                    warehouse_name = F('warehouse__name'),
                                    warehouse_code = F('warehouse__code'),
                                    record_name = F('file_name'),
                                    company_name = F('company__name'),
                                    uploaded_at = F('created_at'),
                                    uploaded_by_id = F('created_by'),
                                    uploaded_by = Concat('created_by__first_name', Value(' '), 'created_by__last_name'),
                                    size_in_kb = F('rv_rec__size_in_kb'),
                                    version_no = F('rv_rec__version_no'),
                                    version_code = F('rv_rec__version_code'),
                                    version_type = F('rv_rec__version_type'),
                                    )
        return queryset.values('id','warehouse_id','warehouse_name','warehouse_code','cabinet','rack','shelf','record_name',
                               'file_no','unique_id','company_name','size_in_kb','version_no','version_code',
                               'version_type','uploaded_at','uploaded_by_id','uploaded_by')
        

    @response_modify_decorator_list_or_get_after_execution_for_onoff_pagination
    def get(self, request, *args, **kwargs):
        count = self.request.query_params.get('count')

        # Pagination Functionality
        paginator = OnOffPagination()
        page_size = self.request.GET['page_size']

        # Filter
        self.queryset = self.get_filter(request)

        field_name = self.request.query_params.get('ordering')
        if field_name:
            self.queryset = base_f.get_sorted_by_fields(self, self.queryset)

        if page_size == '0':
            response = self.queryset
            if count:
                count = len(response)
                return Response({'total_count': count})
        else:
            result_page = paginator.paginate_queryset(self.queryset, request)
            response = paginator.get_paginated_response(result_page)

            # Appending preview_path in response.data of those records whose soft copies are available.
            for index, record in enumerate(response.data['results']):
               
                record_obj = RMLocation.objects.filter(id=record['id']).last()
                if not record_obj.record_extension == None:
                    record_response = record_service.get_record_response(record_obj, request.user, show_preview_path=True)
                    response.data['results'][index]['soft_copy_permission'] = record_response['permission']
                    response.data['results'][index]['extension_name'] = record_response['record_extension']['name']
                    response.data['results'][index]['preview_path'] = record_response['preview_path']
            
            response = response.data

        return Response(response)

Older Response

{
    "count": 49,
    "next": "http://127.0.0.1:8001/rm/location/list/?page=2&page_size=10",
    "previous": null,
    "results": [
        {
            "id": 69,
            "cabinet": "C1",
            "rack": "R1",
            "shelf": "S1",
            "file_no": "FNo1236sdfsdff",
            "unique_id": "RM0026",
            "warehouse_id": 1,
            "warehouse_name": "Godrej Store",
            "warehouse_code": "D667",
            "record_name": "Record Namejjjsdff",
            "company_name": "Demo Company",
            "uploaded_at": "2022-04-08T06:47:21.063450Z",
            "uploaded_by_id": 33,
            "uploaded_by": "Person Name",
            "size_in_kb": 1.0,
            "version_no": 2,
            "version_code": "3.0",
            "version_type": "major",
            "soft_copy_permission": "admin",
            "extension_name": "png",
            "preview_path": "http://127.0.0.1:8001/rm/_storage/preview/1cc1372e-313e-4cbe-8463-09ef85fc36f6|6f63cf58fe90-3648-ebc4-e313-e2731cc1|NA.png/"
        },
        {
            "id": 69,
            "cabinet": "C1",
            "rack": "R1",
            "shelf": "S1",
            "file_no": "FNo1236sdfsdff",
            "unique_id": "RM0026",
            "warehouse_id": 1,
            "warehouse_name": "Godrej Store",
            "warehouse_code": "D667",
            "record_name": "Record Namejjjsdff",
            "company_name": "Demo Company",
            "uploaded_at": "2022-04-08T06:47:21.063450Z",
            "uploaded_by_id": 33,
            "uploaded_by": "Person Name",
            "size_in_kb": 1.0,
            "version_no": 1,
            "version_code": "2.0",
            "version_type": "major",
            "soft_copy_permission": "admin",
            "extension_name": "png",
            "preview_path": "http://127.0.0.1:8001/rm/_storage/preview/1cc1372e-313e-4cbe-8463-09ef85fc36f6|6f63cf58fe90-3648-ebc4-e313-e2731cc1|NA.png/"
        },
        {
            "id": 68,
            "cabinet": "C1",
            "rack": "R1",
            "shelf": "S1",
            "file_no": "FNo1236sdfsdf",
            "unique_id": "RM0025",
            "warehouse_id": 1,
            "warehouse_name": "Godrej Store",
            "warehouse_code": "D667",
            "record_name": "Record Namejjjsdf",
            "company_name": "Demo Company",
            "uploaded_at": "2022-04-08T06:30:05.690434Z",
            "uploaded_by_id": 33,
            "uploaded_by": "Person Name",
            "size_in_kb": 1.0,
            "version_no": 2,
            "version_code": "2.0",
            "version_type": "major",
            "soft_copy_permission": "admin",
            "extension_name": "png",
            "preview_path": "http://127.0.0.1:8001/rm/_storage/preview/22dc1968-0e07-4db5-b146-e136b76eac32|23cae67b631e-641b-5bd4-70e0-8691cd22|NA.png/"
        },
        {
            "id": 68,
            "cabinet": "C1",
            "rack": "R1",
            "shelf": "S1",
            "file_no": "FNo1236sdfsdf",
            "unique_id": "RM0025",
            "warehouse_id": 1,
            "warehouse_name": "Godrej Store",
            "warehouse_code": "D667",
            "record_name": "Record Namejjjsdf",
            "company_name": "Demo Company",
            "uploaded_at": "2022-04-08T06:30:05.690434Z",
            "uploaded_by_id": 33,
            "uploaded_by": "Person Name",
            "size_in_kb": 1.0,
            "version_no": 1,
            "version_code": "1.0.1",
            "version_type": "minor",
            "soft_copy_permission": "admin",
            "extension_name": "png",
            "preview_path": "http://127.0.0.1:8001/rm/_storage/preview/22dc1968-0e07-4db5-b146-e136b76eac32|23cae67b631e-641b-5bd4-70e0-8691cd22|NA.png/"
        },
        {
            "id": 67,
            "cabinet": "C1",
            "rack": "R1",
            "shelf": "S1",
            "file_no": "FNo1236633r",
            "unique_id": "RM0024",
            "warehouse_id": 1,
            "warehouse_name": "Godrej Store",
            "warehouse_code": "D667",
            "record_name": "Record Name6633r",
            "company_name": "Demo Company",
            "uploaded_at": "2022-04-07T09:02:07.704273Z",
            "uploaded_by_id": 33,
            "uploaded_by": "Person Name",
            "size_in_kb": 98.0,
            "version_no": 22,
            "version_code": "20.1",
            "version_type": "minor",
            "soft_copy_permission": "admin",
            "extension_name": "pdf",
            "preview_path": "http://127.0.0.1:8001/rm/_storage/preview/91e666ad-9108-4860-a0bb-891bdcace5f0|0f5ecacdb198-bb0a-0684-8019-da666e19|NA.pdf/"
        },
        {
            "id": 67,
            "cabinet": "C1",
            "rack": "R1",
            "shelf": "S1",
            "file_no": "FNo1236633r",
            "unique_id": "RM0024",
            "warehouse_id": 1,
            "warehouse_name": "Godrej Store",
            "warehouse_code": "D667",
            "record_name": "Record Name6633r",
            "company_name": "Demo Company",
            "uploaded_at": "2022-04-07T09:02:07.704273Z",
            "uploaded_by_id": 33,
            "uploaded_by": "Person Name",
            "size_in_kb": 98.0,
            "version_no": 21,
            "version_code": "20.0",
            "version_type": "major",
            "soft_copy_permission": "admin",
            "extension_name": "pdf",
            "preview_path": "http://127.0.0.1:8001/rm/_storage/preview/91e666ad-9108-4860-a0bb-891bdcace5f0|0f5ecacdb198-bb0a-0684-8019-da666e19|NA.pdf/"
        },
        {
            "id": 67,
            "cabinet": "C1",
            "rack": "R1",
            "shelf": "S1",
            "file_no": "FNo1236633r",
            "unique_id": "RM0024",
            "warehouse_id": 1,
            "warehouse_name": "Godrej Store",
            "warehouse_code": "D667",
            "record_name": "Record Name6633r",
            "company_name": "Demo Company",
            "uploaded_at": "2022-04-07T09:02:07.704273Z",
            "uploaded_by_id": 33,
            "uploaded_by": "Person Name",
            "size_in_kb": 98.0,
            "version_no": 20,
            "version_code": "19.0",
            "version_type": "major",
            "soft_copy_permission": "admin",
            "extension_name": "pdf",
            "preview_path": "http://127.0.0.1:8001/rm/_storage/preview/91e666ad-9108-4860-a0bb-891bdcace5f0|0f5ecacdb198-bb0a-0684-8019-da666e19|NA.pdf/"
        },
        {
            "id": 67,
            "cabinet": "C1",
            "rack": "R1",
            "shelf": "S1",
            "file_no": "FNo1236633r",
            "unique_id": "RM0024",
            "warehouse_id": 1,
            "warehouse_name": "Godrej Store",
            "warehouse_code": "D667",
            "record_name": "Record Name6633r",
            "company_name": "Demo Company",
            "uploaded_at": "2022-04-07T09:02:07.704273Z",
            "uploaded_by_id": 33,
            "uploaded_by": "Person Name",
            "size_in_kb": 98.0,
            "version_no": 19,
            "version_code": "18.0",
            "version_type": "major",
            "soft_copy_permission": "admin",
            "extension_name": "pdf",
            "preview_path": "http://127.0.0.1:8001/rm/_storage/preview/91e666ad-9108-4860-a0bb-891bdcace5f0|0f5ecacdb198-bb0a-0684-8019-da666e19|NA.pdf/"
        },
        {
            "id": 67,
            "cabinet": "C1",
            "rack": "R1",
            "shelf": "S1",
            "file_no": "FNo1236633r",
            "unique_id": "RM0024",
            "warehouse_id": 1,
            "warehouse_name": "Godrej Store",
            "warehouse_code": "D667",
            "record_name": "Record Name6633r",
            "company_name": "Demo Company",
            "uploaded_at": "2022-04-07T09:02:07.704273Z",
            "uploaded_by_id": 33,
            "uploaded_by": "Person Name",
            "size_in_kb": 98.0,
            "version_no": 18,
            "version_code": "17.0",
            "version_type": "major",
            "soft_copy_permission": "admin",
            "extension_name": "pdf",
            "preview_path": "http://127.0.0.1:8001/rm/_storage/preview/91e666ad-9108-4860-a0bb-891bdcace5f0|0f5ecacdb198-bb0a-0684-8019-da666e19|NA.pdf/"
        },
        {
            "id": 67,
            "cabinet": "C1",
            "rack": "R1",
            "shelf": "S1",
            "file_no": "FNo1236633r",
            "unique_id": "RM0024",
            "warehouse_id": 1,
            "warehouse_name": "Godrej Store",
            "warehouse_code": "D667",
            "record_name": "Record Name6633r",
            "company_name": "Demo Company",
            "uploaded_at": "2022-04-07T09:02:07.704273Z",
            "uploaded_by_id": 33,
            "uploaded_by": "Person Name",
            "size_in_kb": 98.0,
            "version_no": 17,
            "version_code": "16.0",
            "version_type": "major",
            "soft_copy_permission": "admin",
            "extension_name": "pdf",
            "preview_path": "http://127.0.0.1:8001/rm/_storage/preview/91e666ad-9108-4860-a0bb-891bdcace5f0|0f5ecacdb198-bb0a-0684-8019-da666e19|NA.pdf/"
        }
    ],
    "status_code": 200,
    "request_status": 1,
    "msg": "Data has been fetched successfully"
}

Some of the functions used in views.py

def get_record_response(record: RMLocation, user: UserDetail, show_preview_path=False, version: int = None):
    read_permission = get_record_permission_for_user(record, user, permission_level='read')
    data = None

    if read_permission:
        record_extension_name = record.record_extension.name if record.record_extension and record.record_extension.name else ''
        data = {
            'id': record.id,
            'type': 'record',
            'warehouse_name': record.warehouse.name,
            'warehouse_code': record.warehouse.code,
            'warehouse_address': record.warehouse.address,
            'cabinet': record.cabinet,
            'rack': record.rack,
            'shelf': record.shelf,
            'record_name': record.file_name,
            'file_no': record.file_no,
            'unique_id': record.unique_id,
            'uuid': record.uuid,
            'company_id': record.company_id,
            'company': record.company.name,
            'record_extension': {
                'id': record.record_extension_id,
                'name': record_extension_name,
            },
            'is_trashed': record.is_trashed,
            'create_modified_details': get_record_create_and_modified_details(record),
        }

        if not record.is_trashed:
            data['permission'] = read_permission[0].soft_copy_permission if user else 'read'
            version_history, last_version = RMRecordVersion.objects.filter(record=record), None
            if version:
                last_version = version_history.filter(version_no=version).first()
                if not last_version:
                    last_version = version_history.order_by('-version_no').first()
                    version = None

                data['version_no'] = last_version.version_no
                data['version_code'] = last_version.version_code
                data['version_type'] = last_version.version_type
                data['size_in_kb'] = last_version.size_in_kb
            if show_preview_path:
                data['preview_path'], _ = create_single_record_download_link(record.uuid, user, 
                                                                request=None, version_no=version,
                                                                action='preview')
        else:
            data['trashed_on'] = record.trashed_on

    return data


def get_sorted_by_fields(self, response):
    field_name = self.request.query_params.get('ordering')
    if 'results' in response:
        if '-' in field_name:
            field_name = field_name[1:]
            response['results'] = sorted(response['results'], key=lambda i: i[field_name]if i[field_name] else '', reverse=True)
        else:
            response['results'] = sorted(response['results'], key=lambda i: i[field_name] if i[field_name] else '')
    else:
        if '-' in field_name:
            field_name = field_name[1:]
            response = sorted(response, key=lambda i: i[field_name]if i[field_name] else '', reverse=True)
        else:
            response = sorted(response, key=lambda i: i[field_name] if i[field_name] else '')
    return response


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source