マイリストからの評価値の削除
バックエンド
ビュー
backend/api/online/views.py
...(略)...
class RatingsView(APIView):
"""評価値ビュークラス
"""
def get(self, request, format=None):
...(略)...
def post(self, request, format=None):
...(略)...
def delete(self, request, format=None):
"""評価値を削除する。
Requests
--------
id : str
評価値ID
Response
--------
rating : json
評価値
"""
# リクエストパラメタの取得
id = request.data['id']
# オブジェクトの削除
rating_model = Rating.objects.get(pk=id)
rating_model.delete()
# レスポンス
rating_dict = RatingMapper(rating_model).as_dict()
data = {
'rating': rating_dict,
}
return Response(data, status.HTTP_200_OK)
フロントエンド
API
frontend/src/services/ratings/deleteRating.ts
import { ApiContext, Movie, Rating, User } from '@/types/data';
import { fetcher } from '@/utils';
const context: ApiContext = {
apiRootUrl: process.env.NEXT_PUBLIC_API_BASE_URL,
};
/**
* 評価値削除API
* @param user - ユーザ
* @param movie - 映画
* @returns 評価値
*/
const deleteRating = async (user: User, movie: Movie): Promise<{ rating: Rating }> => {
const id = user.id + '_' + String(movie.id).padStart(6, '0');
const body = {
id: id,
};
return await fetcher(`${context.apiRootUrl?.replace(/\/$/g, '')}/ratings/`, {
method: 'DELETE',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
};
export default deleteRating;
コンポーネント
frontend/src/app/components/movie/MovieCard.tsx
import { Movie, User } from '@/types/data';
import Image from 'next/image';
import Link from 'next/link';
import React from 'react';
import StarRating from '../rating/StarRating';
import postRating from '@/services/ratings/postRating';
import getRating from '@/services/ratings/getRating';
import { IconButton } from '@mui/material'; // <- 追加
import HighlightOffIcon from '@mui/icons-material/HighlightOff'; // <- 追加
const STAR_WIDTH = 24;
type Props = {
movie: Movie;
user: User;
isMyList?: boolean; // <- 追加
handleRatingClick: Function;
handleDelete?: Function; // <- 追加
};
const MovieCard = (props: Props) => {
...(略)...
return (
<>
<article className="h-72 w-32 shadow" key={props.movie.id}>
...(略)...
<div className="mx-1">
<div className="line-clamp-2 font-semibold text-gray-800">{props.movie.title}</div>
<div className="flex">
<div className="my-1 rounded bg-gray-200 px-1 py-0.5 text-sm text-gray-800">
{props.movie.year}
</div>
{/* ↓追加 */}
{props.isMyList == true ? (
<IconButton onClick={() => props.handleDelete!(props.movie)}>
<HighlightOffIcon className="text-sm" />
</IconButton>
) : (
<></>
)}
{/* ↑追加 */}
</div>
</div>
</article>
</>
);
};
export default MovieCard;
frontend/src/app/components/movie/MovieList.tsx
'use client';
import { Movie, User } from '@/types/data';
import React, { useEffect, useState } from 'react';
import MovieCard from './MovieCard';
import deleteRating from '@/services/ratings/deleteRating'; // <- 追加
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
type Props = {
phrase: string;
movies: Movie[];
perPage: number;
user: User;
isMyList?: boolean; // <- 追加
};
const MovieList = (props: Props) => {
...(略)...
useEffect(() => {
// ↓追加
if (movies.length <= 0) {
setCurrentMovies(movies);
}
// ↑追加
const start = props.perPage * currentPage;
const end = start + props.perPage;
const currentMovies_ = movies.slice(start, end);
// ↓修正
if (currentMovies_.length <= 0) {
let currentPage_ = currentPage <= 0 ? 0 : currentPage - 1;
setCurrentPage(currentPage_);
} else {
setCurrentMovies(currentMovies_);
}
// ↑修正
}, [movies, currentPage]);
...(略)...
// ↓追加
const handleDelete = async (movie: Movie) => {
await deleteRating(props.user, movie);
const movies_ = movies.filter((movie_) => movie_.id != movie.id);
setMovies(movies_);
};
// ↑追加
return (
<>
<div className="text-xl font-bold text-gray-800">{props.phrase}</div>
<div className="mx-4 my-4 flex justify-between">
<button
...(略)...
</button>
{currentMovies?.map((movie) => (
<MovieCard
movie={movie}
user={props.user}
isMyList={props.isMyList} // <- 追加
handleRatingClick={handleRatingClick}
handleDelete={handleDelete} // <- 追加
key={movie.id}
/>
))}
<button
...(略)...
</button>
</div>
</>
);
};
export default MovieList;
ページ
frontend/src/app/mypage/page.tsx
...(略)...
const MyPage = async () => {
...(略)...
return (
<>
{session?.user ? (
<>
<MovieList
phrase={phrase}
movies={movies}
perPage={PER_PAGE}
user={user!}
isMyList={true} // <- 追加
/>
<MovieListBPR user={user!} perPage={PER_PAGE} />
</>
) : (
<></>
)}
</>
);
};
export default MyPage;
ブラウザで下記URLにアクセスしてください。
マイリストから削除アイコンをクリックすると、評価値が削除されます。