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

Posted

技术标签:

【中文标题】在 Django 和 Django REST Framework 中使用保留字“class”作为字段名【英文标题】:Using the reserved word "class" as field name in Django and Django REST Framework 【发布时间】:2018-05-17 17:47:03 【问题描述】:

问题描述

分类学是基于共同特征定义和命名生物有机体群体的科学。有机体被归类为分类群(单数:分类单元),这些组被赋予分类等级。现代使用的主要等级是域、界、门、类、目、科、属和种。 更多关于***中Taxonomy 和Taxonomic ranks 的信息。

按照 WikipediaTaxonomic rank 文章中 red fox 的示例,我需要创建一个这样的 JSON 输出:


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

由于 Django REST 框架根据字段名称创建键,因此分类等级 class(示例中为粗体)会出现问题,因为它是 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

问题

是否有任何可能的方法来解决此问题并生成所需的输出? 如果不是,解决此问题的最佳做法是什么?

【问题讨论】:

请参阅this question 了解如何重命名 django-rest-framework 中的字段。您可以在 Django 模型字段中使用 species_classklass 之类的内容。 @Alasdair 非常感谢,朋友!在序列化方法to_representation 的帮助下,我找到了解决方案。我现在调用的模型字段class_name,这并不麻烦,因为客户端只关心输出。你能添加几行作为答案,所以我可以投票/接受它。 很高兴有帮助。如果您添加自己的答案可能会更好,因为您可以展示您为使其正常工作所做的实际工作。 【参考方案1】:

你可以在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

【讨论】:

谢谢!我也试试这个方法。 绝对是最干净的方法【参考方案2】:

你可以像下面那样做

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

如下回答所述

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

【讨论】:

【参考方案3】:

生物信息学领域的其他软件开发人员可能对此问题的解决方案感兴趣,因此我在此处发布了我的方法,这是 Alasdair 建议的。

我们的目标是为一个活的物种创建一个模型,为了简单起见,假设是一种动物,并使用 Django REST 框架创建一个代表正确分类等级的端点。

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

to_representation 方法帮助我们操作输出。我在这里做了一些额外的工作,以使分类等级按所需的顺序排列。

因此对于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"

这是一个简化的示例,实际上您将拥有更多字段,或者可能为每个分类等级提供一个模型,但在某处您可能会遇到保留字 class 和分类等级 class 之间的冲突。 我希望这也可以帮助其他人。

【讨论】:

【参考方案4】:

你可以像这样通过字符串设置类的属性:

class SpeciesSerializer(serializers.Serializer):
    species = serializers.CharField()
    genus = serializers.CharField()
    family = serializers.CharField()
    vars()['class'] = serializers.CharField()

【讨论】:

以上是关于在 Django 和 Django REST Framework 中使用保留字“class”作为字段名的主要内容,如果未能解决你的问题,请参考以下文章

在django里写自己的api

django rest_framework入门四-类视图APIView

Django + AngularJS:没有使用普通 URL 和视图的 Django REST 框架的类 REST 端点?

带有 django-rest-auth 和 django-rest-knox 的 AttributeError - 令牌序列化器

Django、REST 和 Angular 路由

如何使用 django-rest-auth 和 Mailgun 从 Django 发送密码重置电子邮件