1
votes

I am currently using Django REST framework 3.11.0 with Django 3.0.6. I am still very new to DRF and I am not sure how to approach this problem. I am trying to apply sort to SerializerMethodField and an enum. I was able to find some documentation that states that I could make my own OrderFilter but I dont know how. Are there any examples I could use? Here is my code.

View

from rest_framework import generics
from V1.assets.models.asset_main import Asset
from V1.assets.serializers.asset_serializer import AssetSerializer
from rest_framework import filters
from django_filters.rest_framework import DjangoFilterBackend

class AssetsView(generics.ListCreateAPIView):
    queryset = Asset.objects.all()
    serializer_class = AssetSerializer
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filter_fields = ['asset_type']
    search_fields = ['asset_type', 'asset_properties', 'current_employee_name'] 

Model

class AssetType(models.TextChoices):
    LAPTOP = 'LPT', _('Laptop')
    MOUSE = 'MSE', _('Mouse')
    MONITOR = 'MTR', _('Monitor')
    AC_ADAPTER = 'ADR', _('AC Adapter')
    TELEPHONE = 'TLP', _('Telephone')
    LOCK = 'LCK', _('Lock')
    HEADSET = 'HDS', _('Headset')

class Asset(CreatedModified):

    asset_type = models.CharField(
        max_length=3,
        choices=AssetType.choices,
        default=None
    )
    asset_properties = JSONField(default=dict)
    current_employee = models.ForeignKey(
        Employee,
        related_name='assets_current_employees', 
        related_query_name='assets_current_employee',
        default=None,
        on_delete=models.CASCADE,
        null=True,
        blank=True
    )

    class Meta:
        app_label = 'assets'
        default_related_name = 'assets'

    def __str__(self):
        return self.asset_type

Serializer

from V1.general.enums import AssetStatus, AssetType
from V1.accounts.models.employee_main import Employee

class AssetSerializer(serializers.ModelSerializer):

    current_employee_name = serializers.SerializerMethodField('get_full_name_from_employee')
    asset_type = serializers.CharField(source='get_asset_type_display')

    class Meta:
        model = Asset
        fields = (
            'id',
            'asset_type',
            'asset_properties',
            'current_employee',
            'current_employee_name'
        )
        extra_kwargs = {
            'asset_type': {
                'required': True, 
                'allow_blank': False
            }
        }

    def get_full_name_from_employee(self, asset):
        current_employee_name = asset.current_employee.first_name + ' ' + asset.current_employee.last_name
        return current_employee_name

I can't seem to order by current_employee_name or asset_type. What should I do to allow sorting for these two fields?

1
I would suggest ordering by ['current_employee__first_name', 'current_employee__last_name']. You can set that on ordering=[] on the model Meta. That way you do the sorting in the database, avoiding a potentially memory heavy operation in python. - Johan Schiff
you cant order by a methofield in serializer or model , ordering is happening at the database but method declaration is when the objects have been retreived from database - mahyar
There is always some way to work around it. Hard way, in this case. But even if it's possible, it's a bad idea. To get it working you would need to load all sort values from the db into python to do the sorting. If you have tens of thousands rows at some point in the future, your database will still be able to handle that in an efficient way. Python won't. - Johan Schiff
@JohanSchiff But this wouldn't sort by full name right? The closest solution I can think of is it only sort it by current_employee__first_name. That might be the only way to pull it of without creating my own OrderFilter. - Vince Orio
@Vince It will sort first by first_name, and secondly by last_name. That will yield the same result as sorting by full name. - Johan Schiff

1 Answers

4
votes

As far as I can understand from your question you want to order the Assets by the current employee name. Let's take few names for example:

  • Jean d'Artagnan (first_name: Jean, last_name: d'Artagnan)
  • Joe Plumber (first_name: Joe, last_name: Plumber)
  • Jonas Meyer (first_name: Jonas, last_name: Meyer)

The same ordering can be achieved if we sort first by first_name and then by last_name. In case there are multiple entries with the same first_name the ordering will be done be the last_name.

To achieve this you can simply specify the ordering_fields in your view:

class AssetsView(generics.ListCreateAPIView):
    filters = [filters.OrderingFilter]
    ordering_fields = ['current_employee.first_name', 'current_employee.last_name']