TIL - Day10(django)

김혁·2023년 10월 31일
0

TIL - Dev

목록 보기
9/10

개발 환경 설정

# 가상환경 만들기
python3 -m venv project-name

# 가상환경 활성화
source project-name/bin/activate

# 비활성화
deactivate

# Django 설치하기
pip install django

# 새로운 Django 프로젝트 생성하기
django-admin startproject mysite

# 생성한 프로젝트를 서버에서 실행하기
python3 manage.py runserver

manage.py는 터미널에서 장고를 사용하기 위해서 만들어진다.

프로젝트는 하나의 웹사이트

  • 그 안의 기능은 하나의 app

webserver가 하는 일
url 요청이 들어왔을 때 그려주는 것은 브라우저 일
webserver는 url을 내려주는 일

만약 웹서버에 없는 url을 내려줬을 때

get 방식으로 url을 요청한 것을 확인할 수 있음

관계형 데이터베이스(RDB)

관계형 데이터베이스(RDB)는 데이터를 행과 열로 이루어진 테이블의 형태로 구성하고, 테이블 간의 관계를 정의하는 데이터베이스입니다.

Column(열)

관계형 데이터베이스에서 열(column)은 테이블에 존재하는 필드(field)를 나타냅니다. 필드는 앞으로 테이블의 행에 저장될 내용들이 무엇인지 표시하는 기능을 가지고 있습니다
Question 테이블에는 각 질문들을 식별할 수 있는 Question ID 필드와 질문의 내용에 해당하는 Question Text 필드, 총 두 가지의 필드가 존재하고 있습니다.

  • Question ID (primary key)

  • Question Text

Choice 테이블에는 각 선택지들을 식별할 수 있는 Choice ID 필드와 선택지의 내용에 해당하는 Choice Text 필드 그리고 해당 선택지가 어떤 질문에 속한 것인지 알려주는 Question ID 필드까지 총 3가지의 필드들이 존재하고 있습니다.

  • Choice ID (primary key)

  • Choice Text

  • Question ID (foreign key)

Question 테이블과 Choice 테이블의 필드 중 일부에는 그 옆에 primary key 혹은 foreign key라고 표시된 것들이 있습니다. 그 의미에 관해서 설명드리자면, 먼저 primary key는 테이블의 각 행(row)을 고유하게 식별할 수 있는 열(column)을 의미합니다. Question 테이블과 Choice 테이블에서는 각각 Question ID와 Choice ID가 primary key에 해당합니다.

foreign key는 다른 테이블의 primary key를 참조하는 열(column)을 의미합니다. foreign key를 사용하면 두 테이블 간의 관계를 설정할 수 있습니다. Choice 테이블의 Question ID는 Question 테이블의 primary key인 Question ID를 참조하는 foreign key 입니다. 이를 통해 각 선택지가 어떤 질문에 대한 선택지인지 그 포함 관계를 파악할 수 있습니다

Row(행)

테이블에서 가로로 늘어선 각각의 행(row) 은 테이블에 저장된 데이터 레코드(Record)를 의미합니다.
데이터 베이스에서는 아래와 같이 하나의 단위로 표현되어지는 필드들의 집합을 레코드라고 표현합니다.

각 레코드는 고유한 값을 가지는 Primary Key로 식별되는데, Question 테이블에서의 Primary Key는 Question ID입니다.
경우에 따라서 레코드들의 Question Text 내용이 중복되는 상황이 발생할 수 있지만 , 그럴때마다 Question ID를 이용하면 각 레코드를 식별할 수 있습니다.

장고(Django)에서는 다음 시간에 배울 모델(Model)이라는 개념을 활용해서 관계형 데이터베이스와의 연동을 구현합니다. 모델은 데이터베이스에서 테이블(Table)에 해당하며, 각 모델은 필드(Field)를 가지고 있습니다. 이번 시간 배웠던 관계형 데이터베이스의 개념과 각 용어들의 의미를 잘 기억하시고, 다음 강의에서 본격적으로 모델을 생성하는 방법에 대해 배워보도록 하겠습니다.

ORM

What is ORM? ORM은 메타데이터 설명자를 사용하여 객체 코드(object code)를 관계형 데이터베이스에 연결하는 프로그래밍 기술을 말합니다. 여기서 객체 코드란 Java, C#과 같은 객체 지향 프로그래밍(OOP) 언어를 말합니다.

migrations

모델을 테이블에 써 주기 위한 마이그레이션이라는 걸 만든다.
settings.py의 INSTALLED_APPS에 우리가 만들어준 apps.py의 config를 넣어준다

# 마이그레이션
python3 manage.py makemigrations polls

# 마이그레이션 내용 확인
python3 manage.py sqlmigrate polls 0001
BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "question_text" varchar(200) NOT NULL, "pub_date" datetime NOT NULL);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" bigint NOT NULL REFERENCES "polls_question" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;

우리가 지정하지 않은 id 컬럼이 생겼다. 장고 같은 경우 테이블을 만들 때, 장고에서는 id컬럼을 추가해줌.

마지막엔 create index를 볼 수 있음. index는 question의 id에 대해서 인덱싱을 할 수 있다. question의 id를 가지고 choice를 많이 조회하게 되는데, 인덱싱이 되어 있지 않다면, table을 full scan을 해야하기 때문에, 인덱싱을 상대적으로 바로 진행할 수 있다. 그래서 foreign 키에 대해서 인덱싱을 하게 된다.

# 마이그레이션 이후 table을 생성
python3 manage.py migrate     

# polls 외에도 다른 것은 default로 생성되어 있었던 INSTALLED APPS
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions

모델 -> 모델에 적용할 마이그레이션을 만듬 -> 마이그레이트를 이용해서 table을 생성

django Field

BooleanField

  • Boolean 값 저장

CharField

  • Text

DateTimeField

  • 날짜만 시간은 저장 안됌

FloatField

  • 실수

JsonField

  • json 형식

TextField

  • char 보다 긴 text를 저장

django document의 model layer를 확인해보면 된다.

db 접근

django의 default db는 sqlite이다.

  • sqlite3 db.db이름
    으로 접근하면 된다.
    ctrl + d -> 터미널에서 db 종료

수정했던 마이그레이션을 취소 시켜보자. 그전에 마이그레이션 한 것으로 돌려보자

python manage.py migrate polls 0001

혹시모르니 sqlite에 접근해서 column이 잘 삭제 되었는지 확인해 보자.
# django 관리자 계정 설정
python manage.py createsuperuser

admin page

admin page는 일반적으로 시스템을 관리하는 관리자들이 데이터를 추가하고 수정하는 페이지를 의미. 이런 관리자형 페이지를 하나하나 만들기엔 귀찮은 일임. 그래서 장고에서는 모델들을 기반으로 데이터를 만들고 읽고 업데이트 하거나 지울 수 있는 기능을 제공한다. CRUD

python manage.py createsuperuser

django admin - 모델 등록하기

사용자 뿐만 아니라 우리가 만든 모델들도 CRUD를 하면 좋을 것 같다.
=> 장고 admin에게 모델들도 다뤄달라고 적기만 하면 된다.

# admin.py
# Register your models here.
admin.site.register(Question)
admin.site.register(Choice)

django shell

python3 manage.py shell

#django shell에서 접근할 때,
from polls.models import *
Question.objects.all()
class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    
    #보통 booleanField 같은 경우는 true/false이기에 is_로 이름을 짓는다.


    # 자기 자신을 문자열로 정의할 때
    def __str__(self):
        return f'제목: {self.question_text}, 날짜: {self.pub_date}'


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}'

이것을 보았을 때 choice에서는 question(foreign key로 지정이 되어있기 때문에)에 바로 접근이 가능하다. 하지만 question에서 choice 접근이 불가능하다.

# question에서 choice에 접근하기 위해서는
question.choice_set.all()
>>> <QuerySet [<Choice: 바다>, <Choice: >]>

#기억하면 좋을 것(레코드 중 첫번째 row)
q2 = Question.objects.all()[0]

# 본코드
q1 = Question(question_text = "~")
q1.save() # db 저장
>>> error
# 이유는 question pub_date 같은 경우는 not null 인데 q1같은 경우는 아직 pubdate가 없기 때문

from django.utils import timezone
q1.pub_date = timezone.now()

그런데 매번 timezone을 불러와서 등록해주는 것은 매우 귀찮은 일
-> model.py 자체를 수정해보자

#question의 unique id
class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField(auto_now_add=True)
    #보통 booleanField 같은 경우는 true/false이기에 is_로 이름을 짓는다.


    # 자기 자신을 문자열로 정의할 때
    def __str__(self):
        return f'제목: {self.question_text}, 날짜: {self.pub_date}'

DateTimeField 자체를 auto_now_add = True로 지정하게 된다면 pub_date 자체는 질문이 등록될 때마다 자동으로 시간 생성

shell 자체에서 이제 질문을 만들고 답변을 달아보자

q3 = Question(question_text = 'abc')
q3.save() # db 저장 및 pub_date 자동 생성

# 답변 만들기
q3.choice_set.create(choice_text ='a')
<Choice: a>
>>> q3.choice_set.all()
<QuerySet [<Choice: a>]>
>>> q3.choice_set.create(choice_text ='b')
<Choice: b>
>>> q3.choice_set.all()
<QuerySet [<Choice: a>, <Choice: b>]>
# choice_set.create로 만든 choice 같은 경우는 save가 필요 x

>>> choice_c = Choice(choice_text ='c', question = q3)
>>> choice_c.save()
>>> q3.choice_set.all()
<QuerySet [<Choice: a>, <Choice: b>, <Choice: c>]>

django shell에서 레코드 수정하기

>>> q = Question.objects.last()
>>> q.question_text
'abc'
>>> q.question_text = q.question_text + '???'
>>> q.question_text
'abc???'
# save 전 까지는 django shell의 메모리에만 저장되어 있음 db적용 x
>>> q.save()
>>>
>>> choice = Choice.objects.last()
>>> choice.question.choice_set.all()
<QuerySet [<Choice: a>, <Choice: b>, <Choice: c>]>
>>> q = choice.question
>>> q.choice_set.all()
<QuerySet [<Choice: a>, <Choice: b>, <Choice: c>]>
>>> choice.delete() # 삭제
(1, {'polls.Choice': 1})
>>> q.choice_set.all() # 확인 가능
<QuerySet [<Choice: a>, <Choice: b>]>
>>> choice # 메모리상에는 살아있음
<Choice: c>
>>> choice.choice_text
'c'
>>> choice.id
>>> choice.save() # 다시 저장하면 다시 디비에 저장
>>> q.choice_set.all()
<QuerySet [<Choice: a>, <Choice: b>, <Choice: c>]>
q.choice_set.all()
<QuerySet [<Choice: a>, <Choice: b>, <Choice: c>]>
>>> Question.objects.get(id=1)
<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-10-31 17:10:37+00:00>
>>> Question.objects.get(pub_date__second=37)
<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-10-31 17:10:37+00:00>
>>> Question.objects.filter(pub_date__year=2023)
<QuerySet [<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-10-31 17:10:37+00:00>, <Question: 제목: 가장 좋아하는 디저트는?, 날짜: 2023-10-31 17:11:04+00:00>, <Question: 제목: 커피 vs 녹차, 날짜: 2023-10-31 20:57:38.058378+00:00>, <Question: 제목: abc???, 날짜: 2023-10-31 21:10:38.240797+00:00>]>
>>> Question.objects.filter(pub_date__year=2023).count()
4
>>> q = Question.objects.all().first()
>>> q.choice_set.all()
<QuerySet [<Choice: 바다>, <Choice: >]>
>>> q.choice_set.all().count()
2
>>> choice = q.choice_set.all().first()
>>> choice
<Choice: 바다>
>>> choice.question
<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-10-31 17:10:37+00:00>

get은 원소 하나만 가져올 수 있지만, filter를 이용하면 모든 레코드를 다 가져올 수 있다. 여기서 알아야 할것은 가져오는 레코드 값이 여러개라면 queryset으로 가져온다. choice -> question은 1개의 object만 가져오기 때문에 count를 쓸 수 없다

.query를 붙이게되면 sql 문을 가져올 수 있다.

>>> print(Question.objects.filter(pub_date__year=2023).query)
SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" WHERE "polls_question"."pub_date" BETWEEN 2023-01-01 00:00:00 AND 2023-12-31 23:59:59.999999

django 모델에서 이런 것을 다 처리해줌

filter 와 반대되는 조건은 exclude

>>> Choice.objects.exclude(question__question_text__startswith='휴가')
<QuerySet [<Choice: [커피 vs 녹차]녹차>, <Choice: [커피 vs 녹차]커피>]>
>>>

model내 메서드 만들기

    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

    # 자기 자신을 문자열로 정의할 때
    def __str__(self):
        if self.was_published_recently():
            new_bedge = "NEW!!!"
        else:
            new_bedge = ''
        return f'{new_bedge}제목: {self.question_text}, 날짜: {self.pub_date}'

django timezone

  • django 시간대 설정을 고려하여 현재시간을 가져옴

datetime.timedelta(days=1)

  • 는 단지 하루를 나타내는 시간값 임

즉 pub_date가 24시간 내의 질문인지 파악하는 용도

profile
군도리

0개의 댓글