我如何 'cleanly' 在模型之间创建这种 Django/DRF 序列化关系?
How do I 'cleanly' create this Django/DRF serialization relationship between models?
我读到循环导入是 'code smell' 并且从根本上说是一个糟糕的设计选择。我有一个包含模型、用户、甲板、手的应用程序。我希望用户能够创建 Hand 而无需创建 Deck,但如果需要,还可以让用户选择将 Hand 放入 Deck 中。所以我最终得到这样的结果:
(<表示外键关系)
用户<牌组<手
&&
用户<牌组
&&
用户<手
models.py:
class User(AbstractUser):
pass
class Deck(models.Model):
created = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=100, unique=True,
blank=False, null=False)
user = models.ForeignKey('users.User', related_name='decks',
on_delete=models.CASCADE, null=False)
class Hand(models.Model):
created = models.DateTimeField(auto_now_add=True)
deck = models.ForeignKey('goals.Deck', related_name='hands', on_delete=models.CASCADE, null=True)
name = models.CharField(max_length=100, blank=False, null=False)
user = models.ForeignKey('users.User', related_name='hands', on_delete=models.CASCADE, null=False)
serializers.py:
class HandSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.ReadOnlyField(source='user.username')
deck = serializers.CharField(required=False, source='deck.name')
class Meta:
model = Hand
fields = ('url', 'id', 'created',
'deck', 'name', 'user')
extra_kwargs = {
'url': {
'view_name': 'goals:hand-detail',
}
}
class DeckSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.ReadOnlyField(source='user.username')
hands = HandSerializer(many=True, read_only=True)
class Meta:
model = Deck
fields = ('url', 'id', 'created', 'name', 'user')
extra_kwargs = {
'url': {
'view_name': 'goals:deck-detail',
}
}
class UserSerializer(serializers.HyperlinkedModelSerializer):
decks = DeckSerializer(many=True)
hands = HandSerializer(many=True)
...
这是正确的方法 API-design-wise 以实现我想要的 app-design-wise 吗?如果没有,我应该怎么做呢?如果是这样,当我将用户从 ReadOnlyField 更改为 UserSerializer() 字段时,如何解决循环导入错误?
编辑:
我在想如果循环导入这种方法不好或不可能,我可以创建一个标准的单向关系,如:
用户 --> 牌组 --> 手
并有一个对用户隐藏的默认牌组,这样用户仍然可以创建 Hand 而无需创建 his/her 自己的牌组,因为它已经默认完成(只是隐藏起来)。但这也感觉像是一种 hack,我不知道这种方法是否比最初的方法更难闻。
这是正确的。假设我想请求 ID 为 1 的 Hand。我会为 /api/hands/1 发出 GET 请求,对吧?我真的希望它能序列化一个完整的用户吗?可能是。这取决于。
要绕过它,您可以定义如下内容:
MinimalUserSerializer
- Returns only email, username, and ID.
- Does not return hands.
并且您将使用它而不是 returns 一直在处理的完整 UserSerializer。
我读到循环导入是 'code smell' 并且从根本上说是一个糟糕的设计选择。我有一个包含模型、用户、甲板、手的应用程序。我希望用户能够创建 Hand 而无需创建 Deck,但如果需要,还可以让用户选择将 Hand 放入 Deck 中。所以我最终得到这样的结果:
(<表示外键关系)
用户<牌组<手
&&
用户<牌组
&&
用户<手
models.py:
class User(AbstractUser):
pass
class Deck(models.Model):
created = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=100, unique=True,
blank=False, null=False)
user = models.ForeignKey('users.User', related_name='decks',
on_delete=models.CASCADE, null=False)
class Hand(models.Model):
created = models.DateTimeField(auto_now_add=True)
deck = models.ForeignKey('goals.Deck', related_name='hands', on_delete=models.CASCADE, null=True)
name = models.CharField(max_length=100, blank=False, null=False)
user = models.ForeignKey('users.User', related_name='hands', on_delete=models.CASCADE, null=False)
serializers.py:
class HandSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.ReadOnlyField(source='user.username')
deck = serializers.CharField(required=False, source='deck.name')
class Meta:
model = Hand
fields = ('url', 'id', 'created',
'deck', 'name', 'user')
extra_kwargs = {
'url': {
'view_name': 'goals:hand-detail',
}
}
class DeckSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.ReadOnlyField(source='user.username')
hands = HandSerializer(many=True, read_only=True)
class Meta:
model = Deck
fields = ('url', 'id', 'created', 'name', 'user')
extra_kwargs = {
'url': {
'view_name': 'goals:deck-detail',
}
}
class UserSerializer(serializers.HyperlinkedModelSerializer):
decks = DeckSerializer(many=True)
hands = HandSerializer(many=True)
...
这是正确的方法 API-design-wise 以实现我想要的 app-design-wise 吗?如果没有,我应该怎么做呢?如果是这样,当我将用户从 ReadOnlyField 更改为 UserSerializer() 字段时,如何解决循环导入错误?
编辑:
我在想如果循环导入这种方法不好或不可能,我可以创建一个标准的单向关系,如:
用户 --> 牌组 --> 手
并有一个对用户隐藏的默认牌组,这样用户仍然可以创建 Hand 而无需创建 his/her 自己的牌组,因为它已经默认完成(只是隐藏起来)。但这也感觉像是一种 hack,我不知道这种方法是否比最初的方法更难闻。
这是正确的。假设我想请求 ID 为 1 的 Hand。我会为 /api/hands/1 发出 GET 请求,对吧?我真的希望它能序列化一个完整的用户吗?可能是。这取决于。
要绕过它,您可以定义如下内容:
MinimalUserSerializer
- Returns only email, username, and ID.
- Does not return hands.
并且您将使用它而不是 returns 一直在处理的完整 UserSerializer。