alfajet
alfajet

Reputation: 439

django rest serializer: ordering fields appearance

Is it possible to specify in which order fields will appear in a serialised model?

To make sure there is no confusion, while searching answers for this I have found lots of suggestions for ordering objects in a list view but this is not what I am looking for.

I really mean for a given model, I'd like their fields, once serialized to appear in a specific order. I have a fairly complex serialized object containing a lot of nested serializers, which appear first. I'd prefer instead key identifying fields such as name and slug to show up first, for readability.

Apologies in advance if this question is a duplicate, but I didn't find any relevant responses.

Solution Based on @Toni-Sredanović solution I have implemented the following solution

def promote_fields(model: models.Model, *fields):
    promoted_fields = list(fields)
    other_fields = [field.name for field in model._meta.fields if field.name not in promoted_fields]
    return promoted_fields + other_fields


class MySerializer(serializers.ModelSerializer):
    ...
    class Meta:
        model = MyModel
        fields = promote_fields(model, 'id', 'field1', 'field2')

Upvotes: 0

Views: 1321

Answers (1)

Toni Sredanović
Toni Sredanović

Reputation: 2402

For that you can specify which fields you want to show and their order in class Meta:

    class Meta:
        fields = ('id', 'name', 'slug', 'field_1', 'field_2', ..., )

Here is a full example:

class TeamWithGamesSerializer(serializers.ModelSerializer):
    """
    Team ModelSerializer with home and away games.
    Home and away games are nested lists serialized with GameWithTeamNamesSerializer.
    League is object serialized with LeagueSerializer instead of pk integer.
    Current players is a nested list serialized with PlayerSerializer.
    """
    league = LeagueSerializer(many=False, read_only=True)

    home_games = GameWithTeamNamesSerializer(many=True, read_only=True)
    away_games = GameWithTeamNamesSerializer(many=True, read_only=True)

    current_players = PlayerSerializer(many=True, read_only=True)

    class Meta:
        model = Team
        fields = ('id', 'name', 'head_coach', 'league', 'current_players', 'home_games', 'away_games', 'gender')

And the result:

{
    "id": 1,
    "name": "Glendale Desert Dogs",
    "head_coach": "Coach Desert Dog",
    "league": {
        "id": 1,
        "name": "Test league 1"
    },
    "current_players": [
        {
            "id": "rodriem02",
            "first_name": "Emanuel",
            "last_name": "Rodriguez",
            "current_team": 1
        },
        {
            "id": "ruthba01",
            "first_name": "Babe",
            "last_name": "Ruth",
            "current_team": 1
        }
    ],
    "home_games": [
        {
            "id": 6,
            "team_home": {
                "id": 1,
                "name": "Glendale Desert Dogs"
            },
            "team_away": {
                "id": 2,
                "name": "Mesa Solar Sox"
            },
            "status": "canceled",
            "date": "2019-10-01"
        },
        {
            "id": 7,
            "team_home": {
                "id": 1,
                "name": "Glendale Desert Dogs"
            },
            "team_away": {
                "id": 2,
                "name": "Mesa Solar Sox"
            },
            "status": "",
            "date": "2019-10-04"
        }
    ],
    "away_games": [
        {
            "id": 3,
            "team_home": {
                "id": 2,
                "name": "Mesa Solar Sox"
            },
            "team_away": {
                "id": 1,
                "name": "Glendale Desert Dogs"
            },
            "status": "canceled",
            "date": "2019-10-02"
        }
    ],
    "gender": "M"
}

If you would just use fields = '__all__' default ordering would be used which is:

  1. object id
  2. fields specified in the serializer
  3. fields specified in the model

Best i can think of right now regarding your comment about generating fields is getting the fields in model, not really sure how to access what you've defined in the serializer so you would still need to write that manually.

Here is how you could do it with my example (this would make the name and gender appear on top):

    class Meta:
        model = Team
        fields = ('name', 'gender')\
                 + tuple([field.name for field in model._meta.fields if field.name not in ('name', 'gender')])\
                 + ('league', 'home_games', 'away_games', 'current_players')

Upvotes: 2

Related Questions