增强Django和Django REST Framework中对自然键的支持。
项目描述
Django Natural Keys
Django自然键增强支持。从 wq.db 提取以供通用使用。
Django Natural Keys 提供了一些有用的模型方法(例如 get_or_create_by_natural_key()
),这些方法可以加快Django中处理自然键的速度。该模块还提供了一些序列化类,这些类可以简化为具有自然键的模型创建REST API支持。
使用方法
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
可能有助于理解。
您可以将 NaturalKeyModelSerializer
与 Django 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。为此,您可能需要执行以下操作:
- 创建一个带有
id = serializers.ReadOnlyField(source='natural_key_slug')
的常规序列化器 - 在您的
ModelViewSet
(或类似通用类)上设置lookup_field = 'natural_key_slug'
并相应地更新 URL 注册 - 确保任何相关模型的外键使用
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
为另一个字符。
项目详情
下载文件
下载您平台上的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。