跳转到主要内容

增强Django和Django REST Framework中对自然键的支持。

项目描述

Django Natural Keys

Django自然键增强支持。从 wq.db 提取以供通用使用。

Django Natural Keys 提供了一些有用的模型方法(例如 get_or_create_by_natural_key()),这些方法可以加快Django中处理自然键的速度。该模块还提供了一些序列化类,这些类可以简化为具有自然键的模型创建REST API支持。

Latest PyPI Release Release Notes License GitHub Stars GitHub Forks GitHub Issues

Tests Python Support Django Support

使用方法

Django Natural Keys 通过PyPI提供

# Recommended: create virtual environment
# python3 -m venv venv
# . venv/bin/activate
pip install natural-keys

模型API

要在纯Django中使用 自然键,您需要在您的模型类中定义一个 natural_key() 方法,并在管理器类中定义一个 get_natural_key() 方法。使用 Django Natural Keys,您可以扩展 NaturalKeyModel 并定义以下之一

  • Meta.constraints 中添加一个 UniqueConstraint(推荐),
  • Meta.unique_together 中的一个元组,或者
  • 一个具有 unique=True 的模型字段(除 AutoField 之外)

找到的第一个唯一约束将被视为该模型的自然键,并且所有必要的与自然键相关的功能将自动生效。

from natural_keys import NaturalKeyModel

class Event(NaturalKeyModel):
    name = models.CharField(max_length=255)
    date = models.DateField()
    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=('name', 'date'),
                name='event_natural_key',
            )
        ]
        
class Note(models.Model):
    event = models.ForeignKey(Event)
    note = models.TextField()

或者

from natural_keys import NaturalKeyModel

class Event(NaturalKeyModel):
    name = models.CharField(unique=True)

以下方法将在您的模型及其管理器上可用

# Default Django methods
instance = Event.objects.get_by_natural_key('ABC123', date(2016, 1, 1))
instance.natural_key == ('ABC123', date(2016, 1, 1))

# get_or_create + natural keys
instance, is_new = Event.objects.get_or_create_by_natural_key('ABC123', date(2016, 1, 1))

# Like get_or_create_by_natural_key, but discards is_new
# Useful for quick lookup/creation when you don't care whether the object exists already
instance = Event.objects.find('ABC123', date(2016, 1, 1))
note = Note.objects.create(
     event=Event.objects.find('ABC123', date(2016, 1, 1)),
     note="This is a note"
)
instance == note.event

# Inspect natural key fields on a model without instantiating it
Event.get_natural_key_fields() == ('name', 'date')

嵌套自然键

Django 自然键 的一个关键特性是它将自动遍历相关模型中的 ForeignKey(这些模型也应该是 NaturalKeyModel 类)。这使得定义复杂的、任意嵌套的自然键变得轻而易举。

class Place(NaturalKeyModel):
    name = models.CharField(max_length=255, unique=True)

class Event(NaturalKeyModel):
    place = models.ForeignKey(Place)
    date = models.DateField()
    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=('place', 'date'),
                name='event_natural_key',
            )
        ]
Event.get_natural_key_fields() == ('place__name', 'date')
instance = Event.find('ABC123', date(2016, 1, 1))
instance.place.name == 'ABC123'

REST 框架支持

Django 自然键 提供了与 Django REST 框架 的几个集成,主要通过自定义序列化器类实现。在大多数情况下,您可能希望使用以下之一

  • NaturalKeyModelSerializer,或者
  • 伪字段 natural_key_slug(见下文)

如果您只有一个模型,并且该模型的自然键只有一个字符字段,您可能不需要使用这些集成。在您的视图中,您可以直接使用 Django REST 框架内置的 lookup_field 来直接指向您的自然键。

NaturalKeyModelSerializer

NaturalKeyModelSerializer 便于在 REST API 中处理复杂的自然键。它可以与 NaturalKeyModel 一起使用,或者(更常见的是)与一个具有指向 NaturalKeyModel 的外键但本身不是 NaturalKeyModel 的模型一起使用。(一个具体的例子是 vera.Report 模型,它有一个指向 vera.Event 的外键,而 vera.Event 是一个 NaturalKeyModel)。

NaturalKeyModelSerializer 扩展了 DRF 的 ModelSerializer,但对于指向 NaturalKeyModel 的每个外键使用 NaturalKeySerializer。在执行 update()create() 操作时,嵌套的 NaturalKeySerializer 将自动创建引用模型的新实例(如果它们尚未存在,则通过上述 find() 方法)。请注意,NaturalKeyModelSerializer 不覆盖 DRF 对其他字段(无论它们是否构成主模型的自然键)的默认行为。

NaturalKeySerializer 从技术上讲可以用作顶级序列化器,但这是不建议的。设计 NaturalKeySerializer 来处理嵌套自然键,并且不支持更新或非自然键字段。即使与 NaturalKeyModelSerializer 一起使用,NaturalKeySerializer 也永远不会更新现有相关模型实例。相反,它将外键重新指向另一个(可能是新的)相关模型实例。将其视为特殊的 RelatedField 类而不是真正的 Serializer 可能有助于理解。

您可以将 NaturalKeyModelSerializerDjango REST 框架 和/或 wq.db 一起使用,就像使用任何其他序列化器一样

# Django REST Framework usage example
from rest_framework import viewsets
from rest_framework import routers
from natural_keys import NaturalKeyModelSerializer
from .models import Event, Note

class EventSerializer(NaturalKeyModelSerializer):
    class Meta:
        model = Event
        
class NoteSerializer(NaturalKeyModelSerializer):
    class Meta:
        model = Note

class EventViewSet(viewsets.ModelViewSet):
    queryset = Event.objects.all()
    serializer_class = EventSerializer

class NoteViewSet(viewsets.ModelViewSet):
    queryset = Note.objects.all()
    serializer_class = NoteSerializer

router = routers.DefaultRouter()
router.register(r'events', EventViewSet)
router.register(r'notes', NoteViewSet)

# wq.db usage example
from wq.db import rest
from natural_keys import NaturalKeyModelSerializer
from .models import Event, Note

rest.router.register_model(Note, serializer=NaturalKeyModelSerializer)
rest.router.register_model(Event, serializer=NaturalKeyModelSerializer)

设置完成后,您可以使用您的 REST API 创建和查看 NaturalKeyModel 实例及其相关数据。为了方便与常规 HTML 表单集成,Django 自然键HTML JSON Forms 包集成,后者通过数组命名约定支持嵌套键,如下面的示例所示。

<form action="/events/" method="post">
  <input name="place[name]">
  <input type="date" name="date">
</form>
// /events.json
[
    {
        "id": 123,
        "place": {"name": "ABC123"},
        "date": "2016-01-01"
    }
]
<form action="/notes/" method="post">
  <input name="event[place][name]">
  <input type="date" name="event[date]">
  <textarea name="note"></textarea>
</form>
// /notes.json
[
    {
        "id": 12345,
        "event": {
            "place": {"name": "ABC123"},
            "date": "2016-01-01"
        },
        "note": "This is a note"
    }
]

自然键别名

作为使用 NaturalKeyModelSerializer / NaturalKeySerializer 的替代方案,您还可以使用单个类似短语的字段进行查找和序列化。NaturalKeyModel(及其相关查询集)为此目的定义了一个伪字段,natural_key_slug

class Place(NaturalKeyModel):
    name = models.CharField(max_length=255, unique=True)
    
class Room(NaturalKeyModel)
    place = models.ForeignKey(Place, models.ON_DELETE)
    name = models.CharField(max_length=255)
    
    class Meta:
        unique_together = (('place', 'name'),)
room = Room.objects.find("ABC123", "MainHall")
assert(room.natural_key_slug == "ABC123-MainHall")
assert(room == Room.objects.get(natural_key_slug="ABC123-MainHall"))

您可以将此功能公开在您的 REST API 中,以显示自然键而不是数据库生成的 ID。为此,您可能需要执行以下操作:

  1. 创建一个带有 id = serializers.ReadOnlyField(source='natural_key_slug') 的常规序列化器
  2. 在您的 ModelViewSet(或类似通用类)上设置 lookup_field = 'natural_key_slug' 并相应地更新 URL 注册
  3. 确保任何相关模型的外键使用 serializers.SlugRelatedField(slug_field='natural_key_slug') 进行序列化

wq.db 中,通过在注册 router 时设置 "lookup" 属性,可以实现上述所有功能

# myapp/rest.py
from wq.db import rest
from .models import Room

rest.router.register_model(
    Room,
    fields='__all__',
    lookup='natural_key_slug',
)

请注意,如果任何组件值包含分隔符字符(默认为 -),则 natural_key_slug 可能不会按预期行为。为了减轻这种情况,您可以在模型类上设置 natural_key_separator 为另一个字符。

项目详情


下载文件

下载您平台上的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。

源分布

natural-keys-2.1.1.tar.gz (18.4 kB 查看散列)

上传时间

构建分布

natural_keys-2.1.1-py3-none-any.whl (10.6 kB 查看散列)

上传时间 Python 3

支持者:

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面