在Django和Django REST Framework中使用保留字"class"作为字段名称

cez*_*zar 9 python django bioinformatics python-3.x django-rest-framework

问题描述

分类学是在共同特征的基础上定义和命名生物有机体群的科学.生物被组合成分类群(单数:分类单元),这些组被赋予分类等级.现代使用的主要等级是领域,王国,门,阶级,秩序,家庭,属和物种.有关维基百科中的分类分类排名的更多信息.

按照维基百科文章分类排名中的红狐狸示例,我需要创建一个这样的JSON输出:

{
    "species": "vulpes",
    "genus": "Vulpes",
    "family": "Canidae",
    "order": "Carnivora",
    "class": "Mammalia",
    "phylum": "Chordata",
    "kingdom": "Animalia",
    "domain": "Eukarya"
}
Run Code Online (Sandbox Code Playgroud)

由于Django REST Framework基于字段名称创建密钥,因此问题出现在分类等级(示例中为粗体),因为它是Python中的保留字,不能用作变量名.

我试过了什么

在Django中创建的模型类看起来像这样:

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
Run Code Online (Sandbox Code Playgroud)

有没有办法解决这个问题并生成所需的输出?如果没有,解决这个问题的最佳做法是什么?

小智 16

您可以通过字符串设置类的属性,如下所示:

class SpeciesSerializer(serializers.Serializer):
    species = serializers.CharField()
    genus = serializers.CharField()
    family = serializers.CharField()
    vars()['class'] = serializers.CharField()
Run Code Online (Sandbox Code Playgroud)


小智 11

您可以在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
Run Code Online (Sandbox Code Playgroud)


Tar*_*ani 6

你可以像下面那样做

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")
Run Code Online (Sandbox Code Playgroud)

如以下答案所述

/sf/answers/3340220901/


cez*_*zar 5

生物信息学领域的其他软件开发人员可能对这个问题的解决方案感兴趣,所以我在这里发布我的方法,如Alasdair所建议的.

我们的目标是为生物物种创建一个模型,为了简单起见,我们说一个动物,并用Django REST Framework创建一个代表正确分类等级的终点.

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)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

该方法to_representation可以帮助我们操作输出.我已经在这里做了一些额外的工作,以便按照预期的顺序获得分类等级.

因此,对于红狐狸,输出看起来像这样:

红狐(赤狐)

{
    "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"
}
Run Code Online (Sandbox Code Playgroud)

这是一个简单的例子,在现实中你有更多的领域或可能对每个分类地位的模型,而是介于您可能会遇到的保留字之间的冲突class和分类地位.
我希望这也可以帮助其他人.