터칭 데이터

장고(Django) - 쉘(Shell) & 쉘에서 모델 다루기 본문

장고 (Django)

장고(Django) - 쉘(Shell) & 쉘에서 모델 다루기

터칭 데이터 2023. 11. 1. 12:41

장고 쉘

파이썬 쉘과 마찬가지로 장고를 터미널 환경에서 다룰 수 있도록 해줍니다.

 

터미널에서

(당연히 가상환경(DjangoProjects)를 실행하고 프로젝트(mysite)까지 경로 설정이 되어있어야 합니다.)

 

 

python manage.py shell

장고 쉘을 시작하는 명령어 입니다.

 

 

Python 3.10.9 | packaged by Anaconda, Inc. | (main, Mar  1 2023, 18:18:15) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)

>>>

위와 같이 뜨면 장고 쉘을 성공적으로 켠 것입니다.

장고 환경을 터미널에서 쓸 수 있도록 그대로 로딩된 것과 같습니다.

 

우리가 그동안 다룬 모델들 역시 다룰 수 있습니다.

 

 

 

 

 

 

쉘에서 모델 다루기

 

한번 Question 모델을 다뤄볼까요?

 

>>> Question
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'Question' is not defined

Question이 정의되지 않았다며 에러가 뜹니다.

 

 

맨 위에서 장고 쉘 역시 파이썬 쉘과 같다고 말씀드렸죠?

장고에서 모델은 ORM 방식으로 객체처럼 다룬다고 했습니다.

장고 쉘 역시 Question이라는 모델을 객체처럼 다루기 위해서는 import를 먼저 해줘야 합니다.

 

>>> from polls.models import *
>>> Question
<class 'polls.models.Question'>

import를 마치니 Question 모델을 객체 형태로 다룰 수 있게 되었습니다.

 

그런데 import 코드를 보면 poll.models에서(from) 모델들을 import 하는데요

 

 

 

 

그 이유는 우리가 장고 쉘을 mysite 프로젝트에서 실행했기 때문입니다.

장고 쉘은 빨간색 mysite라는 디렉토리를 기준으로 실행되기 때문에

파란색 polls 앱(폴더)의 연두색 models.py에 있는 모델들을 사용하려면

 

from polls.models import *

위와 같이 입력해야 합니다.

 

장고 쉘은 실행한 프로젝트 위치를 기준으로 삼는다는 점을 기억해주세요.

 

 

 

 

 

 

 

쉘에서 모델 객체로 데이터 읽기

이제 Question 모델도 객체로 가져왔겠다. 데이터를 읽어보고 싶은데 어떻게 해야할까요?

 

가장 간단한 방법은

Question.objects.all()

모델객체.objects.all()로

 

실행결과는 다음과 같습니다.

<QuerySet [<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-10-31 14:19:40+00:00>, 
       <Question: 제목: 가장 좋아하는 디저트는?, 날짜: 2023-10-31 14:22:37+00:00>]>

한가지 눈에 띄는 부분은 우리가 지난 시간 어드민에서 다룬 것 처럼

제목: ~~, 날짜: ~~ 형식으로 데이터들이 조회된다는 것인데

 

이는 우리가 지난 어드민 학습 시간에 __str__ 메서드를 정의했기 때문입니다.

 

 

 

 

Question뿐만 아니라 Choice 데이터들도 보고 싶은데 현재 Choice에는 데이터가 없는 상태입니다.

어드민 페이지에서 Choice에 데이터들을 추가해봅시다.

 

Choice에서 Question은 '휴가를 어디서 보내고 싶으세요?'로 고른 뒤 바다라는 선택지를 추가합니다.

(처음에 테이블을 만들 때 Choice는 Question 질문을 참조해야 한다고 했습니다.)

 

 

데이터도 만들었겠다 이제 터미널에서 Choice 데이터들을 조회해보면

>>> Choice.objects.all()
<QuerySet [<Choice: Choice object (1)>]>

단순히 Choice object (1)로 뜹니다. Choice 모델에는 __str__ 메서드를 정의하지 않았기 때문입니다.

 

 

polls.models.py에

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
    
    def __str__(self): # 추가
        return f'{self.choice_text}' # 추가

__str__ 메서드를 추가합니다.

 

 

그런데 어드민 페이지에서 새로고침을 하면 Choice object (id)에서 바다로 바로 반영이 되는데 반해

 

 

>>> Choice.objects.all()
<QuerySet [<Choice: Choice object (1)>]>

쉘에서는 바로 반영이 되지 않습니다.

장고 쉘은 모델 설정 변경시 자동 로딩이 되지 않기 때문입니다.

그래서 쉘을 껐다 켜줘야 합니다.

 

>>> exit()
>python manage.py shell

 

쉘 역시 껐다 켜면 import도 다시 해줘야 합니다.

>>> from polls.models import *
>>> Choice.objects.all()
<QuerySet [<Choice: 바다>]>

이제 Choice 역시 '바다'라고 뜨는 것을 볼 수 있습니다.

 

 

 

 

 

모델 객체로 데이터 다뤄보기

 

모델의 각 데이터 뒤에 .(필드명)을 붙이면 

>>> Choice.objects.all()
<QuerySet [<Choice: 바다>]>

>>> choice = Choice.objects.all()[0]

>>> choice.id
1

>>> choice.choice_text
'바다'

>>> choice.votes
0

그 필드(컬럼)에 대응하는 데이터들을 조회할 수 있습니다.

 

 

 

여기에 하나 더!

Choice테이블의 데이터 choice(여기서는 Choice.objects.all()[0])는 자신이 참조하고 있는 Question모델(테이블)로 바로 접근할 수 있습니다.

>>> choice.question
<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-10-31 14:19:40+00:00>

>>> choice.question.question_text
'휴가를 어디서 보내고 싶으세요?'

>>> choice.question.pub_date
datetime.datetime(2023, 10, 31, 14, 19, 40, tzinfo=datetime.timezone.utc)

>>> choice.question.id # choice가 아니라 question의 id입니다!
1
 

choice.question ~ 로 Question 모델의 데이터도 바로 읽어 올 수 있는데요.

 

 

 

이는 우리가 model.py에서 Choice 모델을 정의할 때 Choice는 Question의 기본키를 참조한다고 설정했기 때문입니다.

그덕분에 우리는 Choice모델의 데이터 choice를 이용해 그 choice가 참조하는 Question의 데이터까지 쉽게 접근할 수 있었던 것입니다.

 

 

 

그렇다면 반대로 Question에서는 Choice로 접근할 수 있을까요?

 

정답은 NO입니다.

 

그 이유는

1.  바로 위 이미지처럼 Question 모델을 정의할 때 Choice 모델을 참조한다는 ForeignKey 설정을 하지 않았고

2. Choice 입장에서는 참조하는 Question이 단 하나지만, Question 입장에서는 자신을 참조하는 Choice는 많고 동시에 막연하기 때문입니다.

 

>>> question = Question.objects.all()[0]

>>> question
<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-10-31 14:19:40+00:00>

>>> question.question_text
'휴가를 어디서 보내고 싶으세요?'

>>> question.choice # 너무 막연하다
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'Question' object has no attribute 'choice'

다른 명령들은 잘 작동하지만 question.choice는 맨 아래 코드에서 보이듯이 question을 참조하는 여러 choice들 중에서 어떤 choice를 조회해야 하는지 막연하기 때문에 에러가 뜹니다.

 

그래서 Question과 같이 참조되는 모델에서 Choice와 같이 자신을 참조하는 모델의 데이터로 접근하려면

>>> question.choice_set.all()
<QuerySet [<Choice: 바다>]>

모델객체.(자신을 참조하는 소문자 모델명)_set.all() 명령을 입력합니다.

 

지금은 데이터가 1개만 뜨는데요.

 

 

어드민에서 Choice 모델에서 '휴가를 어디서 보내고 싶으세요?'에 대한 Question을 참조하는 '강'이라는 데이터를 추가한 후

 

 

쉘에서 다시 조회를 해보면

>>> question.choice_set.all()
<QuerySet [<Choice: 바다>, <Choice: 강>]>

바다와 강, 데이터가 2개 뜹니다.