24.06.12_TIL ( 팀 프로젝트 : AI NOST Django ) _ 14. db데이터 지우기, deepl 트러블 슈팅, User권한 확인, 사용자 아니면 delete버튼 안보이기
[ 세번째 프로젝트 ]
AI를 이용한 소설 사이트를 만들어 보자.
++ 팀 스로젝트로 팀과의 협업이 중요하다.
++ 장고 공식 문서는 항상 확인하기
https://docs.djangoproject.com/en/4.2/
++ 랭체인 공식 문서
https://www.langchain.com/
++ 리액트 공식문서
https://ko.legacy.reactjs.org/ # 한국어
https://ko.react.dev/
https://github.com/1489ehdghks/NOST.git
1. 디비에 남아있는 회원정보를 지워보자
sell 로 해서 지우는 방법이 있다.
python manage.py shell
특정 유저한테서 책만들기 shell로 하려고 했는데 안되네ㅜㅜ
python manage.py shell
from accounts.models import User
from books.models import Book //모델에 내 앱모델이름
2. 유저 권한 확인
유저 이메일 인증해서 들어가서 잘 되는지 확인한다.
연결해놓은 것이 토큰값을 확인하고 되는 것을 볼 수 있다.
처음 Get 으로 된것도 못가져와서 다음과 같은 권한으로 바꾸었다.
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
permission_classes = [IsAuthenticatedOrReadOnly]
좋아요와 별점이 사용자가 하는데도 되지 않아서 백엔드에서 수정하였다.
like_bool 을 없애고 if 문을 하나 없애었다.
그리고 별점을 소수점 한자리까지만 하기 위해서 다음과 같이 수정
class BookSerializer(serializers.ModelSerializer):
average_rating = serializers.SerializerMethodField()
user_nickname = serializers.SerializerMethodField()
chapters = ChapterSerializer(many=True, read_only=True)
class Meta:
model = Book
fields = "__all__"
def get_average_rating(self, book):
avg_rating = Rating.objects.filter(book=book).aggregate(avg_rating=Avg("rating"))["avg_rating"]
return round(avg_rating, 1) if avg_rating is not None else None
소수점 한자리까지만 나오기가 된다.
3. deepl 트러블 슈팅
번역AI 프로그램인 deep_L 을 실행하는데 오류가 떴다.
AttributeError: 'Translator' object has no attribute 'translate_text'
[07/Jun/2024 12:05:23] "POST /api/books/ HTTP/1.1" 500 114084
이런 오류 메세지....
왜 없다고 하는지 모르겠다.
books/ deepl_translation.py
import deepl
from django.conf import settings
# 요약 내용 지정 언어로 번역
def translate_summary(content, language) :
auth_key = settings.DEEPL_API_KEY
translator = deepl.Translator(auth_key)
if isinstance(content,dict) : #data 값이 dict 형태일때 번역
translated_data = {}
for key,value in content.items() :
result = translator.translate_text(value, target_lang=language)
translated_data[key] = result.text
return translated_data
elif isinstance(content,str) : #data 값이 text 형태일때 번역
result = translator.translate_text(content, target_lang=language)
return result.text
멀쩡히 있는데 안나온다.
deepl 과 deepl.py를 두개 설치해 주셨다고 했는데
이게 버전차이로 충돌이 나서 두개를 한꺼번에 가져오지 못하고
중간에 가져오는 것을 다 못가져와서 translate_text 이걸 못가져온 거였다.ㅠㅠ
이걸로 6시간을 끙끙.... 누구는 되고 누구는 안되고.... 아마 가져오고 안가져오고 랜덤이었던것 같다.
pip uninstall deepl
pip uninstall deepl-py
그래서 버전을 다 삭제해주고 다시 deepl만 깔아서 코드를 조금 수정해주어야한다.
pip install deepl
새로운 프론트엔드 파일도 받아와서 잘 연결되어 생성되는 것을 확인할 수 있다....ㅠㅠ
아이고... 정말 이거 6시간 걸렸는데 다 정리되서 설명 써놓으니까 몇줄안에 끝나다니ㅜㅜ 억울하다...
4. 사용자 아니면 delete버튼 안보이기
src/ pages/ widgets/ bookdetail.jsx 수정하기
import useAuthStore from '../../shared/store/AuthStore'; // useAuthStore 불러오기
...
const BookDetail = () => {
...
const { userId: currentUserId } = useAuthStore(); // 현재 사용자 ID 가져오기
useEffect(() => {
axios.get(`http://127.0.0.1:8000/api/books/${id}/`)
.then(response => {
setBookData(response.data);
console.log('data : ', response.data);
})
.catch(error => {
console.error('Error fetching book data:', error);
});
axios.get(`http://127.0.0.1:8000/api/books/${id}/comments/`)
.then(response => {
setComments(response.data || []);
})
.catch(error => {
console.error('Error fetching comments:', error);
});
}, [id]);
const handleDeleteBook = async () => {
try {
await axiosInstance.delete(`http://127.0.0.1:8000/api/books/${id}/`);
// 삭제 성공 시, 메인 페이지로 이동
navigate('/main');
} catch (error) {
console.error('Error deleting book:', error);
}
};
...
return (
...
{bookData.user_id === currentUserId && ( // 현재 사용자와 책 작성자 비교
<button
style={buttonStyle}
onClick={handleDeleteBook}
>
Delete Book
</button>
)}
...
</div>
);
};
export default BookDetail;
comment 부분도 같이 수정해준다.
import useAuthStore from '../../shared/store/AuthStore'; // useAuthStore 불러오기
const BookComment = ({ bookId, comments, setComments, currentTheme }) => {
...
const { userId: currentUserId } = useAuthStore(); // 현재 사용자 ID 가져오기
...
return (
<div className="comment-box" style={{ color: currentTheme.textColor }}>
<h2>Comment Box</h2>
<div className="comments">
{comments.slice(0, visibleComments).map((comment) => (
<div className="comment" key={comment.id}>
<div className="comment-avatar" style={{ backgroundColor: currentTheme.buttonBackgroundColor, color: currentTheme.buttonTextColor }}>
{comment.user_nickname.charAt(0).toUpperCase()}
</div>
<div className="comment-content">
<p>{comment.content}</p>
<p style={{ color: currentTheme.sidebarBg }}>{comment.user_nickname}
<small> | {new Date(comment.created_at).toLocaleDateString('en-US', { year: 'numeric', month: '2-digit', day: '2-digit' })}</small>
</p>
<div>
{comment.user_id === currentUserId && ( // 현재 사용자와 댓글 작성자 비교
<>
<button style={buttonStyle} onClick={() => { setEditingCommentId(comment.id); setUpdatedContent(comment.content); }}>Edit</button>
<button style={buttonStyle} onClick={() => handleDeleteComment(comment.id)}>Delete</button>
{editingCommentId === comment.id && (
<div>
<textarea value={updatedContent}
onChange={(e) => setUpdatedContent(e.target.value)}></textarea>
<button style={buttonStyle} onClick={() => handleEditComment(comment.id, updatedContent)}>Save</button>
<button style={buttonStyle} onClick={() => setEditingCommentId(null)}>Cancel</button>
</div>
)}
</>
)}
</div>
</div>
</div>
))}
...
);
};
export default BookComment;