AIVLE TIL ('23.05.12) Django (3)

cjkangme·2023년 5월 12일
0

에이블스쿨

목록 보기
71/81
post-thumbnail
post-custom-banner

ORM (Object Relation Mapping)

ORM (Object Relation Mapping)은 객체-관계-맵핑의 약자로, 말 그대로 객체와 관계를 맵핑해주는 도구이다.

코드(객체)를 통해 관계형 데이터베이스를 다룰 수 있게한다.

ORM의 장점

  1. 유지보수의 편의성 증가
    • SQL과 코드를 섞어 사용하지 않으므로 가독성 증가
    • 객체지향적 접근이 가능하므로 재사용 및 유지보수가 용이
    • SQL 쿼리 중복 방지
  2. DBMS에 대한 의존도가 낮아짐
    • DBMS에 따라 문법, 기능이 달라지는 것을 신경쓸 필요 없고 ORM을 통해 일관된 방법으로 다룰 수 있음
  3. 보안 강화
    • ORM에서 SQL 삽입 공격을 차단

Django ORM 사용

django에서 사용할 수 있는 라이브러리인 django-extensions를 통해 관계형 데이터베이스를 객체로 다룰 수 있다.

django shell 사용하기

  1. pip install django-extensions 로 라이브러리 설치
  2. 사용할 프로젝트의 settings.py > INSTALLED_APPS에 django-extensions 추가
  3. 이제 python manage.py shell_plus 로 django shell 진입 가능

조회

# 모든 사용자 가져오기
User.objects. all()
>> SELECT * FROM user_auth;
# 이메일 주소로 사용자 필터링하기
User.objects. filter(username='admin' )
>> SELECT * FROM user_auth WHERE username='admin';
# 사용자 이름으로 정렬하기
>> SELECT * FROM user_auth ORDER BY username ASC;
  • QuerySet 객체를 받아오며, 이를 변수로 저장하면 반복문 등에서 하나씩 꺼내어 사용할 수 있다.
    • QuerySet.__dict__ : 객체를 딕셔너리로 변환
{'_state': <django.db.models.base.ModelState at 0x26df4d530a0>,
 'id': 1,
 'password': 'pbkdf2_sha256$600000$pLjDzijWJbFyVp6Vfi8cdq$26xMiL5jalyIUwYFNNL2J99LGgOCsWWjHvgZ7dZt2eE=',
 'last_login': datetime.datetime(2023, 5, 12, 1, 48, 54, 123218, tzinfo=datetime.timezone.utc),
 'is_superuser': True,
 'username': 'admin',
 'first_name': '',
 'last_name': '',
 'email': '',
 'is_staff': True,
 'is_active': True,
 'date_joined': datetime.datetime(2023, 5, 12, 0, 53, 40, 498738, tzinfo=datetime.timezone.utc)}

생성

# 새로운 사용자 생성하기
new_user = User.objects.create(
username= 'exampleuser' ,
password= 'examplepassword' ,
email= 'example@example.com' )

# 새로운 사용자 생성하기2
new_user = User(
username= 'exampleuser' ,
password= 'examplepassword' ,
email= 'example@example.com')
new_user.save()

생성에는 두 가지 방법이 있다.
1. create() 메서드 이용
2. 데이터 객체를 직접 만든 뒤, save() 메서드를 통해 저장

수정

# 사용자 정보 업데이트하기
user = User.objects.get(username= 'exampleuser' )
user.email = 'newemail@example.com'
user.save()
# 사용자 삭제하기
user = User.objects.get(username= 'exampleuser' )
user.delete()
  • 기본적으로 조회문을 통해 데이터를 객체에 담아 수정한다.
  • save(), delete() 등을 통해 데이터 조작을 확정한다.
    • create() 역시 데이터 조작을 확정하는 것이기 때문에 save()를 하지 않아도 테이블에 데이터가 추가된다.

로깅

  • settings.py 에서 로그 설정을 하면 SQL 사용 내역과 어떤 과정을 거쳐 DB의 자료를 조회하는지 로그를 남길 수 있다.
  • DB의 성능 저하, 오류 등이 발생할 때 추적하는 것에 용이하다.

객체지향과 Python

데이터 구조체의 역사 (데이터 처리 방식의 진화)

초창기 - Structure Type의 형태

클래스에 아무런 메서드 없이 데이터만 담아서 사용하는 것

class User:
def __init__(self, id, name, email):
	self. id = id
	self.name = name
	self.email = email

users = [
	User(id=1, name='blackdew' , email= 'blackdew7@gmail.com' ),
	User(id=2, name='django' , email= 'django@naver.com' ),
	User(id=3, name='python' , email= 'python@gmail.com' ),
]

User 클래스로 생성된 인스턴스는 id, name, email 값을 가지는 형식의 일종의 데이터 자료형처럼 사용할 수 있다.
데이터 구조를 명시함으로써 코드를 읽기만해도 데이터 구조를 쉽게 파악할 수 있다는 장점을 갖는다.

  • 단점
    • struct 타입의 데이터 구조를 정확히 알고 있어야만 데이터를 다룰 수 있다.
    • 마찬가지로 데이터 구조를 알지 못하면 이 데이터를 다루는 코드를 알기 어렵다

발전된 형태 - Class 문법

데이터를 다루는 함수를 클래스 안에 담아서 사용하는 것

class User:
	def __init__(self, id, name, email):
		self. id = id
		self.name = name
		self.email = email
	def domain(self):
		return self.email.split('@')[-1]

def filter_domain(users, domain):
	return [u for u in users if u.domain() == domain]
filtered = filter_domain(users, 'gmail.com' )
for f in filtered:
	print(f.name, f.email)

사용자는 데이터의 구조를 몰라도 domain() 메소드의 기능만 알고 있으면 사용할 수 있다.

현재 - 객체 지향

캡슐화, 다형성, 상속을 이용하여 객체들을 연결 시켜 프로그래밍 하는 것

코드 재사용, 유지 보수를 감소시키는 장점이 있다.

Python Class

  • 파이썬에서 ‘객체 지향’을 위해 이해해야 하는 것들
    • 속성(property)과 행동(method)의 관점 이해 및 사용
    • 상속에 대한 이해
    • self에 대한 이해
    • magic methods에 대한 이해

파이썬의 기본 클래스

  • list, dict, set 과 같은 자료구조는 모두 하나의 객체들이다.
  • dir() 내장 함수는 객체가 어떤 메소드를 갖고 있는지 출력한다.

기본 클래스 상속하여 강화하기

  • map 함수를 메서드 형식으로 사용하고 싶다면?

class xlist(list):
	def map(self, f):
		s = map(f, self)
		return xlist(s)

→ 이제 리스트를 선언해서 arr.map(function)을 이용하면 리스트를 반환받을 수 있다!

객체지향 방법론과 데이터 베이스

  • Class 기반 객체지향 방법론을 웹에 데이터베이스 처리에 사용하는 것은 매우 어렵다.
  1. 객체 지향 프로그래밍과 관계형 데이터베이스는 서로 다른 패러다임을 갖고 있다.
  2. 쿼리에 따라 가져오는 데이터의 형태가 다르기 때문에 클래스 문법을 통해 일관적으로 처리할 수 없다.
  3. 데이터베이스의 스키마가 바뀌면 Class의 코드도 바꾸어주어야 하기 때문에 유지보수가 복잡해진다.
  • 이를 극복하는 방법론이 바로 ORM이다.
    • 데이터페이스의 테이블을 객체 지향 언어의 클래스로 맵핑함
    • 이를 통해 코드 내에서 객체를 처리하는 것 처럼 데이터를 처리할 수 있다.
    • django에서는 모델을 통해 ORM을 다룰 수 있다.

Django Model

  • MVT 구조

DB와 ORM을 통해 데이터를 주고받을 수 있도록 데이터 구조를 지정한다.
Model을 거쳐 객체화된 데이터는 view에서 파이썬 코드를 통해 다양한 처리가 가능하다.

모델 선언 과정

1. 클래스 생성

# models.py
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length = 100)
    content = models.TextField()
    created_at = models.DateField(auto_now_add = True)
    published_at = models.DateField(null = True)

2. Migrate

이 과정을 거치친 후 migration을 만들어야 한다.

python manage.py makemigrations <app 이름> : 지정한 앱에 migration을 추가한다.

migrate 전

migrations 파일 생성 후

  • migrate 정보를 저장하는 일종의 버전 역할을 하는 파이썬 파일이 생성되었다.
  • 이제 migrate를 할 경우 manage.py가 새로운 버전 파일을 읽어 테이블 구조를 생성한다.

migrate 후

  • class에 선언한 구조대로 테이블이 생성되었다.

3. 관리자페이지 등록

  • 테이블을 생성한 application의 admin.py 에 추가한 모델을 등록하면 된다.
# admin.py
from django.contrib import admin
from . import models

# Register your models here.
admin.site.register(models.Post)

  • 생성한 테이블이 관리자페이지에 등록되었다.

버전 되돌리기

  • 만약 이전 버전으로 돌아가고 싶다면 migrate 명령어를 통해 쉽게 돌아갈 수 있다.
  • python manage.py migrate blog 0001 : blog앱에서 선언했던 0001버전으로 돌아간다.

Model Field의 종류

  • CharField : 짧은 문자열, max_length로 최대 길이를 지정해야한다.
  • TextField : 긴 문자열, 최대 길이를 지정할 필요가 없다.
  • IntergerField : 정수 값 필드
  • DataField : 날짜 값 필드
  • 관계형 필드 : SQL에서 JOIN과 동일한 역할을 한다.
    • ForeignKey : 일대다 관계를 형성하는 관계형 필드

    • OneToOneField : 일대일 관계를 형성하는 관계형 필드

    • ManyToManyField : 다대다 관계를 형성하는 관계형 필드

    • 연결하려는 모델을 매개변수로 입력

      class Student(models.Model):
          user = models.OneToOneField(User, on_delete=models.CASCADE)
          name = models.CharField(max_length=50)
          year = models.IntegerField()
    • User와 Student는 일대일 관계이다 → 한명의 유저는 한명의 학생이다.

      • User는 장고에서 제공하는 기본 모델이기 때문에 건드리지 않고 이런식으로 일대일 관계를 연결하여 사용하는 경우가 많다.
  • 관계데이터베이스의 관계성은 기능도 많고 복잡함도 많으니 되도록 따로 공부하도록 하자..

관계형 필드

  • 자식 객체 → 부모 객체 접근 (일대일, 다대다에서 다)
    • 연결된 테이블의 데이터를 테이블명 체이닝을 통해 가져올 수 있다.
    • ex) user와 student 테이블이 연결되었을 때 → user.student.name 식으로 사용 가능
  • 부모 객체 → 자식 객체 접근 (다대다, 다대일에서 일)
    • 하나의 데이터만 가져오는 것이 불가능하므로 필드명에 더해 조건을 넣어주어야 한다.
    • ex) student.course_set.all() → 모두 가져오기
    • ex) student.course_set.get(id=5) → 연결된 데이터중 id 컬럼값이 5인 데이터만 가져오기
    • 위와 같이 <테이블명>_set 으로 묶어 사용한다. → 필드가 지정되어있을 경우 필드로 사용하면 된다.

ORM을 View에서 사용하기

shell_plus에서 사용했던 ORM 문법을 사용할 수 있다.

# views.py
def post_list(req, id):
    posts = models.Post.objects.all()
    post = posts.get(id=id)
    return render(req, 
                  'blog/post.html', 
                  {
                      'title' : post.title,
                      'content': post.content
                  })
<!-- post.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h2>{{ title }}</h2>
    <p>{{ content }}</p>
</body>
</html>

  • blog/post/2/ 도메인으로 이동하면 해당 id의 데이터를 받아올 수 있다.

ORM QuerySet 조회 예제

# title에 This가 포함된 post 추출
Post.objects.all().filter(title__contains='This')
# title에 This가 포함된 post 제외
Post.objects.all().exclude(title__contains='This')
# 여러 조건으로 조회: 아래와 같이 여러 조건을 걸어 데이터를 조회할 수 있다.
Post.objects.filter(title__contains='this', title__endswith='title')
Post.objects.filter(title__contains='this').filter(title__endswith='title')
# or and 연산자 사용
from django.db.models import Q
Post.objects.filter(Q(title__contains='this') | Q(title__endswith='title'))
Post.objects.filter(Q(title__contains='this') & Q(title__endswith='title'))
# title에 대소문자 구분 없이 this가 포함된 post 추출
Post.objects.filter(title__icontains=’this’)
# title이 this로 시작하는/끝나는 post 추출
Post.objects.filter(title__startswith=’This’)
Post.objects.filter(title__endswith=’title’)
# id가 1, 2, 3인 post 추출
Post.objects.filter(id__in=[ 1, 2, 3])
# id가 1~9인 post 추출
Post.objects.filter(id__range=( 1,10))
# 날짜 관련
Post.objects.filter(published_at__lt=timezone.now())
Post.objects.filter(published_at__gte=timezone.now())
Post.objects.filter(created_at__year=2019)
Post.objects.filter(created_at__month=5)
Post.objects.filter(created_at__day=21)
Post.objects.filter(published_at__isnull= True)
  • filter에서 컬럼명과 필터 이름을 묶을때 언더바 2개를 사용한다는 것에 주의하자.
post-custom-banner

0개의 댓글