dbeaver
목차
1. Quick Start, 기본 예제
1.1. 멤버 변수
1.2. 멤버 함수
1.3. class Meta
2. 멤버변수 -> 테이블 간의 관계
2.1. 1:1 - OneToOneField
2.2. 1:N - ForeignKey
2.3. M:N - ManyToManyField
3. Model - QuerySet, SQL
3.1. QuerySet API - 기본 api
3.2. QuerySet API - lookup 필드 #__
3.3. QuerySet API - Q개체 #좀 더 깐깐하게
3.4. QuerySet API -> SQL
4. DBeaver로 데이터베이스 접속하기
웹에는 DB가 있다. 장고는 이를 쉽게 다루고 관리할 수 있게 해주는 Model이 있다. Model은 클래스의 일종(멤버변수, 멤버함수를 가짐)이며, 기본적인 형태는 다음과 같다.
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=255)
class Meta:
verbose_name_plural = "Authors"
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=255)
genre = models.CharField(max_length=255)
published_date = models.DateField()
author = models.ForeignKey(Author, on_delete=models.CASCADE)
class Meta:
ordering = ['-published_date']
verbose_name_plural = "Books"
def __str__(self):
return f"{self.title} by {self.author.name}"
하나의 모델은 하나의 데이터베이스 테이블을 나타낸다. 위의 Book 모델은 다음과 같은 DB 테이블로 나타낼 수 있다.
테이블 명은 app이름_class이름 형태인 소문자로 등록된다.
예제에서 models.py가 들어있는 app이름이 library라면
library_book이라는 이름의 테이블이 되는 것이다.
from django.db import models
class MyModel(models.Model):
name = models.CharField(max_length=100)
user = models.ForeignKey(User, on_delete=)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
max_length 옵션 필수지정
https://docs.djangoproject.com/en/5.0/ref/models/fields/#charfield
선택하는 것도 가능
from django.utils.translation import gettext_lazy as _
class RestaurantTable(models.Model):
class Weekday(models.TextChoices):
MONDAY = 'MON', _('월요일')
TUESDAY = 'TUE', _('화요일')
WEDNESDAY = 'WED', _('수요일')
THURSDAY = 'THU', _('목요일')
FRIDAY = 'FRI', _('금요일')
SATURDAY = 'SAT', _('토요일')
SUNDAY = 'SUN', _('일요일')
weekday = models.CharField(max_length=3, choices=Weekday.choices, default=Weekday.MONDAY)
max_digits : 필수옵션
decimal_places : 필수옵션
price = models.DecimalField(max_digits=2, decimal_places=2)
to 모델 : 필수지정
on_delete : 필수지정
realted_name : 반대방향 모델 인스턴스 호출 명
- db_index = True : 해당 필드를 인덱스 열로 설정
B-Tree 자료구조로 DB 인덱싱
# django\db\models\fields\__init__.py
def __init__(
self,
verbose_name=None,
name=None,
primary_key=False,
max_length=None,
unique=False,
blank=False,
null=False,
db_index=False,
rel=None,
default=NOT_PROVIDED,
editable=True,
serialize=True,
unique_for_date=None,
unique_for_month=None,
unique_for_year=None,
choices=None,
help_text="",
db_column=None,
db_tablespace=None,
auto_created=False,
validators=(),
error_messages=None,
db_comment=None,
db_default=NOT_PROVIDED,
):
from django.db import models
class YourModel(models.Model):
def get_absolute_url(self):
return reverse('app_name:url_name', args=[str(self.id)])
def __str__(self):
return f'{self.some_field}'
def get_absolute_url(self):
def __str__(self)
: 모델을 문자열로 표현할 때 사용할 값을 지정
: 대표적으로 print() 함수를 사용할 때 사용
: 모델 인스턴스를 template에서 표현할 때 나타나는 값
모델 class의 전반적인 명세에 대해 설명할 때 사용한다.
class Meta:
unique_together = ['product_name', 'price']
여기선 2개의 멤버변수(필드) 조합이 고유해야 한다는 것이다.
product_name | price
제품A | 1000원 (O)
제품A | 2000원 (X) bcz. product_name 중복
제품B | 1000원 (X) bcz. price 중복
제품B | 3000원 (O)
하나의 데이터베이스에는 여러 테이블이 있다. 이때 테이블 간의 관계는 중요하다. 그 관계는 3종류가 있는데 하나씩 알아보자.
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=100)
class Profile(models.Model):
person = models.OneToOneField(Person, on_delete=models.CASCADE)
age = models.IntegerField()
해당 테이블을 합치면 다음과 같다.
장고 ORM 명령으로는
persons = Person.objects.select_related('profile').all()
이는 아래의 SQL문과 동일하다.
SELECT * FROM "yourapp_person"
LEFT JOIN "yourapp_profile" ON "yourapp_person"."id" = "yourapp_profile"."person_id";
작가 1명이 N(여러)개의 책을 쓸 수 있다. N측에 ForeignKey로 나타내면 된다.
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
ForeignKey(to, on_delete)
to
: 1:N의 관계에서 '1의 모델(부모 모델)'을 적은다. N(자식 모델들)이 어디를 바라보는지 쓰는 것이다.
on_delete
: 관련 record를 삭제 할때 어떻게 할지 설정.
: 부모가 사라지면 자식들도 폭포수에 쓸려 내려가듯 같이 사라지면 CASCADE(주로 사용)
: 부모가 사라져도 자식은 남아있어야 한다면 SET_NULL을 사용
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
Book.objects.filter(author_id=1) : Book 테이블의 author_id 필드를 활용해 조회
Book.objects.filter(author__name='김철수') : Author 테이블까지 타고 올라가서, 부모 모델(Author)의 name 필드가 '김철수'인 것을 조회
이런 기본적인 조회 뿐만 아니라 역방향 조회도 가능하다. 부모 모델에선 '자식 모델명_set'(해당 예제에선 book_set)으로 조회도 가능한 것이다. Book 클래스를 다시 살펴보자.
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
다음과 같이 author.book_set.all()로 조회가능하다.
그런데 같은 클래스 내 다른 멤버변수가 같은 부모를 바라볼 때도 있다. 이땐 서로 그 값이 곂칠 수도 있다. 이를 방지하기 위해 개발자가 related_name 옵션으로 커스텀이 가능하다.
class Book(models.Model):
...
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='myapp_book_set')
다음과 같이 조회가능하다.
author_instance = Author.objects.get(pk=1)
books_by_author = author_instance.myapp_book_set.all()
이젠 두 테이블을 합쳐보자.
Django ORM문
from your_app.models import Author, Book
# Author와 Book을 Author의 ID와 Book의 Author_id를 기준으로 Left Join
result = Author.objects \
.filter(id=Book.objects.values('author_id')) \
.values('id', 'name', 'book__id', 'book__title')
SQL문
SELECT Author.id, Author.name, Book.id AS book_id, Book.title
FROM Author
LEFT JOIN Book ON Author.id = Book.author_id;
from django.db import models
class Student(models.Model):
name = models.CharField(max_length=100)
class Course(models.Model):
title = models.CharField(max_length=200)
students = models.ManyToManyField(Student)
ManyToManyField(to, blank)
to에 해당하는 클래스가 밑에 선언된다면 '클래스명'으로 선언한다. 위의 예제에서 MTM 관계를 Student 클래스에서 선언하고자 하면 다음과 같이 적으면 된다.
class Student(models.Model):
students_set = models.ManyToManyField('Student')
중간 테이블이 생성되는데 그 이름은 appname_table1_table2_set이다.
위의 경우 app이름이 xxx라면
xxx_student_course_set으로 생긴다.
이와 관련해서 추가적인 내용을 보충하고자 하면 through 옵션을 사용하면 된다.
DB에서 데이터를 가져올 때 사용하는 기본 쿼리를 살펴보자.
https://docs.djangoproject.com/ko/5.0/ref/models/querysets/
쿼리셋(개체들의 모음(list)) 반환 O
쿼리셋(개체들의 모음(list)) 반환 X, 개체 반환 O
default를 제외하고 전달
obj, created = MyModel.objects.get_or_create(
field_name='조건값',
defaults={'additional_field': '기본값'}
)
https://docs.djangoproject.com/ko/5.0/ref/models/querysets/#get-or-create
쿼리셋(개체들의 모음(list)) 반환 X, 개체 반환 O
from django.shortcuts import get_object_or_404
obj = get_object_or_404(YourModel, pk=object_id)
from yourapp.models import YourModel
# 모든 레코드 조회
all_records = YourModel.objects.all()
# 특정 조건에 맞는 레코드 조회 (예: id가 1인 레코드)
specific_record = YourModel.objects.get(id=1)
# 특정 조건에 맞는 레코드들 조회 (예: name이 'John'인 레코드들)
filtered_records = YourModel.objects.filter(name='John')
# 특정 조건에 맞는 레코드가 있는지 확인
exists = YourModel.objects.filter(name='Jane').exists()
# 정렬된 상태로 조회 (예: 나이를 기준으로 오름차순)
ordered_records = YourModel.objects.order_by('age')
# 특정 필드만 조회 (예: 이름 필드만 조회)
name_values = YourModel.objects.values_list('name', flat=True)
# 조건을 만족하는 레코드 개수 조회
record_count = YourModel.objects.filter(age__gte=18).count()
# 중복제거
queryset.distinct().all()
Book.objects.filter(published_date__gte=today)
lookup field를 사용하여 쿼리를 요청하는 예를 보자. 원하는 필드에 언더바 2개(__)를 붙이고 사용하면 된다.
# models.py
class RestaurantTable(models.Model):
restaurant = models.ForeignKey(
Restaurant, on_delete=models.CASCADE,
)
time = models.TimeField()
available = models.IntegerField()
# views.py에서 쿼리에 조건 넣어 조회
query_sets = Restaurant.objects.filter(visible=True).order_by('-created_at')
relation_conditions = relation_conditions & Q(restauranttable__time__gte=start_time)
Q : ORM에서 filter() 사용할 때 논리 조건 사용가능하게
ex1) Q(조건1) | Q(조건2)
ex2) Q(조건1) & Q(조건2)
from django.db.models import Q
from yourapp.models import YourModel
# Q 객체를 사용한 OR 연산 예제
query = Q(name='John') | Q(name='Jane')
or_records = YourModel.objects.filter(query)
# AND 연산
and_query = Q(age__gte=18) & Q(city='New York')
and_records = YourModel.objects.filter(and_query)
# NOT 연산
not_query = ~Q(name='Bob')
not_records = YourModel.objects.filter(not_query)
# 복잡한 조합 ( (A AND B) OR (C AND D) )
complex_query = (Q(age__gte=21) & Q(city='Los Angeles')) | (Q(age__gte=18) & Q(city='San Francisco'))
result_records = YourModel.objects.filter(complex_query)
장고는 ORM(Object-Relational Mapping : 데이터베이스와 상호작용하기 위한 파이썬 객체를 제공하는 도구)이다. ORM은 개발자가 SQL 쿼리를 직접 작성하지 않고도 데이터베이스와 상호 작용할 수 있게 해준다. 하지만 ORM에서의 DB 관련 명령어가 내부적으로는 어떤 SQL을 날리는지 알 필요는 있다. 프로젝트의 앱이름이 yourapp이라면
Book.objects.all()
SELECT * FROM yourapp_book;
query_set = Post.objects.filter(id=1)
str(query_set.query)
Book.objects.filter(published_date__gte=today)
SELECT * FROM yourapp_book WHERE published_date >= '현재날짜';
Book.objects.filter(
models.Q(genre='Fantasy') | models.Q(genre='Mystery')
)
SELECT * FROM yourapp_book WHERE genre = 'Fantasy' OR genre = 'Mystery';
Book.objects.order_by('-published_date')
SELECT * FROM yourapp_book ORDER BY published_date DESC;
Book.objects.values('title')
SELECT title FROM yourapp_book;
Book.objects.values('genre').annotate(count=Count('id'))
SELECT genre, COUNT(id) AS count FROM yourapp_book GROUP BY genre;
Author.objects.get(name='J.K. Rowling').book_set.all()
SELECT * FROM yourapp_book WHERE author_id = (SELECT id FROM yourapp_author WHERE name = 'J.K. Rowling');
배포 서버 내역이 아래와 같고,
IP Address : xxx
DB 컨테이너의 환경변수가 아래와 같으면
POSTGRES_HOST=xxx
POSTGRES_PORT=5432
POSTGRES_DB=xxx
POSTGRES_USER=xxx
POSTGRES_PASSWORD=xxx
DATABASE_URL=xxx
배포하는 서버의 내역이 다음과 같다면, SSH 탭에서 다음과 같이 설정하면 된다.
IP Address : xxx
Username : xxx
Password : xxx
그러면 다음과 같이 접속이 가능해진다.