Django 기본(1)

2024. 10. 11. 20:33·Python/Django

Python 가상 환경 설정하기

Django는 프로젝트마다 가상 환경을 설정해줘야 한다.

 

1) 의존성 관리

    라이브러리 및 패키지를 각 프로젝트마다 독립적으로 사용할 수 있다.

2) 팀 프로젝트 협업
    모든 팀원이 동일한 환경과 의존성 위에서 작업하여 버전 간 충돌을 방지한다.

py -m venv project-name
project-name\Scripts\activate.bat

deactivate

 

가상환경(venv) 생성하고 가상환경 활성화 시킨다. 비활성화시키려면 deactivate

가상환경은 안으로 들어오는 개념이 아닌 on/off 개념

 

vscode에서도 python환경을 맞출 수 있다. (이것때문에 코드에 에러 밑줄이..)

ctrl + shift + p => interpreter누르고 가상환경 폴더가 있는 곳으로 

출처: https://velog.io/@gata96/3.-Django-01

 

Django Project 시작하기

구축한 가상환경을 활성화시키고 장고 프로젝트 설치하기 

py -m pip install Django

그리고 프로젝트를  만들고 싶은 폴더로 이동한 다음

django-admin startproject mysite

django-admin startproject conf .

 

입력한 프로젝트 이름으로 프로젝트 폴더가 생성된다.

vscode에서 mysite 폴더를 열어보면 

manage.py는 장고 프로젝트를 터미널에서 조작할 수 있는 명령 제공한다. 

프로젝트 실행하기 위해서는..  http://127.0.0.1:8000/

python manage.py runserver

 

이 프로젝트 폴더에서 app을 하나씩 만드는 구조이다.

한 프로젝트는 수 많은 app으로 이루어져 있다. (하나의 웹사이트 생각하면)

 

새로운 앱 만들기 위해서는 

python manage.py startapp polls

 

그러면 polls라는 폴더가 생기고 안에 많은 파일이 생긴다. 

앱을 추가하고 이 앱의 url을 등록해줘야 한다.

그리고 등록된 url에서 어떤 응답을 줄지 구현해 주는 방식 

# polls/views.py
from django.http import HttpResponse
def index(request):
    return HttpResponse("Hello, world.")

# mysite/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
    path("admin/", admin.site.urls),
    path("polls/", include('polls.urls'))
]

#polls/urls.py
from django.urls import path
from . import views
urlpatterns = [
    path('',views.index, name='index')
]

 

모델과 ORM

지금까지는 장고 서버에 들어온 url 요청을 웹페이지를 통해 처리하는 과정이었다.

하지만 보통 서버는 들어온 요청에 대해 db에 저장된 값을 읽어와서 웹페이지를 그려준다. 

 

이 db의 값을 읽어오는 역할은 models.py에서 한다.

model은 db를 테이블별로 읽어서 하나의 테이블에 저장된 값을 코드에서 읽어올 수 있도록 한다. 

Django에서는 Django ORM이, Java에서는 Hibernate가 있다. 객체 간의 관계를 바탕으로 SQL을 자동으로 생성해서

sql쿼리문 없이도 데이터베이스의 데이터를 다룰 수 있게 해 준다. 자세한거는..

 

본격적으로 하기 전에 앱을 settings.py에 등록해줘야 한다.

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'polls.apps.PollsConfig',
]

 

그 후 다음과 같은 순서를 거친다.

1) 모델을 만든다.

2) 마이그레이션을 만든다.

3) 최종적으로 테이블 만든다. 

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

각 모델은 항상 models.Model을 상속받는다.  

django에 어떤 필드가 있는지는 docs.djangoproject.com의 documentation에서 model layer에서 보면 된다. 

 

python manage.py makemigrations

 

0001의 migrations 파일이 생긴다.

 

자세한 과정 확인해 보면 id가 자동으로 생성되고, foriegn key에 대해서도 indexing 해준다. 

이제 migration 파일 만들었고 어떻게 돌아가는지 보았으니까 실제 table을 만들면

python manage.py migrate

admin, auth, sessions,.. 같이 settings.py에 default로 있는 것들도 table로 만들어진다.  

정리하면

1) 처음에 모델 만들고

2) 모델에 적용할 mkaemigrations 만들고

3) migrate를 통해 실제 table을 변경한다.

 

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    #is_something = models.BooleanField(default=False)
    #average_score = models.FloatField(default=0.0)

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

 

이렇게 model을 변경하고 나면 

migration을 만들고 table에 반영해 준다. 

만든 두 필드를 다시 지우고 싶다면 

python manage.py migrate polls 0001

 

polls의 migration을 0001로 돌려준다.

그리고 vscode에 가서 migration을 지워준다. 

 

이렇게 실행된 migration은 sqllite3의 database 접속해서 확인할 수도 있다.(django에서 기본적으로 제공하는 db)

sqlite3 db.sqlite3   ->database에 terminal로 접속

. tables 하면 전체 테이블 볼 수 있다. 

SELECT * FROM django_migrations; 하면 여러 migration 확인 가능(polls1 , polls2)

에러! SQLite는 데이터베이스 파일을 열어보고, SQL 쿼리를 실행하며, 테이블 구조를 확인할 수 있는 프로그램이다.

또한 db.sqlite3라는 파일이 해당경로에 있어야 한다. 안 그러면. tables 시 0kb짜리 파일 생성됨 

Django Admin 계정 

Django에서는 시스템을 관리하는 관리자들에게 만들어진 Model을 

쉽게 CRUD 할 수 있는 Admin 페이지를 제공해 준다. 

python manage.py createsuperuser

 

mysite의 urls.py에 자동으로 admin의 url이 등록되어 있다. 접속해서 superuser를 자유롭게 추가할 수 있다. 

 

우리가 만든 Question, Choice 모델도 쉽게 다룰 수 있다. 

그러기 위해서는 admin.py에 모델들을 등록해줘야 한다.

from django.contrib import admin
from .models import *

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

 

그러면 관리자 페이지에서 다음과 같인 Question값을 추가할 수 있다.

 

여기서 내용을 Question의 제목으로 바꾸고 싶다면 

Question이 표현될 때 어떻게 표현되는지를 정의해 주면 된다. 

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    
    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}'

 

__str__  오버라이딩함으로써, 객체를 출력하거나 문자열로 변환할 때 원하는 형식으로 출력되도록 할 수 있다.

 

admin 페이지에는 데이터를 조회할 때는 DB(db.sqlite)에서 데이터를 가져오지만

그 데이터의 구조는 models.py에 정의된 모델을 참조해서 설정한다.

따라서 둘이 다르면(models.py에 저장된거하고 migration된거) 에러가 뜬다. 

Django Shell

python manage.py shell

 

위 명령어로 shell환경 실행, Django

일반 Python 쉘과는 달리 Django 프로젝트의 설정(settings.py)이 로드된 상태로 실행

ORM, 설정 등을 바로 사용할 수 있다.

참고로 장고 쉘은 변경사항이 있으면 바로 반영이 안되므로  수정되면 껐다 켜야 한다. 

#models.py 파일에 정의된 모든 모델 가져오기
>>> from polls.models import *
>>> Question

#모든 Question,Choice 오브젝트 가져오기
>>> Question.objects.all()
>>> Choice.objects.all()

#첫번째 Choice 오브젝트 가져오기
>>> choice = Choice.objects.all()[0]
>>> choice.id
>>> choice.choice_text
>>> choice.votes

#첫번째 Choice와 연결된 Question 가져오기
>>> choice.question
>>> choice.question.pub_date
>>> choice.question.id

#해당 Question과 연결되어 있는 모든 Choice 가져오기 
>>> question.choice_set.all()

 

Qustion과 Choice는 -> N:1의 관계이다. 

Choice에서는 Question으로 바로 접근이 가능하다. foriegn_key로 정의되어 있기 때문에

하지만 Question은 여러 Choice가 있기 때문에 choice_set.all()로 접근

 

Create, Delete

값을 shell에서 추가하거나 삭제하기 위해서는 

>>> from polls.models import *

#"커피 vs 녹차" 라는 내용의 새로운 Question 오브젝트를 생성하고 'q1'이라는 변수에 저장하기
>>> q1 = Question(question_text = "커피 vs 녹차")

#tiemzone을 활용하여 새로운 오브젝트 'q1'의 생성시각을 설정하기
>>> from django.utils import timezone
>>> q1.pub_date = timezone.now()

#새로운 Question 오브젝트 'q1'을 데이터베이스에 저장하기
>>> q1.save()

 

직관적이라 이해하기에는 어렵지 않은 것 같다. 

Question의 question_text인 객체를 만든다. (아직 메모리에만)

이때 timezone 정보가 없으면 에러. 추가해 준 뒤 q1.save()으로 db에 저장해준다. 

 

Model의 DateTimeField에 auto_now=True를 설정하면 객체가 저장될 때마다 현재 시간으로 자동 설정된다.

이를 활용하면 매번 timezone.now()를 호출하는 번거로움을 줄일 수 있다.

 

파이썬에서는 보통 datetime 모듈을 사용하여 현재 시각을 구한다.

그러나 장고(Django)에서는 웹 서비스에서 발생할 수 있는 다양한 시간대 문제를 해결하기 위해

datetime에 timezone정보가 들어가야 한다. 

>>> q3 = Question(question_text = "abc")
>>> q3.save()

#create() 메서드를 활용하여 q3와 연결된 새로운 Choice 오브젝트를 생성하고, choice_text 필드에 값을 넣어주기
>>> q3.choice_set.create(choice_text = "b")

#새로운 Choice 오브젝트를 생성하고 question 필드에 q3 값을 넣어 연결하기
>>> choice_c = Choice(choice_text='c', question=q3)

#새로운 Choice 오브젝트를 데이터베이스에 저장하기
>>> choice_c.save()

#Question 오브젝트 중 가장 마지막으로 만들어진 것을 가져오기
>>> q = Question.objects.last()

#해당 오브젝트의 question_text에 새로운 내용을 더해 수정하기
>>> q.question_text = q.question_text + '???'

#Choice 오브젝트 중 가장 마지막으로 만들어진 것을 가져오기
>>> choice = Question.objects.last()

#해당 오브젝트에 연결된 Question을 통해서 choice set을 가져오기
>>> choice.queston.choice_set.all()

#해당 오브젝트를 삭제하기
>>> choice.delete()

 

q3와 연결된 Choice 객체도 쉽게 생성 할 수 있따. 

q3.choice_set 뒤에 create() 메서드를 통해 생성한다.

아니면 Choice 객체를 직접 생성하고 save()메서드를 통해

이때 생성 시 question 필드에 q3를 넣어 연결한다. 

조회

모델을 통해 테이블에서 다양한 조건에 맞는 레코드들을 가져올 수 있다. 

# get() 메서드를 사용하여 조건에 해당하는 오브젝트를 필터링하기
>>> Question.objects.get(id=1)
>>> q = Question.objects.get(question_text__startswith='휴가를')
>>> Question.objects.get(pub_date__year=2023) # get으로 여러가지 오브젝트를 가져오려고 한다면 에러발생
polls.models.Question.MultipleObjectsReturned: get() returned more than one Question

#filter() 메서드를 사용하여 조건에 해당하는 오브젝트를 필터링하기
>>> Question.objects.filter(pub_date__year=2023)
<QuerySet [<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-02-05 18:52:59+00:00>, <Question: 제목: 가장 좋아하는 디저트는?, 날짜: 2023-02-05 18:53:27+00:00>, ...]>
>>> Question.objects.filter(pub_date__year=2023).count()

#쿼리셋의 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

>>> print(Question.objects.filter(question_text__startswith='휴가를').query)
SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" WHERE "polls_question"."question_text" LIKE 휴가를% ESCAPE '\'

>>> q = Question.objects.get(pk=1)
>>> q.choice_set.all()
>>> print(q.choice_set.all().query)
SELECT "polls_choice"."id", "polls_choice"."question_id", "polls_choice"."choice_text", "polls_choice"."votes" FROM "polls_choice" WHERE "polls_choice"."question_id" = 1

 

get() vs filter()

 

get() 메서드는 주어진 조건에 맞는 하나의 객체만 반환

조건에 맞는 객체가 없거나, 조건에 맞는 객체가 여러 개인 경우 예외 발생

filter() 메서드는 조건에 맞는 모든 객체들을 담은 QuerySet을 반환,

QuerySet은 데이터베이스로부터 조회한 결과 집합을 나타내며, 조건에 맞는 객체가 없으면 빈 QuerySet을 반환

참고로 Quetsion.objects.all()의 모든 object 가져올때도 QuerySet을 반환했다. 

 

.query 를 통해 실제 query를 가져올 수 도 있다. 예상대로 sql이 잘 나가고 있는지 파악할 떄

#startswith 연산자를 활용하여 오브젝트를 필터링하기
>>> q = Question.objects.filter(question_text__startswith='휴가를')
>>> q2 = Question.objects.filter(pub_date__year=2023)

#contains 연산자를 활용하여 오브젝트를 필터링하기
>>> Question.objects.filter(question_text__contains='휴가')

>>> Choice.objects.all()
>>> Choice.objects.filter(votes__gt=0)

#해당 쿼리셋에 대한 SQL 쿼리를 생성하기
>>> Choice.objects.filter(votes__gt=0).query
>>> print(Choice.objects.filter(votes__gt=0).query)
SELECT "polls_choice"."id", "polls_choice"."question_id", "polls_choice"."choice_text", "polls_choice"."votes" FROM "polls_choice" WHERE "polls_choice"."votes" > 0

>>> choice=Choice.objects.first()
>>> choice.votes=5
>>> choice.save()

#정규표현식을 활용하여 조건에 해당하는 오브젝트들을 필터링하기
>>> Question.objects.filter(question_text__regex=r'^휴가.*어디')
>>> print(Question.objects.filter(question_text__regex=r'^휴가.*어디').query)
SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date", "polls_question"."owner_id" FROM "polls_question" WHERE "polls_question"."question_text" REGEXP ^휴가.*어디

 

또한 보면 조건같은 것은 해당 attribue 뒤에 __을 붙인다. 

ex1) Question.objects.filter(question_text__contains='휴가') 휴가라는 단어가 포함된 질문들

ex2) Choice.objects.filter(votes__gt=0)  vote 값이 0보다 큰 것들 

여기서 더 많은 조건들 정보를 볼수 있다. 

그중에서 reg가 있는데...

정규 표현식

 

관계에 의한 필터링

#Question의 question_text 필드 값이 '휴가'로 시작하는 모든 Choice 오브젝트를 필터링하기
>>> Choice.objects.filter(question__question_text__startswith='휴가')

#exclude() 메서드를 사용하여 question_text 필드 값이 '휴가'로 시작하는 모든 Choice 오브젝트를 제외하고 필터링하기
>>> Question.objects.exclude(question_text__startswith='휴가')

 

모델 사이의 관계를 이용해서 filter할 수 있다.

Choice모델의 값을 filter해서 가져올때 그 조건을 foreign key의 Question에 대해서 적용할 수 있다.

예를 들어 질문내용에 휴가가 포함된 모든 choice들 불러내려면

question__question_text__startswith question의 question_text를 이용 그리고 startswith가 휴가인 경우에 가져와라라는 뜻

이렇게 foriegn key를 통해서 다른 테이블의 내용 이용 가능

 

exclude 방식도 있다.

Question.objects.exclude(question_text__startswith='휴가')   휴가를 포함하지 않는

 

web_framework에서 model을 통해 tabel 읽어오는 건 자주 쓰이므로 잘 익혀두자

 

모델에 관련 메소드도 추가할 수 있다. 

>>> choice.votes=5

>>> choice.save() 

여기서 save 메소드를 쓸 수 있는 이유는 Model을 상속받기 때문에

__str__  도 오버라이딩 했기 떄문에 

from django.utils import timezone
import datetime

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
    
    def __str__(self):
        if self.was_published_recently():
            new_badge = 'NEW!!!'
        else:
            new_badge = ''
        return f'{new_badge} 제목: {self.question_text}, 날짜: {self.pub_date}'

 

최근에 생성되 질문 글에 대해서 'NEW!!!'를 붙이도록

'Python > Django' 카테고리의 다른 글

Django 기본(3)  (0) 2024.10.15
Django 기본(2)  (0) 2024.10.11
'Python/Django' 카테고리의 다른 글
  • Django 기본(3)
  • Django 기본(2)
dev.di
dev.di
devdi 님의 블로그 입니다.
  • dev.di
    개발 블로그
    dev.di
  • 전체
    오늘
    어제
    • 분류 전체보기 (28)
      • Algorithm (9)
        • Basics (9)
      • AWS (0)
        • AWS (0)
        • SAA (0)
      • Computer Science (1)
        • OS 벼락치기 (1)
        • DB 벼락치기 (0)
      • Data Engineer (8)
        • Airflow (0)
        • Data Warehouse (0)
        • Kafka (0)
        • Spark (0)
        • 데브코스 (8)
      • Docker (0)
      • Interviews (1)
      • Network (2)
        • Physical Layer (0)
        • Data Link Layer (0)
      • OOP (3)
        • GoF (3)
      • Python (4)
        • Django (3)
        • Scraping (1)
      • Software Engineering (0)
      • Spring (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    sql
    포트포워딩
    IPv4
    데이터 웨어하우스
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
dev.di
Django 기본(1)
상단으로

티스토리툴바