🔥TIL#5. Queryset과 json serialize

백승진·2020년 11월 7일
0

wecode Django 실습

목록 보기
7/16

What you will learn?

  1. Queryset API?
  2. Queryset에서 and, or 조건으로 데이터 조회하는 방법.
  3. Queryset으로 join 기능 구현 방법.
  4. Queryset 다중 join 기능 구현 방법.
  5. 하나의 table에서 2개 이상의 FK가 타 table의 PK를 동시에 relation해야 할 경우 django 에서 생기는 문제 조치 방법.

1. Queryset API?

models.py를 통해 class를 정의하고 migration을 통해 table을 생성을 할 수 있다고 배웠다. 그러면 table에 대한 CRUD를 하려면 어떻게 해야 할까?
이를 위해 Django는 Queryset API를 제공한다. 다음 코드를 보자.

class Users(models.Model):
    name            = models.CharField(max_length=50)
    phone_number    = models.CharField(max_length=20)
    email           = models.CharField(max_length=50)
    password        = models.CharField(max_length=300)
   
    class Meta:
    	db_table = 'users'

위는 models.py에 정의된 Users class 이다. 이는 migrate시 database에 'users' table로 생성될 것이다. 그리고 Users class는 아래와 같이 사용할 수도 있다.

# 회원가입시 중복되는 전화번호, 사용자 이름, 이메일이 있으면 에러 메시지 응답.
if Users.objects.filter(name=user_account['name']).exists():
	return JsonResponse({"message":"same name is already exist."}, status=400)
if Users.objects.filter(name=user_account['phone_number']).exists():
	return JsonResponse({"message":"same phone_number is already exist."}, status=400)
if Users.objects.filter(name=user_account['email']).exists():
	return JsonResponse({"message":"same email is already exist."}, status=400)

Users(
	name            = user_account['name'],
	email           = user_account['email'],
	phone_number    = user_account['phone_number'],
	password        = data['password'],
	).save()

위는 Users class를 이용해서 users table의 데이터를 조회하고 추가도 하는 로직이다.
models.py에 정의한 class로 table에 대한 제어가 가능한데 이것이 Queryset API 기능이다.

2. Queryset에서 and, or 조건으로 데이터 조회하는 방법.

select * from users where (name=value1 or email=value1 or phone_number=value1) and password='password';

위는 Table에서 and와 or 조건을 이용하여 해당 row를 조회하는 sql 이다.
이를 Queryset으로 구현해보면 아래와 같다.

from django.db.models import Q

rows = Users.objects.filter(Q(name=login_info['account'])
			| Q(email=login_info['account'])
			| Q(phone_number=login_info['account']),
			password=login_info['password'])

'filter' 함수는 sql의 select와 같은 조회 기능이며 매개변수로 *args 형식을 받는다.
',' 기준으로 입력되는 값들이 자동적으로 and 조건이 된다.
or 조건의 경우 django.db.models.Q 기능을 이용, 위 코드처럼 Q()에 하나의 조건을 넣고 여러 Q를 '|' 로 묶으면 된다.

3. Queryset으로 join 기능 구현.

Queryset는 SQL의 join 기능도 제공한다. 먼저 example sql을 보자.

select posts.name, users.name 
from posts where posts.id=1
join users ON posts.user_id = users.id

posts table에서 'id=1' 인 post의 name을 가져온다. 이 때 post의 user_id(FK)에 매칭되는 users가 있다면 해당 user의 name을 get, 둘을 join한 조회 결과를 제공한다.
이제 Queryset 으로 구현한 code를 보자.

rows = Posts.objects.select_related('user').filter(id=1)
for row in rows:
	print(row.name)			# post 이름
    	print(row.user.name)		# 해당 post를 작성한 user의 이름

select_related()이 sql의 'join'의 역할을 하는 함수이고 argument는 ForeignKey(여기서는 'user_id') column 명이 들어간다.

[참고할 사항]

위에 select_related시 'user'를 인자로 넣었는데 실제 column명은 'user_id' 이다. 혼란을 발생시키는 요인이므로 어떤 이유인지에 대해 조금 설명하려 한다.

위 사진에서 Posts class를 보면 'user'가 FK로 지정되고 있다. 이번엔 database를 보자.

posts table에는 'user_id' 로 들어가 있다. 왜 틀린걸까?

이유는 django에선 FK로 생성되는 column은 migration시 column + '_id'를 자동으로 붙여주기 때문이다. Queryset은 models에 선언된 형식을 따라야 한다.

4. Queryset 다중 join 기능 구현 방법.

class ShowAllPosts(View):
    def get(self, request):
        posts       = []
        entries     = Posts.objects.select_related('content').select_related('content__user').all()

        for row in entries:
            posts.append({"article":row.content.article, "image_url":row.image_url, "created_at":row.content.created_at , "user":row.content.user.name})

        return JsonResponse({"posts":posts}, status=200)

위 code를 보면 select_related 를 두번 실행하고 있는데 'content'에 대한 join을 실행 후 join 결과물에서 content가 relation하고 있는 user에 대해 한번 더 join을 하고 있다.
join 결과물을 다시 join 하는 경우부터 '__' keyword를 이용, relation 관계인 두 key를 연결한다.

[추가 정보]
최근에 멘토님으로부터 위와 같은 경우 select_related를 한번만 호출하는 것도 가능하다는 것을 배웠다.

Posts.objects.select_related('content', 'content__user').all()

5. 하나의 table에서 2개 이상의 FK가 타 table의 PK를 동시에 relation해야 할 경우 django 에서 생기는 문제 조치 방법.

sql로 실현은 가능하나 django는 경고 문구를 발생시키면서 처리하지 않는다. 아래 코드를 보자.

class Follows(models.Model):
    followed_user   = models.ForeignKey(Users, related_name='related_followed_user',  on_delete=models.CASCADE)
    following_user  = models.ForeignKey(Users, related_name='related_following_user', on_delete=models.CASCADE)

위 code는 2개의 column이 Users의 PK를 relation 하도록 시도하고 있다. argument로 'related_name' 이라는 것이 들어가 있는데 만약 이 argument를 넣어주지 않으면 django는 Error 처리한다.
여러개의 FK가 하나의 PK를 같이 바라봐야 하는 경우 'related_name'을 적절히 사용하자.
related_name 관련 django document

profile
12년 .NET 개발 경력을 가진 웹 초짜 개발자입니다 :)

0개의 댓글