Django View.py 를 작성할 때 유의점...

김루트·2021년 4월 4일
0

데이터베이스

목록 보기
4/4

해당 테이블을 이용해서, Django models.py와 views.py를 작성하는 도중에, 부딪혔던 문제 그리고 해결하지 못한 문제에 대해서 간단히 포스팅 해본다.

단지 두개의 테이블이 일대다 관계로 연결되어 있기 때문에 모델 자체는 간단한다.

from django.db import models



class Owner(models.Model):
    name = models.CharField(max_length=45)
    email = models.CharField(max_length=300)
    age = models.IntegerField()

    class Meta:
        db_table = "owners"


class Dog(models.Model):
    owner = models.ForeignKey('Owner', on_delete=models.CASCADE)
    name = models.CharField(max_length=45)
    age = models.IntegerField()

    class Meta:
        db_table = "dogs"

문제는 views.py를 작성할 때 발생한다. 아직 프론트엔드 측에서 받아오는 데이터가 없기 때문에 httpie를 이용해서 직접 post, get 매서드를 사용해서 요청을 보내는 방식으로 진행했다. 당연히 에러가 발생했는데, 404에러와 500에러를 계속해서 마주했다.

1. 404 에러

쉽게 말하면, "없는 페이지를 요청하기 때문에" 이런 에러가 발생한다.

장고 프로젝트 폴더를 보면 두 개의 urls.py 파일을 확인할 수 있다. 이를 은행에 비유하자면, 프로젝트명과 같은 이름의 디렉토리 안에 있는 urls.py는 은행이 위치한 건물의 주소를, 프로젝트 안의 앱 디렉토리에 있는 urls.py는 은행에서 각각의 창구를 의미한다고 볼 수 있다. 만약 내가 http -v post 127.0.0.1:8000/app 이름/view 이름과 같은 형식으로 요청을 보낸다면, 상위 디렉토리의 urls.py는 (여기서는) assignments라는 앱의 urls.py로 내 요청을 넘길 것이고, 해당 앱 안의 urls.py는 내 요청에 알맞는 view로써 요청을 실행할 것이다.

내게 404 에러가 발생했던 이유는 1) 프로젝트의 urls.py의 path('앱이름', include('앱이름.urls'))에서 설정한 앱이름과는 다른 요청을 보냈고 2) 앱의 urls.py에서 path('view 이름',) 을 설정할 때 "/" 슬래시 표시를 넣지 않았기 때문이었다.

가령 내가 http를 통해 요청을 보낼 때는 http -v get 127.0.0.1:8000/assignments/dogs와 같은 형식으로 슬래시로 앱과 뷰를 구분하여 요청을 보냈는데, 해당 urls.py에서는 슬래시를 명기하지 않았기 때문에 에러가 날 수 밖에 없었다. 원래는 장고에서 알아서 슬래시를 붙여준다는데, 왜 나는 에러가 발생했는지는 잘 모르겠다...

2. 500 에러

무언가 내 로직 (view)이나 모델에 문제가 있음을 의미한다. 맞춤법과 들여쓰기(indent) 문제인 경우가 대부분이었으며 변수명과 view 이름을 통일성 있게 지어야하는 이유를 절감할 수 있었다.

● 모델명: 대문자 단수 (예를 들면 Dog, Owner)
● class Meta: MySQL 데이터베이스에서 보여질 테이블의 명칭 설정하기. 소문자 복수형. (예를 들면, dogs, owners)
● view명: 마찬가지로 대문자 단수 (예를 들면 DogView, OwnerView)

이외에도 처음부터 app 이름을 비직관적으로 지은 탓에 오타로 인한 오류가 발생하기도 했고, 매서드에서 역참조를 잘못 이해해서 로직에서 문제가 발생한 경우도 있었다.

def get(self, request):
    # data = json.loads(request.body)

    result = []
    owners = Owner.objects.all()

    for owner in owners:
        dog_list = []
        dogs = owner.dog_set.all()
        # 이 부분에서 all()매서드를 사용할 때 문제가 발생. 왜지...
        # 이유 찾음: 역참조시에는 클래스명(Dog, Owner)을 소문자로 썼어야...
        # 다른 이유: owner.objects.dog_set이 아니었다...

        for dog in dogs:

            dog_information = {
                "name": dog.name,
                "age": dog.age}

            dog_list.append(dog_information)
        # dog에 관한 정보를 for문을 두번 사용하여, 추가할 수 있음. 이 때, 역참조에 주의할 것.
        owner_information = {
            'name': owner.name,
            'email': owner.email,
            'age': owner.age,
            'dog': dog_list

        }

        result.append(owner_information)

    return JsonResponse({'result': result}, status=201)

특히 for문을 중첩하여, owner에 대한 정보에 dog 관련 정보를 추가할 때, Queryset의 역참조 매서드 문법을 잘못 이해한 탓에 계속 오류가 발생했다.

dogs = owner.dog_set.all()

Dog에서 Owner를 역참조 하는 이유는, Dog는 owner_id를 통해 Owner에게 접근할 수 있지만 반대의 경우는 불가하기 때문이다.

이때 일반적인 참조에서는 가령, Dog.objects.get(id=1) 등과 같이 objects 다음에 매서드를 명시하고 괄호안에 필요한 조건을 적는 것이지만 역참조에서는 objects를 쓰지 않는다. n:1 테이블에서 n에서 1을 바라보는 것이 정방향 참조라면, 1에서 n을 바라보는 것은 역참조이다. 역참조에서는 1에 해당하는 클래스. n에 해당하는 클래스_set 와 같은 형식으로 나타내야한다. 여기서 objects를 빼지않고 계속 놔뒀기 때문에 500에러가 발생하였다.

※ 역참조시에는 클래스명을 소문자 단수형으로 표기한다.

def post(self, request):
    data = json.loads(request.body)
    owner = Owner.objects.get(id=data['owner_id'])

마지막으로, post 매서드를 쓸 때는 json형식으로 내가 직접 데이터를 적어서 요청을 하면, 그 형식에 맞게 view가 작동한느 방식이었기 때문에, data=json.loads(request.body)를 명시해주어야만 했다 (이를테면 http -v post 127.0.0.1:8000/assignments/owners name="kim" age=23 email="abc@def.com"과 같은 형식으로 요청을 보낸다).

그런데, get 매서드에서는 json 형식으로 데이터를 추가하겠다는 요청을 하는 것이 아니라, "내 DB에서 모든 데이터를 나의 view 로직에 따라서 뽑아오겠다"라는 뜻이기 때문에 post 매서드에서는 명시해주어야 했던 json 형식은 사족이다. 따라서 여기서도 500에러가 발생했으며 이를 삭제한 이후, 정상적으로 요청과 응답이 이루어짐을 확인할 수 있었다.

profile
반갑습니다.

0개의 댓글

관련 채용 정보