如何在具有自定义.update()的情况下更新实例中的值以更新DRF可写嵌套序列化程序中的多对多关系

Man*_*mar 8 django django-orm django-rest-framework

我有三个模型玩家,团队和成员,其中玩家和团队使用成员资格作为中间模型具有多对多关系.

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)
Run Code Online (Sandbox Code Playgroud)

现在我被要求使用django rest框架更新此成员资格.我尝试通过编写团队序列化程序的自定义来更新使用Writable嵌套序列化程序的那些.update().

@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'
            )
Run Code Online (Sandbox Code Playgroud)

现在,这适用于更新数据库中的成员资格表值.我遇到的唯一问题是我无法更新内存中的预取实例,在PATCH请求此API更新数据库中的值但API响应显示过时的数据.

对同一资源的下一个GET请求提供更新的数据.任何在django中使用多对多关系并为可写嵌套序列化器编写自定义更新/创建方法的人都可以帮助我理解解决此问题的可能方法.

Man*_*mar 1

在这种情况下,我能够找到问题所在。我正在使用.prefetch_related()查询集,这导致实例将预取数据用于多对多关系。我有两个解决方案,可能会导致更多的数据库点击来获取更新的数据。

1.不使用.prefetch_related()

一种明显的方法是不使用.prefetch_related()Which不推荐,因为它会导致大量的数据库命中。

或者

2. 在序列化器的更新方法中更新多对多关系后获取更新的模型实例

instance = self.context['view'].get_queryset().get(
    id=instance.id
)
Run Code Online (Sandbox Code Playgroud)

修改.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)
Run Code Online (Sandbox Code Playgroud)

如果有人有更好的方法,如果他们能为这个问题添加答案,我将不胜感激。