Uso de la palabra reservada “clase” como nombre de campo en Django y Django REST Framework

Descripción del problema

La taxonomía es la ciencia de definir y nombrar grupos de organismos biológicos sobre la base de características compartidas. Los organismos se agrupan en taxones (singular: taxón) y estos grupos reciben un rango taxonómico. Los rangos principales en uso moderno son dominio, reino, filo, clase, orden, familia, género y especie. Más información sobre taxonomía y rangos taxonómicos en Wikipedia.

Siguiendo el ejemplo para el zorro rojo en el artículo Clasificación taxonómica en Wikipedia , necesito crear una salida JSON como esta:

{ "species": "vulpes", "genus": "Vulpes", "family": "Canidae", "order": "Carnivora", " class ": "Mammalia", "phylum": "Chordata", "kingdom": "Animalia", "domain": "Eukarya" } 

Dado que Django REST Framework crea las claves basadas en los nombres de los campos, el problema surge con la clase de rango taxonómico (negrita en el ejemplo), ya que es una palabra reservada en Python y no se puede usar como nombre de variable.

Lo que he intentado

Una clase modelo creada en Django se vería así:

 class Species(models.Model): species = models.CharField() genus = models.CharField() family = models.CharField() # class = models.CharField() - class is reserved word in Python # class_ = models.CharField() - Django doesn't allow field names # ending with underscore. That wouldn't be either a satisfying solution. # further fields 

Pregunta

¿Hay alguna forma posible de resolver este problema y generar el resultado deseado? Si no es así, ¿cuál es la mejor práctica para solucionar este problema?

Puedes hacerlo como abajo.

 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") 

Como se explica en la siguiente respuesta

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

Otros desarrolladores de software en el campo de la bioinformática podrían estar interesados ​​en una solución de este problema, por lo que publico aquí mi enfoque como lo sugiere Alasdair .

El objective es crear un modelo para una especie viva, en aras de la simplicidad, digamos un animal, y crear un punto final con Django REST Framework que represente los rangos taxonómicos correctos.

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 

El método to_representation nos ayuda a manipular la salida. He puesto un poco de trabajo adicional aquí para obtener los rangos taxonómicos en el orden deseado.

Así, para el zorro rojo, la salida se ve así:

Zorro rojo ( 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" } 

Es un ejemplo simplificado y, en realidad, tendría muchos más campos o posiblemente un modelo para cada rango taxonómico, pero en algún lugar puede encontrar el conflicto entre la class palabras reservada y la clase de rango taxonómico.
Espero que esto pueda ayudar a otras personas también.

Puede cambiar el nombre del campo en la versión sobrecargada del método get_fields()

 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