장고 첫번째 팀프로젝트 :: 게시판 CRUD관련 코드 리뷰 - 게시판 생성 삭제 및 편집

권수민·2023년 9월 19일
1

임포트 할 모듈

from django.http import HttpResponse, HttpResponseNotAllowed
from django.shortcuts import redirect, render, get_object_or_404
from django.contrib.auth.decorators import login_required
from post.models import Post, Comments, PostLike, CommentLike
from django.conf import settings  # media url
from .forms import PostForm, ImageForm, FileForm, CommentForm
from django.contrib import messages
from django.urls import reverse

인덱스함수 :

def index(request):
    posts = Post.objects.all().order_by("-created_at")
    #꺼꾸로 최신글 부터 위로 오게 설정
    if request.method == "GET":
        return render(
            request,
            "post/index.html",
            {"posts": posts, "MEDIA_URL": settings.MEDIA_URL},
        )
    elif request.method == "POST":
        pass
    else:
        return HttpResponse("Invalid request method", status=405)

게시물 생성 함수 :

만약 한개의 이미지만 가져오고 싶었을때와 폼 없이 저장하고 싶었을때:

@login_required(login_url="/users/login/")
# 즉, 로그인 되어있지 않을시 로그인 페이지로 옮겨
#if request.user.is_authenticated:해준 모든것을 알아서 처리해준다.

def create(request):
     #if request.user.is_authenticated:
        if request.method == "POST":
            title = request.POST['title']
            author =request.user
            content =request.POST['content']
            
            #한개의 이미지
            image =request.FILES.get('image')
            file = request.FILES.get('file')

            Post.objects.create(
                title = title,
                author =author,
                content=content,
                image = image,
                file = file,
                )
                
            #여러개의 이미지 : 폼을 안쓰고 
            #1번쨰 방식
            for img_file in request.FILES.getlist('images'):
                Image.objects.create(post=post, image=img_file)

            for file in request.FILES.getlist('files'):
                File.objects.create(post=post, file=file)
            
            #2번쨰 방식
            # for f in file:
            #     file_instance = File(file=f) # 클라스 인스턴스 생성
            #     file_instance.save()
            
            # for img in image:
            #     img_instance = Image(image=img)
            #     img_instance.save()
            
            
            return redirect('/post/')
        elif request.method =="GET":
            return render(request,'post/create.html')
        else:
            return HttpResponse("Invalid request method" , status=405)
  #  else:  # if not request.user.is_authenticated:
    # 로그인전 새로만들기 눌렀을때 로그인페이지로 next값 저장해주고 넘어가게해주기위해 만든것
    # login_url = f"/user/login/?next={request.path}"
    # return redirect(login_url)

form은 모델과 연동을 통해 여러 파일,사진등을 자동적으로 데이터의 생성, 수정, 유효성 검사, 에러 메시지 표시 등에 관련 기능이 풍부하기 떄문에 사용.

이번 프로젝트 에서는 더 templates와 연결할때 사용할 수 있는 편리한 FORM기능을 사용하지는 않음. 왜냐면 따로 <input ~~ multiple>같은 옵션을 걸어줘야할때는 사용할 수 없기 때문에.

허나 유효성검사 및 데이터 생성 수정 연결....해줌!


@login_required(login_url="/users/login/")
def create(request):
    # if request.user.is_authenticated:
        if request.method == "POST":
            post_form = PostForm(request.POST, request.FILES)  # 폼 인스턴스 생성
            if post_form.is_valid(): # 유효성 검사
                post = post_form.save(commit=False) 
                #commit=False해줄 시 바로 디비에 저장되지않고 폼만 형성되어 대기.
            	#해주는 이유는 templates에서 받지 못한 값을 따로 저장해 주기 위해서 
                post.author = request.user
                post.save()

                for img_file in request.FILES.getlist("image"):
                #getlist를 통해서 request된 FILES의 모든 이미지를 받아 오는것
                #for문으로 하나씩 돌려
                    image_form = ImageForm({"post": post.id}, {"image": img_file}) #정확히 어떤 위치의 포스트에 어떤 파일을 지정해주는것.
                    if image_form.is_valid():
                        image = image_form.save(commit=False)
                        image.post = post
                        image.save()
                    else:
                        #print(image_form.errors) #디버깅
                        #이렇게 에러메세지를 터미널에 볼수 있어 FORM에러메세지 기능
                        image_form = ImageForm()
                       # 처음 방문할 때 폼 객체가 존재하지 않기 때문에 생기는 오류를 방지
                       #템플릿에서 {{ form.as_p }} 등의 폼 렌더링 메서드를 사용할 경우 오류가 발생할 수 있기때문에 => 애초에 사용하지않았지만 해준다.
            # context =  {'post_form': post_form, 'image_form' : image_form , 'file_form':file_form}

                        return render(
                            request, "post/create.html", {"image_form": image_form}
                        )

                for file in request.FILES.getlist("file"):
                    file_form = FileForm({"post": post.id}, {"file": file})
                    if file_form.is_valid():
                        file = file_form.save(commit=False)
                        file.post = post
                        file.save()
                    else:
                        #print(f"file{file_form.errors}")
                        file_form = FileForm()
                        return render(request, "post/create.html", {"file_form": file_form})
                return redirect("/post/")
            else:
                print(post_form.errors)
                post_form = PostForm()
                return render(request, "post/create.html", {"post_form": post_form})

        elif request.method == "GET":
            posts = Post.objects.all()
            return render(request, "post/create.html", {"posts": posts})
        else:
            return HttpResponseNotAllowed(["GET", "POST"])
    #else:
     #로그인전 새로만들기 눌렀을때 로그인페이지로 next값 저장해주고 넘어가게해주기위해 만든것
    	#login_url = f"/user/login/?next={request.path}"
    	#return redirect(login_url)
        
        

위에는 폼을 사용해주었고 참고로 폼셋도사용해봤지만 TEMPLATES와 연결해주는것에 에러가 자꾸 나서 폼으로 사용해 주었다.

폼셋을 예를 들어주면:

ImageFormSet = modelformset_factory(
Image, form=ImageForm, extra=0) # extra : 추가적인 빈폼 갯수
form = ImageFormSet(request.POST, request.FILES, queryset=post.images.all())

# post.images.all()는 특정 post와 연결된 모든 Image 객체를 반환합니다. 따라서 이 폼셋은 해당 post와 관련된 이미지만을 표시하고 편집
# queryset는 폼셋의 초기 상태를 정확하게 제어하기 위해 사용되며, 이를 통해 사용자가 특정 데이터만을 볼 수 있도록 하거나 특정 데이터에 대한 작업만을 수행하도록 제한.
# 안해주면 데이터베이스에 있는 모든 이미지에 대한 폼이 표시

폼으로 여러개 파일이미지 업뎃 및 그 이전 파이 불러와 삭제 해주는 기능:

여기서 삭제기능은 폼을 설정할때 boolean타입으로 IMAGE 및 file의 위젯을 변경해주었다.

@login_required(login_url="/users/login/")
def update(request, post_id):

    post = get_object_or_404(Post, pk=post_id)
    
    current_images = post.images.all() #저장된 이미지를 모두 불러와.
    current_files = post.files.all()
    
    post_form = PostForm(request.POST, instance=post) 
    #instance=post는 이전의 저장된 포스트를 불러오는것.
    #즉, instance 인자를 불러오지않으면 새로운 객체를 생성하느것이고, INSTANCE를 부를시 그 객체를 불러와 수정한다는 의미이다.
    
    # 로그인한자와 글쓴이가 같은지 
    if request.user != post.author:
        messages.error(request, "삭제권한이 없습니다")
        return redirect("post:detail", post_id=post.id)
    if request.method == "POST":
        # 이미지 수정 및 삭제
        if post_form.is_valid():
            post = post_form.save(commit=False)
            post.author = request.user
            post.save()
           # print(post_form.instance.id) 디버깅 체크
        else:
            post_form = PostForm(instance=post)
            # 
            return redirect("post:home")

		# 기존의 저장된 파일 들을 삭제할것인지 
        for img in current_images:  
            image_form = ImageForm(
                request.POST, request.FILES or None, instance=img, prefix=str(img.id)
                
                #request.FILES or None을 해주는 이유는 만약 아무런 데이터가 없어 즉, 이미지를업로드 시키지 아니아하였을때 .is_valid()유효성 체크시 None => false가 나올수 있기때문에 넣어준다.
            )
            if image_form.is_valid():
                if f"delete_{img.id}" in request.POST:  
                # template에서 전해주는 name을 확인해서 넘겨준다.
                #<label>삭제하기: <input type="checkbox" name="delete_{{ form.instance.id }}" value="{{ form.instance.id }}" /></label>
                #위의 체크박스를 넣어주고 그걸 연결시켜 값을 보내주는것.
                
                    img.delete()
            else:
                image_form = ImageForm(instance=img)
                return HttpResponse("Invalid image", status=405)

        for file in current_files:
            file_form = FileForm(
                request.POST, request.FILES or None, instance=file, prefix=str(file.id)
            )
            if file_form.is_valid():
                if f"delete_{file.id}" in request.POST:
                    file.delete()
            else:
                file_form = FileForm(instance=file)
                return HttpResponse("Invalid file", status=405)
	
    
    
    	#새롭게 추가하여 더해줄 파일들
        for img_file in request.FILES.getlist("image"):
            new_image_form = ImageForm({"post": post.id}, {"image": img_file})
            if new_image_form.is_valid():
                print("imageform ")
                image = new_image_form.save(commit=False)
                image.post = post
                image.save()
            else:
                new_image_form = ImageForm()
                return HttpResponse("Invalid image", status=405)

        for f_file in request.FILES.getlist("file"):
            new_file_form = FileForm({"post": post.id}, {"file": f_file})
            print("fileform ")
            if new_file_form.is_valid():
                file = new_file_form.save(commit=False)
                file.post = post
                file.save()
            else:
                new_file_form = FileForm()
                return HttpResponse("Invalid file", status=405)

        return redirect("post:detail", post_id=post.id)

	# 위에 다 수정을 해줫으면 새로히 수정된 내용 저장[] 해주고 
    # 이름에 의해 일어날 충돌을 막기 위해서 앞에 이름을 덧 붙혀준것 => prefix=str(~.id)
    # 다 이미지 폼으로 생성되면서 충돌이 생길까봐.
    
    image_forms = [
        ImageForm(prefix=str(img.id), instance=img) for img in current_images
    ]  
    
    file_form = [FileForm(prefix=str(file.id), instance=file) for file in current_files]

    return render(
        request,
        "post/update.html",
        {
            "post": post,
            # 'post_from' : post_form,
            "image_forms": image_forms,
            "file_form": file_form,
        },
    )

게시물을 삭제하는 메소드:

@login_required(login_url="users:login")
def delete(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    if request.user != post.author: 
        messages.error(request, "삭제권한이 없습니다")
        return redirect("post:detail", post_id=post.id)
    else:
        post.delete()
        return redirect("post:home")


profile
초보개발자

0개의 댓글