LocalLibrary Model 디자인 하기
모델을 코딩하기 전에
우선 어떤 데이터를 저장할지,
그리고 다른 객체들에 대한 관계를
어떻게 지정할지에 대해 생각을 해봐야 한다.
이번 LocalLibrary 앱에는
책에 관한 정보들(제목, 저자, 분류 등)을 저장할 필요가 있다.
또한 모델을 디자인할 때 각각의
객체(관련된 정보의 모임) 마다
분리된 모델을 가지는 것이 좋을 것이다.
이 예시에서 확인할 수 있는 객체들은
책, 책 인스턴스, 저자이다.
이렇게 모델과 필드를 결정하고 나면
각 객체들간의 관계에 대해 생각해봐야 한다.
Django에서는 관계에 대해
일 대 일(OneToOneField),
일 대 다(ForeignKey),
다 대 다(ManyToManyField)
이 3가지로 설정할 수 있다.
mozilla 튜토리얼에서는
아래와 같이 이러한 모델에 대한
UML다이어그램을 제시해준다.
앞서 튜토리얼을 진행한 나로서는
Book, Author, BookInstance 이렇게
3가지 model이 나오지 않을 까 추측해본다.
모델 입문서
공식 도큐먼트의 튜토리얼에서 모델은 작성해봤지만
모델을 작성할 때의
각 요소에 대한 설명은 자세히 되어있지 않았던 것이
아쉬웠었다.
그런 나의 아쉬움에 대답을 해주듯이
이번 mozilla 튜토리얼에서는
만족할 만큼 꽤나 자세히 적혀있었다.
따라서 이 모델에 관한 쪽은
가능한 자세히 다루기로 하겠다.
①모델의 정의
Django에서 Model들은 보통 models.py 파일에서 정의된다.
이 Model들은
django.db.models.Model의 서브
클래스로 구현되며
필드, 메소드 그리고 메타데이터가 포함될 수 있다.
아래의
MyModelName클래스는
전형적인 model들의 구성을 보여준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | from django.db import models class MyModelName(models.Model): """A typical class defining a model, derived from the Model class.""" # Fields my_field_name = models.CharField(max_length=20, help_text='Enter field documentation') ... # Metadata class Meta: ordering = ['-my_field_name'] # Methods def get_absolute_url(self): """Returns the url to access a particular instance of MyModelName.""" return reverse('model-detail-view', args=[str(self.id)]) def __str__(self): """String for representing the MyModelName object (in Admin site etc.).""" return self.field_name | cs |
1)필드(Fields)
모델은 모든 자료형의 필드를 가질 수 있다.
각 필드는 데이터베이스에
저장하길 원하는 데이터 열(column)을 나타낸다.
각 데이터베이스 레코드는
각 필드 값들 중 하나로 구성되어 있다.
위의 예제 중의 필드 코드를 살펴보자.
|
my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')
|
cs
|
위의 MyModelName 이라는
모델은
model_my_field_name이라는
하나의 필드를 가지고 있고
model.CharField 타입 이다.
여러 자료형에 익숙한 개발자라면
이 필드는 문자열이 들어갈 변수라는 것을
추측해 볼 수 있을 것이다.
-
max_length = 20 : 필드의
길이를 설정한다.
model_my_field는 영문자 20글자 인것을 알 수 있다.
-
help_text = ' Enter field documentation'
:
HTML 양식(Form)에서 사용자들에게 입력될 때
어떤 값을
입력해야 하는지 알려주기위한 텍스트 라벨이다.
필드의 이름은 쿼리 및 템플릿에서 참조할 때 사용되며,
인수로 지정된 라벨을 가지고 있거나
필드 변수의 이름 첫자를 대문자로 바꾸고
밑줄을 공백으로 바꿔서 기본 라벨을 추정할 수 있다.
예제의 my_field_name의
경우
My field name을 기본 라벨로 가지고 있다.
2)일반적인 필드 인수
일반적으로 사용되는 필드의 인수는 아래와 같다.
-
help_text
: HTML 양식(Form)에 대해
텍스트 라벨을 제공한다.
-
verbose_name
: 필드 라벨 안에 사용되는 필드 이름이다.
지정되어있지 않는다면,
Django에서 지정한다.
(위의 경우는 My field name을 기본 인수로
가진다.)
-
default
: 필드를 위한 기본값이다.
여기에 들어가는 인수는 값 이나 객체 일 수
있다.
이때 객체는 새로운 레코드가 생성될 때 마다 호출 된다.
-
null
: null인수가 True라면,
Django는 NULL값을 레코드로 저장한다.
기본값은
False다.
-
blank
: blank 인수가 True라면,
해당 필드의 레코드 값을 비워두는 것을
허락한다.
기본값은 False이며,
이는 Django의 양식(Form)에 값을
입력하도록 강제한다는 것이다.
이는 종종 null = True와 함께
사용되는데
blank 값이 허용될 때,
DB에서도 공백값을 적절하게
표시 할 수 있어야하기 때문이다.
-
choices
: 필드를 위한 선택들의 모임이다.
이 인수가 제공된다면,
해당 기본
양식(form) 위젯은 표준 텍스트 필드가 아닌
이 선택 항목을 가진
선택 상자가 된다.
-
primary_key
: primary_key 인수가 True라면
현재 필드를 모델의 primary key로
설정한다.
만약 primary key로 지정된 필드가 없다면
Django가
자동적으로 primary key를
가지고 있는 필드를 생성한다.
이외의 필드는 아래의 공식 도큐먼트를 참고하길 바란다.
3) 일반적인 필드 타입
일반적인 필드 타입의 객체에 대해서는 아래와 같다.
-
CharField
: 작은 고정된 길이의 문자열을 정의할 때 사용한다.
저장하기 위해서는
max_length 인수를
정의해주어야 한다.
-
TextField
: 긴 문자열을 정의할 때 사용한다.
max_length
인수를 지정해야되지만,
필드가 양식(form)안에 표시될 때 지정해도
된다.
(DB 레벨에서는 강제되지 않기 때문)
-
IntegerField
: 정수값을 정의할 때 사용한다.
양식(form)에 입력된 값이 정수임을
검증하기도 한다.
-
DataField,
DateTimeField
: 날짜와 날짜 및 시간 정보를
저장, 표현하는데 사용 한다.
이
필드들은 모델이 저장될 때 마다 필드를 현재 날짜로 설정하기 위한 인수인
auto_now=True,
모델이
처음 생성되었을 때만 날짜를 설정하기 위한 인수인
auto_now_add,
그리고 사용자에 의해 변경될 수 있는 기본 날짜를 설정하기 위한
인수인
default를
선언할 수 있다.
-
EmailField
: 이메일 주소를 저장하고 검증하기 위해 사용한다.
-
FileField,
ImageField
: 파일과 이미지를 업로드하기 위해 사용된다.
ImageField는 단지 파일이
이미지임을 검증하는 기능이 추가로 있을 뿐이며,
이
필드들은 업로드된 파일들이 어디에 어떻게 저장되는지를
정의하는
매개변수를 가진다.
-
AutoField
: 자동적으로 증가하는 IntegerField의 특별한 타입이다.
이 타입의
primary key는 명시적으로 지정하지 않는 이상
모델에 자동적으로
추가된다.
-
ForeignKey
: 다른 model들과 일대다 관계를 지정하기 위해 사용된다.
일대다에서
"일"쪽이 key를 포함하는 모델이다.
-
ManyToManyField
: 다대다 관계를 지정하기 위해 사용된다.
(예시 : 책은 여러 장르를 가질
수 있고,
각각 장르에도 많은 책들이 있을 수 있다.)
예제인
도서관 애플리케이션에서는
이 필드를 ForeignKeys와 매우 유사하게
사용할 예정이다.
하지만, 그룹 사이의 관계를 보여주기
위해서는
더욱 복잡한 방식으로 사용될 수 있다.
이것은
레코드가 삭제됐을 때
어떤 일이 일어나는지 정의하기
위해
on_delete 매개변수를 가진다.
이 외에도 많은 타입의 필드에 대해서는 아래의
공식 도큐먼트 링크에서 참고하기 바란다.
4) 메타 데이터
아래와 같이 Class Meta를
선언하여
모델에 대한 모델-레벨에서 메타 데이터를 선언할 수 있다.
|
class Meta: ordering = ['-my_field_name']
|
cs
|
이 메타 데이터의 가장 유용한 기능 중 하나는
모델 타입의 쿼리문을 실행 할 때 반환되는
기본 레코드 순서를 제어할 수 있다는 것이다.
이렇게 하려면 위와 같이
필드 이름에 ordering 속성에 지정해야 한다.
순서는 필드의 타입에 따라 달라질 것이다.
위와 같이 오름 차순으로 정렬하고 싶다면
마이너스 기호(-)를 필드 이름 앞에 접두사로 붙이면 된다.
예를 들어,
기본적으로 아래와 같이 책들을 정렬하려고 한다면
|
ordering = ['title', '-pubdate']
|
cs
|
책들은 A-Z까지 알파벳 순으로 정렬되고,
그 후에는 제목(title)안에 있는 발행일 별로
가장 최근 것 부터 가장 오래된 것 순으로 정렬될 것 이다.
다른 일반적인 속성은
verbose_name이며,
단일 및 복수 형식의 클래스를 위한 이름 이다.
|
verbose_name = 'BetterName'
|
cs
|
이외의 모든 메타데이터 옵션에 대해서는
아래의 공식 도큐먼트 링크에서 참고하길 바란다.
② 메소드(Methods)
모든 모델 마다
표준 python 클래스인 __str__()을
정의하여
각 객체가 문자열을 리턴하도록 하자.
(java에서 생성자와 유사한 역활을 한다)
이 문자열은 관리자 사이트에 있는
개별적인 레코드를 보여주는 데 사용된다.
| def __str__(self): return self.field_name | cs |
Django의 model에 포함시켜야 할 메소드는
get_absolute_url()이다.
웹 사이트의 개별적인 Model의 레코드를
보여주기 위한 URL를 리턴해주는 메소드이다.
만약 이 메소드를 정의했다면
Django는 자동으로 관리자 사이트 안의
모델 레코드 수정 화면에
"View on Site"버튼을 자동적으로 추가하게 할 것이다.
아래의 코드에서 get_absolute_url()의
표준적인 사용 방법을 확인할 수 있다.
| def get_absolute_url(self): """Returns the url to access a particular instance of the model.""" return reverse('model-detail-view', args=[str(self.id)]) | cs |
만약 model의 개별적인 레코드를 보여주기 위해
/myapplication/mymodelname/2와 같은
URL을 사용한다고 가정해보자.
이 경우 응답과 id를 'model-detail-view'에 전달하기 위해
URL Mapper를 만들어야 한다.
위의 reverse()함수에 알맞은 포맷의
URL을 생성하기 위해서 URL Mapper를 반전 시킬 수 있다.
물론 위의 코드는 아직 까지
URL Mapping과 View, Template를
작성하지 않았기 때문에 작동하지는 않는다.
③ 모델 관리(management)
Model Class들을 정의한 이후에는
Class들을 사용해서 레코드를
생성, 업데이트, 삭제할 수 있고
모든 레코드와 레코드의
특정 하위 집합을 가져오기 위해
쿼리를 실행할 수도 있다.
튜토리얼에서 View를 정의할 때
방법을 보여줄 것이지만,
아래와 같이 간략한 요약하기로 한다.
1) 레코드의 생성과 수정
레코드를 생성하려면
Model의 인스턴스를 정의하고 save()를 호출 할 수 있다.
| # Create a new record using the model's constructor. record = MyModelName(my_field_name="Instance #1") # Save the object into the database. record.save() | cs |
만약 어떤 필드에서도
primary_key를 선언하지 않았다면,
새로운 레코드는 자동적으로
id라는 필드 이름을 가진 primary_key가 생성될 것이다.
위의 레코드를 저장한 후,
id 필드를 쿼리할 수 있는데
1의 값을 가질 것이다.
수정된 값들을 DB에 저장하기 위해서는
save() 메소드를 호출해야 한다.
| # Access model field values using Python attributes. print(record.id) # should return 1 for the first record. print(record.my_field_name) # should print 'Instance #1' # Change record by modifying the fields, then calling save(). record.my_field_name = "New Instance Name" record.save() | cs |
2) 레코드 검색하기
objects.all()을 사용하여 모델의 모든 레코드들을
QuerySet으로 가져올 수 있다.
QuerySet은 반복 가능한 객체이며,
이는 반복할 수 있는 많은 객체들을 포함하고 있다는 의미이다.
| all_books = Book.objects.all() | cs |
Django의 filter()는 리턴된 QuerySet이
지정된 문자 또는 숫자 필드를
특정 기준에 맞추어 필터링 할 수 있게 한다.
예를 들어 "wild"를 제목 안에 포함하는 책들을
필터링하여 그 숫자를 세어 보려면
아래와 같이 코딩하면 된다.
| wild_books = Book.objects.filter(title__contains='wild') number_wild_books = Book.objects.filter(title__contains='wild').count() | cs |
기준이 될 필드와 타입은
filter() 메소드 안의 매개 변수에 의해 정의된다.
field_name__match_type을 사용해서
위와 같이 대소문자를 구분하여 tilte을 필터링 할 수 있다.
대소문자를 구분하지 않는 icontains,
대소문자를 구분하지 않는 상태에서 정확히 일치하게 하는 iexact,
대소문자를 구분해 정확히 일치하게 하는 exact,
그리고 공식 도큐먼트 튜토리얼에서 정리했었던
아래와 같은 것들도 있다.
키워드 | 설명 |
__lt / __gt | 작다 / 크다 |
__lt / __gte | 작거나 같다/ 작거나 크다 |
__in | 리스트([1,2,3와 같은]) 의 데이터와 동일한 검색 |
__year / __month / __day | 년, 달, 일 검색 |
__isnull | Null인 값 검색 |
__contains | 지정한 문자열을 포함하는 데이터를 검색 |
__icontains | 지정한 문자열을 포함하는 데이터를 대소문자 구별하지 않고 검색 |
__startswith | 지정한 문자열로 시작하는 데이터를 검색 |
__istartswith | 지정한 문자열로 시작하는 데이터를 대소문자 구별하지 않고 검색 |
__range | 문자, 숫자, 날짜의 범위를 지정해 검색 |
이외의 인수에 대해서는
아래의 공식 도큐먼트를 참고하기 바란다.
또한 어떤 경우에는
일대다 관계를 다른 Model에 정의하는 필드를
필터링해야 할 때도 있다(ForeignKey의 경우)
이 경우에 추가적인 이중 밑줄을 사용하여
관련된 모델 안의 필드에 "색인(index)"할 수 있다.
예를 들어 특정한 장르 패턴을 가진 책들을 필터링하려면,
아래와 같이 genre 필드를 통해서
name에 색인(index)해야 한다.
| # Will match on: Fiction, Science fiction, non-fiction etc. books_containing_genre = Book.objects.filter(genre__name__icontains='fiction') | cs |
밑줄(__)을 사용하여 원하는 다양한 레벨의 관계
(ForeignKey / ManyToManyField)를 탐색할 수 있다.
예를 들어서
추가적인 "cover"관계를 사용하여 정의된
다른 타입의 Model Book은
다음과 같은 매개 변수 이름을 가질 것이다.
type__cover__name__exact='hard'
또한
관련된 모델의 역방향 검색, 필터 변경,
값의 더 작은 집합으로 리턴 등
쿼리로 할 수 있는 것들은 더 많이 존재 한다.
이외에 QuerySet에 대한 것들은
아래 링크의 공식 도큐먼트를 참고하길 바란다.