2023년 4월 13일 TIL (#장고소개_팀프로젝트_day4)



오늘의 공부 계획

_django 프로젝트 주석 읽기 기능 구현


녹음하고 싶은 것

_read 함수를 구현하는 과정에서 사용자로부터 받아 DB에 저장한 사진을 다시 html로 보여주려고 노력했습니다.

DB에 저장해두었던 img가 경로에 불과하다는 사실을 깨달았습니다.

이미지를 돌려주려면 실제로 이미지가 입력되었을 때 가지고 있어야 하는데 그러려면,,,

CRUD에서 만들기부터 시작하겠습니다.

#이용자가 보는 comment_create.html에서

<form method="post" enctype="multipart/form-data">
    <label for="comment_image">이미지 파일 추가</label> <!-- 자세한 코드는 DB 가용성 보고 수정 -->
    <br>
    <input type="file" id="comment_image" name="comment_image" onchange="setThumbnail(event);">
	<br>
    <button type="submit">등록</button>
</form>

이렇게 하면 이미지 파일을 얻을 수 있습니다. 여기서 포인트는

이는 양식 태그에 enctype이 속성으로 포함되어 있음을 의미합니다.

html에서 백엔드로 사용자 입력 데이터를 보내려면 일반적으로 양식으로 래핑하기만 하면 됩니다.

래핑된 데이터에 텍스트 파일이 아닌 바이너리 파일(이미지 또는 동영상)이 포함되어 있는 경우,

enctype=”multipart/form-data” 속성을 추가해야 합니다.

**enctype은 인코딩 유형의 약자입니다.

이렇게 받은 데이터는 경로를 통해 들어갑니다.

POST 방식으로 views.py에서 아래 기능을 터치하면,

@login_required    #위에서 임포트한 로그인 판별기 데코레이터
def comment_create(request):  # 작성하기 버튼 클릭 시 모든 인풋값을 받아서 comments DB에 저장하는 함수
    if request.method == 'GET':  # GET 메소드로 들어오면 댓글 작성용 화면을 보여줌
        return render(request, 'comments/comment_create.html')
    if request.method == 'POST':  # POST 메소드로 들어오면 작성한 인풋값들을 DB에 전송
        user = request.user  # 지금 계정에 로그인된 사용자 이름 가져옴
        my_comment = CommentModel()  # 댓글 모델클래스 이리와
        my_comment.comment_writer = user  # UserModel의 id값을 ForeignKey로 참조하여 로그인된 사용자에 할당

        # html에서 받은 각각 인풋값들을 DB에 넣기 위해 변수 선언
        my_comment.comment_content = request.POST.get('comment_content', '')  # 사용자가 입력한 댓글내용
        my_comment.comment_image = request.FILES.get('comment_image')  # 사용자가 업로드한 이미지파일

        my_comment.save()    #입력한 값들을 DB에 저장하는 중요한 명령어
        return redirect('/api/comments')    #저장하고 나면 댓글 보는 화면으로 보낸다.

잔잔한 땅은 물이 진동해도 반으로 갈라지지 않습니다.

데이터는 DB에 잘 저장됩니다.

아래는 데이터 저장소 유형을 정의하는 model.py입니다.

class CommentModel(models.Model):
    class Meta:
        db_table = "my_comment"

    comment_writer = models.ForeignKey(UserModel, on_delete=models.CASCADE) #UserModel의 id값을 참조해서 comment_writer에 담음.
    comment_created_at = models.DateTimeField(auto_now_add=True)  # 이게 이미 날짜 찍는거
    comment_content = models.TextField(max_length=200) #개행을 할 수 있도록 TextField로 한다. CharField가 아니라.
    comment_image = models.ImageField(upload_to='images/') #DB이미지필드에 이미지이름을 넣으며 동시에 이미지 실물은 media의 하위폴더 images/ 경로로 저장.

그리고 이렇게 하면 해당 이미지 파일의 이름만 DB에 저장되고 끝납니다.

실물을 받아서 보유하고 사용자가 보는 화면에 표시할 수 있으므로 별도의 디렉토리를 생성

사용자가 입력한 이미지를 저장할 공간을 준비해야 합니다. 원래 파일은 EngineX와 같은 웹 서버에서 전달되어야 하지만 django에서 직접 전달되지는 않습니다. 프로세스 기반 언어 Python을 사용하는 django는 요청을 한 번에 하나씩 처리하기 때문에 누군가가 10개 이상의 동영상이나 이미지를 업로드하는 등 로드가 많은 요청을 하면 다른 사람에 대한 응답이 지연됩니다.

먼저 기본 이미지 파일을 저장하기 위해 manage.py와 동일한 계층 구조에 static이라는 폴더를 생성하고,

그 아래에 admin 폴더를 만들고,

그 아래에 img 폴더를 만들고,

그런 다음 설정으로 이동

STATIC_URL = 'static/'
# STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),)
STATICFILES_DIRS=(BASE_DIR / 'static')



MEDIA_ROOT = BASE_DIR / 'media'
MEDIA_URL = '/media/'

다음과 같이 정적 및 미디어 폴더에 대한 링크를 만드십시오.

앱 폴더에서

urls.py로 이동

from django.conf.urls.static import static #urlpatterns += 전용
from django.conf import settings #urlpatterns += 전용
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) #장고한테 이미지 루트를 알려줌

Yoroka는 Django에게 이미지를 가져올 위치를 알려줍니다.

이렇게 세이브가 잘 되었다면 이제 읽기편~

views.py에서 api/comments 요청이 오면 아래 기능을 터치합니다.

def comment_read(request):  # comments DB 댓글들을 모두 불러와서 html에 표시하는 함수. 표시된 댓글들 중 나의 댓글에는 수정하기와 삭제하기 버튼이 보인다.
    all_comment = CommentModel.objects.all().order_by("-comment_created_at") #커멘트모델의 모든 오브젝트를 불러오는데 오더바이 안의 순서로 불러와라. 앞에 - 썼으니 역순이라 최신글 상단
    return render(request, "comments/comment_read.html", {"all_comment": all_comment}) #렌더를 해오되, 왼쪽all_comment 오른쪽all_comment 값을 담아서 html에 보내주는 것.

준비된 템플릿이 렌더링되고 html 화면이 촬영됩니다. 그 때 저장한 데이터를 .object.all() 로 호출하고 템플릿을 전달하는 응답에 모두 함께 불러옵니다.

{% load static %} #스태틱폴더를 참조한다는 장고명령어
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>챌린지 댓글을 남겨주세요!</h1>
<div style="background-color: gray; width: 70%; height: 50%; padding: 10px;">
    <a href="http://iamvinethankyou-coding.api/comments/create">나도 작성하기!</a>
    {% if all_comment %} #조건문을 걸기 시작한다. views.py에서 렌더 해줄때 담아 보낸 DB데이터가 all_comment
        {% for comment in all_comment %} #데이터를 돌려서
            <div style="background-color: white; width: 100%; margin-top: 10px;">
                <p style="display: flex; flex-direction: row; justify-content: space-between; padding: 10px">
                    <span>{{ comment.comment_writer }}</span>   #이런식으로 데이터를 돌리면서 원하는 데이터를 추출해서 포맷팅.
                    <span>{{ comment.comment_created_at }}</span> # {{ for문 인덱스.원하는데이터 }}
                </p>
                <p style="display: flex; flex-direction: row; padding: 10px; justify-content: space-around;">
                    {% if comment.comment_image %}
                        <img src="{{ comment.comment_image.url }}" alt="comment_image" #이미지의 경우엔 뒤에 .url을 붙여줌. 이미지 이름만 나오면 안되고 이미지 이름이 곧 이미지의 url이라고 알려줘야함.
                             style="height : 130px ; margin-left:20px">
                    {% else %}
                        <img src="{% static 'admin/img/defaultimg.png' %}" # DB에서 이미지가 잘 꺼내진다면 if문처럼 보여주지만 잘 안꺼내진다면 static/admin/img에 넣어 놓은 디폴트 이미지를 출력한다.
                             alt="default Image" style="height : 130px; margin-left:20px">
                    {% endif %}
                    <span>{{ comment.comment_content }}</span>
                </p>
            </div>
        {% endfor %}
    {% endif %}
</div>
</body>
</html>

우선 CSS는 인라인으로 처리합니다 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 이미지가 정적으로 왔다 갔다 해서 뭔가 잘못될 것 같은 느낌이 싫어서 작업이 끝나면 CSS 파일로 분리해 둘 생각입니다.

이렇게 화면에 이미지를 띄우면!


하하하 실행하고 나서 너무 기뻤어요.

앱 폴더의 urls.py에서 django에게 pull할 이미지 파일의 경로를 알려주는 작업은

최고의 포인트였습니다.

포인트를 주신 창의과외선생님께 무한한 감사를…

이제 해야 할 CSS 작업이 남아 있습니다.