2월 20, 2022

[Django] CRUD assignment1 review

 우선, 과제의 요구사항이 이러했는데, 제가 아주 큰 실수를 했습니다


저는 master브랜치에서 feature/owner 브랜치를 만든 이후, 필수 과제를 모두 진행한 이후, feature/owner 브랜치에서 바로 check out -b 명령어로 feature/movie 브랜치로 이동했습니다. 

이렇게 하게 되니, feature/movie 브랜치에는 owners app 과 movies app이 공존하게 되는 상황이 벌어졌습니다. 결국 해당 브랜치를 삭제하고, master 브랜치로 이동했음에도 


이런식으로 pycache파일이 여전히 남아있는 문제가 발생했습니다. remote에서 pull을 받아와도 문제는 해결되지 않았습니다. 결국은 새로운 디렉토리를 파서 거기서 remote의 master 브랜치를 pull받은 이후, 새로운 feature/movie 브랜치를 생성하는 식으로 과제를 진행했습니다.

branch를 만들때는 무조건 master(main)브랜치로 이동하여 브랜치를 만들기!! 라는 큰 교훈을 얻었습니다...


실수한 점 2. 



이 요구사항을 보고, 주인의 - post / 주인의 - get / 강아지의 - post / 강아지의 - get 

이렇게 클래스를 총 네개로 나눠서 만들 생각을 했습니다.

그러나 views의 각 클래스는, 어떠한 자원(데이터)를 다룰지 결정하는 것입니다. 

예를 들어, owner클래스가 있다면, owner클래스는 주인에 대한 데이터를 클라이언트에게 줄건지(GET), 클라이언트가 준 데이터를 담을건지(POST)를 결정하는 것입니다.

담거나, 주거나 하는 각각의 기능들은 함수로 이뤄지고, 함수의 이름들은 http요청들의 이름과 똑같아야 합니다. 이는 urls.py에서 as_view() 함수를 사용하는 경우, as_view()가 http method와 , class안의 메소드의 이름을 비교해서, 똑같은 것을 실행할 수 있도록 해주기 때문입니다.

정리하자면, 주인에 대한 데이터를 다루는 OwnersView 클래스와 / 강아지에 대한 데이터를 다루는 DogsView 클래스를 나눠준 뒤, 각각의 클래스 함수에 get과 post함수를 구현해주면 되는 것입니다.


OwnersView클래스의 get함수를 구현하면서 겪은 시행착오들입니다.

주인 한명당, 하나의 age/email/name만 나오고, 강아지의 이름과 - 나이만 리스트로 나오게 하고 싶었습니다.



첫번째 시도입니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def get(self, request):
        owners = Owners.objects.all()
        results = []
        dog_dictionary = {}
        # dogs = []
        for owner in owners:
            results.append(
                    {
                        "name" : owner.name,
                        "email" : owner.email,
                        "age"   : owner.age,
                    }
            )        
            for owner_dog in owner.dogs_set.all():
                dog_dictionary["name"] = owner_dog.name,
                dog_dictionary["age"]   = owner_dog.age,
            results.append(dog_dictionary)
        return JsonResponse({'results':results}, status=200)

{
    "results": [
        {
            "age": 25,
            "email": "1park4170@gmail.com",
            "name": "박영서"
        },
        {
            "age": [
                11
            ],
            "name": [
                "김치"
            ]
        },
        {
            "age": 35,
            "email": "mutd@gmail.com",
            "name": "이승준"
        },
        {
            "age": [
                11
            ],
            "name": [
                "김치"
            ]
        }
    ]
}

return 이후는 요청을 보내본 결과입니다.

데이터 상 "이승준"은 가지고 있는 강아지가 없음에도 "박영서"의 강아지와 함께 나오고, 강아지의 나이와 이름도 불필요하게 리스트에 담겨져 나옵니다. 심지어 "박영서"가 가진 강아지는 세마리인데, 마지막 한마리만 나오고 있습니다.

우선 마지막 한마리만 나오고 있는 이유는, for문이 돌면서 dog_dictionary의 키 값에 해당하는 value값을 계속 바꿔주기 때문입니다. 값이 누적돼서 쌓이는 것이 아니라, for문이 돌때마다 값이 바뀌게 되는 것이죠.

왜 강아지가 없는 승준이도 강아지를 갖게 된 것일까요?

자세히 보니 두번째로 나온 11살의 김치란 강아지는 승준이의 강아지로 나온것이 아닙니다. 

dog_dictionary는 for문의 바깥에 이미 선언돼 있는 딕셔너리입니다. 첫번째 주인인 "박영서"의 강아지 세명 중 마지막 아이의 값이 반영된 이후, dog_dictionary의 값은 그대로 변함없이 존재합니다. 왜냐면, 승준이가 owner인 상황에서, owner_dog라는 것은 존재하지 않기에, owner_dog.name이라는 값이 들어갈 수 없는 것이죠. .name 속성을 지닌 객체 자체가 없기에 값이 들어갈 수가 없습니다.

 따라서 이전의 "박영서"owner에서 만들어졌던 dog_dictionary는 그대로 존재하다가 results.append(dog_dictionary)코드에 의해 한번 더 results리스트 안에 append 되게 됩니다.


왜 dog_dictionary의 age와 name 키의 값은 리스트에 담겨서 나올까요?

....이에 대해서는 멘토님께 여쭤봤는데도 명확한 답이 나오지 않았습니다. 추가적으로 고민하고 서치해보고 업데이트 하겠습니다




다음 시도한 get함수의 코드와 결과는 다음과 같습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
def get(self, request):
        owners = Owners.objects.all()
        results = []
        #dog_dictionary = {}
        dogs = []
        for owner in owners:
            results.append(
                    {
                        "name" : owner.name,
                        "email" : owner.email,
                        "age"   : owner.age,
                    }
            )        
            for owner_dog in owner.dogs_set.all():
                dog_attribute = []
                dog_attribute.append(owner_dog.name)
                dog_attribute.append(owner_dog.age)
                dogs.append(dog_attribute)
            results.append(dogs)

        return JsonResponse({'results':results}, status=200)


{
    "results": [
        {
            "age": 25,
            "email": "1park4170@gmail.com",
            "name": "박영서"
        },
        [
            [
                "마파",
                9
            ],
            [
                "두부",
                10
            ],
            [
                "김치",
                11
            ]
        ],
        {
            "age": 35,
            "email": "mutd@gmail.com",
            "name": "호날두"
        },
        [
            [
                "마파",
                9
            ],
            [
                "두부",
                10
            ],
            [
                "김치",
                11
            ]
        ]
    ]
}

이번에도 강아지가 없는 주인에게 강아지가 나옵니다.

이번에도 강아지가 없는 주인에게 강아지가 나온 것이 아니라, 강아지가 있던 전 for문의 주인에서 만들어졌던 dogs 리스트가, 강아지가 없는 주인이 for문을 도는  경우에 수정되고 있지 않다가, results.append(dogs) 구문으로 인해 results리스트에 또 더해져서 그렇습니다


3번째 시도입니다

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def get(self, request):
        owners = Owners.objects.all()
        results = {}
        #dog_dictionary = {}
        try:
            for owner in owners:
                results["name"] = owner.name
                results["email"] = owner.email
                results['age']    = owner.age  
                for owner_dog in owner.dogs_set.all():
                    results["dog"]=(owner_dog.name, owner_dog.age)

            return JsonResponse({'results':results}, status=200)
        except KeyError:
            for owner in owners:
                results["name"] = owner.name
                results["email"] = owner.email
                results['age']    = owner.age  
            return JsonResponse({'results':results}, status=200)

결과
{
    "results": {
        "age": 35,
        "dog": [
            "김치",
            11
        ],
        "email": "mutd@gmail.com",
        "name": "이승준"
    }
}

이번에는 강아지가 있는 영서는 결과에 나오지도 않고, 강아지가 없는 승준이는 강아지 정보와 함께 출력됩니다. 게다가 첫번째 시도와 마찬가지로 강아지의 정보가 리스트에 담겨서 나옵니다.

일단, 영서가 나오지 않고 승준이만 나오는 것과, 마지막 강아지인 김치만 출력되는 것은 1번의 문제와 같은 원인입니다. results 라는 딕셔너리는 for 문 바깥에서 별도로 존재합니다. 

이 별도로 존재하는 딕셔너리의 'name' , 'email', 'age' 값에 각 주인들의 값이 들어왔다가, 교체되는 것이죠. 'dog'의 경우도 강아지가 있는 첫번째 주인이 for문을 돌 때 키값에 저장된 값이 그대로 남아있다가 return되는 것입니다.

except KeyError의 경우도 적용될 일이 없는 코드입니다. 왜냐하면 키에러는 딕셔너리의 키 값이 없어서 생기는 에러인데, 해당 코드의 강아지를 가져오는 for 문에서는 "dog"키값에 대한 값이 이미 존재하고, 강아지가 없는 주인이 for문을 돌 경우 값이 그대로 남아있기 때문입니다.