Using the reserved word “class” as field name in Django and Django REST Framework

后端 未结 3 592
慢半拍i
慢半拍i 2020-12-06 11:37

Description of the problem

Taxonomy is the science of defining and naming groups of biological organisms on the basis of shared characteristics. Organisms are grou

相关标签:
3条回答
  • 2020-12-06 12:09

    Other software developers in the field of Bioinformatics might be interested in a solution of this problem, so I post here my approach as suggested by Alasdair.

    The goal is to create a model for a living species, for the sake of simplicity let's say an animal, and create an endpoint with Django REST Framework representing the correct taxonomic ranks.

    models.py

    from django.db import models
    
    class Animal(models.Model):
        canonical_name = models.CharField(max_length=100, unique=True)
        species = models.CharField(max_length=60, unique=True)
        genus = models.CharField(max_length=30)
        family = models.CharField(max_length=30)
        order = models.CharField(max_length=30)
        # we can't use class as field name
        class_name = models.CharField('Class', db_column='class', max_length=30)
        phylum = models.CharField(max_length=30)
        # we don't need to define kingdom and domain
        # it's clear that it is an animal and eukaryote
    
        def __str__(self):
            return '{} ({})'.format(self.canonical_name, self.species)
    

    serializers.py

    from collections import OrderedDict
    
    from rest_framework import serializers
    
    from .models import Species
    
    class SpeciesSerializer(serializers.HyperlinkedModelSerializer):
        class Meta:
            model = Animal
            fields = ('url', 'id', 'canonical_name', 'species', 'genus',
                'subfamily', 'family', 'order', 'class_name', 'phylum')
    
        def to_representation(self, obj):
            # call the parent method and get an OrderedDict
            data = super(SpeciesSerializer, self).to_representation(obj)
            # generate a list of the keys and replace the key 'class_name'
            keys = list(data.keys())
            keys.insert(keys.index('class_name'), 'class')
            keys.remove('class_name')
            # remove 'class_name' and assign its value to a new key 'class'
            class_name = data.pop('class_name')
            data.update({'class': class_name})
            # create new OrderedDict with the order given by the keys
            response = OrderedDict((k, data[k]) for k in keys)
            return response
    

    The method to_representation helps us to manipulate the output. I have put some extra work here to get the taxonomic ranks in the desired order.

    Thus for the red fox the output looks like this:

    Red fox (Vulpes vulpes)

    {
        "url": "http://localhost:8000/animal/1",
        "id": 1,
        "canonical_name": "Red fox",
        "species": "Vulpes vulpes",
        "genus": "Vulpes",
        "family": "Canidae",
        "order": "Carnivora",
        "class": "Mammalia",
        "phylum": "Chordata"
    }
    

    It is a simplified example and in reality you'd have many more fields or possibly a model for every taxonomic rank, but somewhere you might come across the conflict between the reserved word class and the taxonomic rank class.
    I hope this can help other people too.

    0 讨论(0)
  • 2020-12-06 12:16

    You can rename field in the overloaded version of get_fields() method

    class MySerializer(serializers.Serializer):
        class_ = serializers.ReadOnlyField()
    
        def get_fields(self):
            result = super().get_fields()
            # Rename `class_` to `class`
            class_ = result.pop('class_')
            result['class'] = class_
            return result
    
    0 讨论(0)
  • 2020-12-06 12:26

    You can do it like below

    class SpeciesSerializer(serializers.HyperlinkedModelSerializer):
        class Meta:
            model = Species
            fields = (
                'url', 'id', 'canonical_name', 'slug',  'species', 'genus',
                'subfamily', 'family', 'order','class', 'phylum',
                'ncbi_id', 'ncbi_taxonomy',
            )
            read_only_fields = ('slug',)
            extra_kwargs = {
                'url': {'lookup_field': 'slug'}
            }
    
    SpeciesSerializer._declared_fields["class"] = serializers.CharField(source="class_name")
    

    As explained in below answer

    https://stackoverflow.com/a/47717441/2830850

    0 讨论(0)
提交回复
热议问题