如何在具有自定义 .update() 更新 DRF 可写嵌套序列化程序中的多对多关系时更新实例值
How to update values in instance when have a custom .update() to update many-to-many relations in DRF writable nested serializer
我有三个模型 Player、Team 和 Membership,其中 Player 和 Team 使用 Membership 作为中介模型具有多对多关系。
class Player(models.Model):
name = models.CharField(max_length=254)
rating = models.FloatField(null=True)
install_ts = models.DateTimeField(auto_now_add=True, blank=True)
update_ts = models.DateTimeField(auto_now_add=True, blank=True)
class Team(models.Model):
name = models.CharField(max_length=254)
rating = models.FloatField(null=True)
players = models.ManyToManyField(
Player,
through='Membership',
through_fields=('team', 'player'))
is_active = models.BooleanField(default=True)
install_ts = models.DateTimeField(auto_now_add=True, blank=True)
update_ts = models.DateTimeField(auto_now_add=True, blank=True)
class Membership(models.Model):
team = models.ForeignKey('Team')
player = models.ForeignKey('Player')
#date_of_joining = models.DateTimeField()
install_ts = models.DateTimeField(auto_now_add=True, blank=True)
update_ts = models.DateTimeField(auto_now_add=True, blank=True)
现在我需要使用 django rest 框架更新此成员资格。我尝试通过编写团队序列化程序的自定义 .update()
使用 Writable nested serializers 更新那些。
@transaction.atomic
def update(self, instance, validated_data):
'''
Cutomize the update function for the serializer to update the
related_field values.
'''
if 'memberships' in validated_data:
instance = self._update_membership(instance, validated_data)
# remove memberships key from validated_data to use update method of
# base serializer class to update model fields
validated_data.pop('memberships', None)
return super(TeamSerializer, self).update(instance, validated_data)
def _update_membership(self, instance, validated_data):
'''
Update membership data for a team.
'''
memberships = self.initial_data.get('memberships')
if isinstance(membership, list) and len(memberships) >= 1:
# make a set of incoming membership
incoming_player_ids = set()
try:
for member in memberships:
incoming_player_ids.add(member['id'])
except:
raise serializers.ValidationError(
'id is required field in memberships objects.'
)
Membership.objects.filter(
team_id=instance.id
).delete()
# add merchant member mappings
Membership.objects.bulk_create(
[
Membership(
team_id=instance.id,
player_id=player
)
for player in incoming_player_ids
]
)
return instance
else:
raise serializers.ValidationError(
'memberships is not a list of objects'
)
现在这对于更新数据库中的成员值 table 非常有效。我面临的唯一问题是我无法更新内存中的预取实例,根据对此 API 的 PATCH 请求更新数据库中的值,但 API 响应显示过时的数据。
对同一资源的下一个 GET 请求将提供更新后的数据。任何在 django 中处理多对多关系并为 writable 嵌套序列化器编写自定义 update/create 方法的人都可以帮助我理解解决这个问题的可能方法。
我认为你必须 "reload" 玩家字段 instance.players.all()
,因为查询集已经在序列化程序的 is_valid
方法中计算过。
@transaction.atomic
def update(self, instance, validated_data):
'''
Cutomize the update function for the serializer to update the
related_field values.
'''
if 'memberships' in validated_data:
instance = self._update_membership(instance, validated_data)
# remove memberships key from validated_data to use update method of
# base serializer class to update model fields
validated_data.pop('memberships', None)
instance.players.all()
return super(TeamSerializer, self).update(instance, validated_data)
在这种情况下,我能够找到 issue。我在 queryset 上使用 .prefetch_related()
,这导致实例对多对多关系使用预取数据。我有两个解决方案,可能会导致更多的数据库命中以获取更新的数据。
1。不使用 .prefetch_related()
一个明显的方法是不使用 .prefetch_related()
,不推荐,因为它会导致大量的数据库命中。
或
2。在序列化程序的更新方法中更新多对多关系后获取更新的模型实例
instance = self.context['view'].get_queryset().get(
id=instance.id
)
已修改 .update()
,共 TeamSerializer
@transaction.atomic
def update(self, instance, validated_data):
'''
Cutomize the update function for the serializer to update the
related_field values.
'''
if 'memberships' in validated_data:
instance = self._update_membership(instance, validated_data)
# remove memberships key from validated_data to use update method of
# base serializer class to update model fields
validated_data.pop('memberships', None)
# fetch updated model instance after updating many-to-many relations
instance = self.context['view'].get_queryset().get(
id=instance.id
)
return super(TeamSerializer, self).update(instance, validated_data)
如果有人有更好的方法,如果他们可以添加这个问题的答案,我将不胜感激。
我有三个模型 Player、Team 和 Membership,其中 Player 和 Team 使用 Membership 作为中介模型具有多对多关系。
class Player(models.Model):
name = models.CharField(max_length=254)
rating = models.FloatField(null=True)
install_ts = models.DateTimeField(auto_now_add=True, blank=True)
update_ts = models.DateTimeField(auto_now_add=True, blank=True)
class Team(models.Model):
name = models.CharField(max_length=254)
rating = models.FloatField(null=True)
players = models.ManyToManyField(
Player,
through='Membership',
through_fields=('team', 'player'))
is_active = models.BooleanField(default=True)
install_ts = models.DateTimeField(auto_now_add=True, blank=True)
update_ts = models.DateTimeField(auto_now_add=True, blank=True)
class Membership(models.Model):
team = models.ForeignKey('Team')
player = models.ForeignKey('Player')
#date_of_joining = models.DateTimeField()
install_ts = models.DateTimeField(auto_now_add=True, blank=True)
update_ts = models.DateTimeField(auto_now_add=True, blank=True)
现在我需要使用 django rest 框架更新此成员资格。我尝试通过编写团队序列化程序的自定义 .update()
使用 Writable nested serializers 更新那些。
@transaction.atomic
def update(self, instance, validated_data):
'''
Cutomize the update function for the serializer to update the
related_field values.
'''
if 'memberships' in validated_data:
instance = self._update_membership(instance, validated_data)
# remove memberships key from validated_data to use update method of
# base serializer class to update model fields
validated_data.pop('memberships', None)
return super(TeamSerializer, self).update(instance, validated_data)
def _update_membership(self, instance, validated_data):
'''
Update membership data for a team.
'''
memberships = self.initial_data.get('memberships')
if isinstance(membership, list) and len(memberships) >= 1:
# make a set of incoming membership
incoming_player_ids = set()
try:
for member in memberships:
incoming_player_ids.add(member['id'])
except:
raise serializers.ValidationError(
'id is required field in memberships objects.'
)
Membership.objects.filter(
team_id=instance.id
).delete()
# add merchant member mappings
Membership.objects.bulk_create(
[
Membership(
team_id=instance.id,
player_id=player
)
for player in incoming_player_ids
]
)
return instance
else:
raise serializers.ValidationError(
'memberships is not a list of objects'
)
现在这对于更新数据库中的成员值 table 非常有效。我面临的唯一问题是我无法更新内存中的预取实例,根据对此 API 的 PATCH 请求更新数据库中的值,但 API 响应显示过时的数据。
对同一资源的下一个 GET 请求将提供更新后的数据。任何在 django 中处理多对多关系并为 writable 嵌套序列化器编写自定义 update/create 方法的人都可以帮助我理解解决这个问题的可能方法。
我认为你必须 "reload" 玩家字段 instance.players.all()
,因为查询集已经在序列化程序的 is_valid
方法中计算过。
@transaction.atomic
def update(self, instance, validated_data):
'''
Cutomize the update function for the serializer to update the
related_field values.
'''
if 'memberships' in validated_data:
instance = self._update_membership(instance, validated_data)
# remove memberships key from validated_data to use update method of
# base serializer class to update model fields
validated_data.pop('memberships', None)
instance.players.all()
return super(TeamSerializer, self).update(instance, validated_data)
在这种情况下,我能够找到 issue。我在 queryset 上使用 .prefetch_related()
,这导致实例对多对多关系使用预取数据。我有两个解决方案,可能会导致更多的数据库命中以获取更新的数据。
1。不使用 .prefetch_related()
一个明显的方法是不使用 .prefetch_related()
,不推荐,因为它会导致大量的数据库命中。
或
2。在序列化程序的更新方法中更新多对多关系后获取更新的模型实例
instance = self.context['view'].get_queryset().get(
id=instance.id
)
已修改 .update()
,共 TeamSerializer
@transaction.atomic
def update(self, instance, validated_data):
'''
Cutomize the update function for the serializer to update the
related_field values.
'''
if 'memberships' in validated_data:
instance = self._update_membership(instance, validated_data)
# remove memberships key from validated_data to use update method of
# base serializer class to update model fields
validated_data.pop('memberships', None)
# fetch updated model instance after updating many-to-many relations
instance = self.context['view'].get_queryset().get(
id=instance.id
)
return super(TeamSerializer, self).update(instance, validated_data)
如果有人有更好的方法,如果他们可以添加这个问题的答案,我将不胜感激。