Django REST frameworkでManyToManyフィールドの更新の仕方

作成:2023/02/08
更新:2023/08/16

色々調べてもフロントのコードが乗ってなかったり。シリアライザーでコード書いていたり、ビューで書いていたりでハマったので記録しておく。

Django==4.1.5
djangorestframework==3.14.0

followUser がフォローしているTopicをManyToManyで管理する

models.py

class Topic(models.Model):
    title = models.CharField(max_length=255, unique=True)
    description = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)


class FollowTopic(models.Model):
    followUser = models.OneToOneField(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="followUser"
    )
    topics = models.ManyToManyField(Topic, related_name="followedTopics", blank=True)


views.py
実際にはユーザーを確認する手順が必要になると思う。
Firebaseで認証しているのでユーザーIDはそこから取得している。

class FollowTopicView(ModelViewSet):
    queryset = FollowTopic.objects.all()
    serializer_class = FollowSerializer

    def get_object(self):
        q = FollowTopic.objects.get(pk=self.kwargs["pk"])
        return q

    def update(self, request, *args, **kwargs):
        # フロントから送ってきたデータを取り出す
        data = request.data
        # 狙いのobjectsを取り出す。getの中身はPostの仕方によってになると思う
        update_follow = FollowTopic.objects.get(pk=self.kwargs["pk"])
        update_follow.save
        # いったんManyToManyをすべてクリアする
        update_follow.topics.clear()
        # Topicのidからobjectを取得して、addで追加していく
        for topic in data["topics"]:
            topic_obj = Topic.objects.get(pk=topic)
            update_follow.topics.add(topic_obj)

        serializer = FollowSerializer(update_follow)
        return Response(serializer.data)

    def get_queryset(self):
        try:
            auth = FirebaseAuthentication()
            user, _ = auth.authenticate(self.request)
            # get_or_createにするので初めてクエリーするときに1つIDが発行されるのでエラーにならない
            queryset = FollowTopic.objects.get_or_create(followUser_id=user.id)
            return queryset
        except:
            print("FollowTopic except")



serializers.py

class FollowSerializer(serializers.ModelSerializer):

    class Meta:
        model = FollowTopic
        fields = [
            "id",
            "followUser_id",
            "topics",
        ]
        depth = 1


Next.jsでPutする場合
putするURLにFollowTopicのidを指定しているので、ユーザーは固定されている(Djangoで確認はすべき)。
実際にデータとして送る必要があるのはtopicsにリストの形でIdを入れるだけ。

	const FollowTopicBtn = (token: string) => {
		const ids =
			followData &&
			followData[0].topics.map((t: any) => {
				return t.id;
			});
		const data = {
			topics: [...ids, topic.id],
		};
		const res = axios
			.put(`your URL/${followData[0].id}/`,
				data,
				{
					headers: {
						"Content-Type": "application/json",
						Authorization: `Bearer ${token}`,
					},
				}
			)
			.then((response) => {
				return response.data;
			})
			.catch((error) => {
				console.log(error);
			})
			.finally(() => {
				//更新
				mutate(true);
			});
	};




参考
Django Rest Framework API #22 / Many To Many Relationship , Nested Data
https://www.youtube.com/watch?v=NCBxyw6rDds