Dev/Django

Django 이해하기 Part3. 많이 사용하는 파일(views.py)

sincerely10 2020. 7. 12. 21:34
반응형

바로 이어서 views.py에 대해서 작성하겠습니다.

3. Views.py

마지막으로 views.py 입니다. 사실상 가장 많이 사용하는 파일로서 너무나도 중요합니다. 활용도도 다양하기 때문에 저는 기초적 사용만 다루고 역할을 정의 하겠습니다.

views 파일은 앞에서 보았던 url과 models의 개념도 다시 사용됩니다. urls.py에서 url Pattern에 따라 이 view 파일의 함수를 불러오고, models.py에서 정의 했던 class들이 import 되어 이 데이터를 다룹니다.
그리고 이 컨트롤 된 데이터를 통해 JsonResponse 또는 HttpResponse 형태를 return 해줍니다.

개인적으로 생각하기에 views.py에서 가장 많이 익숙해지고 다뤄봐야할 내용은 Models에서도 언급한 Django QuerySet(장고 쿼리셋) 입니다. 익숙한 DB SQL문이 아닌 Django QuerySet을 통해 데이터를 다뤄야 넘겨 받은 request를 효과적으로 다루고 적절한 Response를 할 수 있기 때문입니다.

제가 작성한 예제코드를 통해서 간단한 몇 가지 예시를 살펴 보겠습니다.

# users/views.py
import json
from django.http    import JsonResponse
from django.views   import View
from users.models   import User,Following

class SignUpView(View):
    def post(self,request):
        data = json.loads(request.body)

        try:
            if data['name'] and data['email'] and data['password']:

                if '@' not in data['email']:
                    return JsonResponse({'message':'Invalid email format'},status=401)

                if User.objects.filter(email=data['email']).exists():
                    return JsonResponse({'message':'Already registered User_ID'},status=401)

                if len(data['password']) < 5:
                    return JsonResponse({'message':'Password must be at least 5 chracters.'},status=401)

                User(
                    name        = data['name'],
                    email       = data['email'],
                    password    = data['password']
                ).save()
                return JsonResponse({'message':'Register Success'},status=200)

        except KeyError:
            return JsonResponse({'message': 'KEY_ERROR'},status=400)

class SignInView(View):
    def post(self,request):
        data = json.loads(request.body)

        if User.objects.filter(email=data['email']).exists():
            login_user = User.objects.get(email=data['email'])

            if login_user.password == data['password']:
                return JsonResponse({'message':'SUCCESS'},status=200)

            return JsonResponse({'message':'INVALID ID or PASSWORD'},status=401)

        return JsonResponse({'message':'INVALID ID or PASSWORD'},status=401)

class FollowUserView(View):
    def post(self,request):
        data = json.loads(request.body)

        try:
            if User.objects.filter(id=data['followed_user_id']).exists() and User.objects.filter(id=data['following_user_id']).exists():
                following_users = list(Following.objects.filter(followed_user=data['followed_user_id']).values('following_user_id'))

                for following_user in following_users:
                    if int(data['following_user_id']) == following_user['following_user_id']:
                        return JsonResponse({'message':'Already Followed'},status=401)

                Following(
                        followed_user_id     =   data['followed_user_id'],
                        following_user_id    =   data['following_user_id']
                ).save()

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

            return JsonResponse({'message':'INVALID_User_id'},status=401)

        except KeyError:
            return JsonResponse({'message': 'KEY_ERROR'},status=400)

 

3.1 View Class

자세하게 언급하기 전에 짚고 넘어갈 개념은 View Class입니다.
보시면 아시겠지만 각 class는 기능별로 구현이 되었고 API에 따라 함수가 구분됩니다. 그리고 가장 중요한 것은 이 각 class가 class에 변수로 들어간 즉, View라는 Class를 상속 받는다는 것 입니다.

부모 class인 view에 대해서 깊이 다루지는 않겠습니다. 한다면, 다른 포스트로 다뤄야 할 것 같습니다.
중요한건 이 class 기반의 View가 request를 받을 수 있고 API에 따라 함수를 사용할 수 있다는 것 입니다.

3.2 Django QuerySet: objects.filter()

지금 부터 Django 쿼리셋을 다뤄보겠습니다.
첫 번째는 가장 많이 사용되는 쿼리셋 중 하나인 objects.filter 입니다. 여기서 object란 models에 의해 생성된 각 Row를 의미 합니다.
SQL로 비유하자면 SELECT에 WHERE 조건과 비슷합니다. filter 조건에 맞지 않는다면 에러는 나오지 않고 빈 QuerySet 객체가 나옵니다. 여러개라면 아래 결과 같이 쿼리셋 객체 안에 User객체가 여러개인 형태로 출력 될 것 입니다.

<QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (3)>, <User: User object (4)>, <User: User object (5)>, <User: User object (6)>, <User: User object (7)>]>

filter 내에는 예제코드 외에도 여러가지 조건을 줄 수 있습니다. 포함하는 단어, 시간 등 다양한 조건이 있습니다.
또한, and와 or 조건도 가능합니다. arguments 내에 ,로 구분지어 여러 조건을 병합할 수 있습니다. or의 경우 다음과 같이 표현할 수 있습니다.

startwith_j_or_t_user = User.objects.filter(name__startswith='J') | User.objects.filter(name__startswith='T')

 

3.3 DjangoQuerySet: objects.get()

마찬가지로 자주 사용되는 Method중에 하나입니다. get은 filter와 다르게 하나의 객체만을 반환합니다. 만약 두 개 이상 또는 0개의 객체면 오류가 발생합니다.

이런 경우라면 get은 하나의 pk 값 처럼 고유값을 가지는 Field에 적용하기 좋습니다. 제가 작성한 예제코드에도 get은 이미 한 번 있다고 확인되는 경우에만 사용 했습니다. 로그인된 user의 객체를 받는데에 사용했습니다.

더 다양한 Django QuerySet 사용법이 있지만, 다 적기에 너무 많아 공식 홈의 주소를 기재합니다.

docs.djangoproject.com/en/3.0/ref/models/querysets/#istartswith

 

QuerySet API reference | Django documentation | Django

The Django Software Foundation deeply values the diversity of our developers, users, and community. We are distraught by the suffering, oppression, and systemic racism the Black community faces every day. We can no longer remain silent. In silence, we are

docs.djangoproject.com

3.4 filter한 객체들의 value만 list로 가져오기

 문장이 복잡한데 다시 한 번 정리하면 filter로 여러 객체를 받게 될텐데 이 중에서 Model의 특정 attribute(속성값)에 대해서만 list로 저장할 때의 방법입니다.

filter를 거친 객체는 QuerySet이라는 객체에 들어가 있으므로 바로 접근이 안 됩니다.
저는 이 객체들을 우선 value를 통해 dict형 자료구조에서 필요한 Key-Value를 추출 했습니다.
(예제코드의 FollowUserView 함수)
following_users = list(Following.objects.filter(followed_user=data['followed_user_id']).values('following_user_id'))
그리고 이를 list로 변형하면 아래 라인과 같은 출력결과를 볼 수 있습니다.

[{'following_user_id': 3}, {'following_user_id': 5}]

3.5 Django QuerySet 100배 활용하기 vol1. SQL 쿼리문으로 변경하기

Django QuerySet은 SQL 쿼리문과 100% 상응한다고 계속해서 언급을 하였습니다. 그리고 이 쿼리셋을 실제 SQL 쿼리문으로 볼 수 있습니다.

비교적 쉬운 쿼리문인 위에 예제로 기재한 T 또는 J로 시작하는 유저를 찾는 쿼리를 예로 들겠습니다.

>>> str(startwith_j_or_t_user.query) 'SELECT `users`.`id`, `users`.`name`, `users`.`email`, `users`.`password`, `users`.`created_at`, `users`.`updated_at` FROM `users` WHERE (`users`.`name` LIKE BINARY J% OR `users`.`name` LIKE BINARY T%)'

간단하게 .query를 붙이고 str 객체로 감싸니 출력 되었습니다. 조금 지저분해 보이긴 해도 정확하게 원하는 내용을 찾는 쿼리입니다.

3.6 Django QuerySet 100배 활용하기 vol2. python shell 활용하기

manage.py를 python shell로 수행하고 이를 테스트 한다면 파일을 직접 저장하지 않고도 내 쿼리셋이 정확한지 확인할 수 있을 것입니다.

확인할 수 있는 방법은 python manage.py shell로 확인이 가능합니다.
조금 전 제가 확인한 QuerySet들도 전부 shell을 통해 확인했습니다.

3.7 모든 case에 대한 return은 필수적

Web 통신에 대한 개념이 자리 잡지 못 해, views.py에서 로그인 케이스에서 return을 안 해준적 있습니다. 제가 하려했던 기능이 로그인과 글 내용을 동시에 전달하려고 했기 때문이었습니다.
결론 부터 얘기하면 이 케이스는 작성하기가 조금은 난해합니다. RESTful 한 즉, 기존의 포맷대로 한다면 각각은 별개의 기능으로 가는 것이 맞는 형태입니다.

즉, response가 없다면 통신에러로 기록됩니다.

제 예제 코드에서 JsonResponse를 준 것은 HTTP 통신을 주고 받는 대상인 Front-End가 JavaScript 언어이기 때문입니다.
Django Tutorial 같은 경우 자체 Template을 사용하기 때문에 HttpResponse를 건내줍니다.

여기까지 Django에서 가장 많이 다루는 urls, models, views 파일에 대한 상세 역할과 활용도를 확인 해보았습니다.
저에게도 단순히 EndPoint 구현할 때 보다 더 체계적으로 와 닿았습니다.

Django에 대해 다룰 내용은 너무 많으므로 구체적인 리뷰를 다시 가져보겠습니다.

반응형