티스토리 뷰

Django

localLibrary 모델(models.py) 편집

editor752 2020. 8. 4. 15:49

2020/08/03 - [Django] - localLibrary 뼈대 만들기

 

localLibrary 뼈대 만들기

본 내용은 Django 튜토리얼 사이트를 공부하면서 내용을 정리한 것이다. TOC 0. 선행 과제 1. 프로젝트 생성 2. 애플리케이션 생성 3. 애플리케이션 등록 4. 애플리케이션 연결 5. 테스트 선행 과제 djan

editor752.tistory.com

본 내용은 Django 튜토리얼 사이트를 공부하면서 내용을 정리한 것이다.

 

TOC

  1. 데이터베이스 디자인
  2. 모델 정의하기
  3. locallibrary의 모델 정의

앞에서 locallibrary 프로젝트의 앱인 catalog를 생성하는 과정에서 models.py 파일이 자동으로 생성되었었다. 간단히 언급한 바와 같이 models.py은 해당 애플레키이션의 데이터베이스 구조와 관련된 내용을 담고 있다. 따라서 본 강의에서는 앱에서 사용할 데이터베이스를 디자인하고 이를 models.py에 담는 과정을 알아보게 될 것이다. 즉 우리는 데이터베이스를 직접 제어할 필요가 없으며 단지 models.py를 편집하는 것으로 간단히 대체할 수 있다.

1. 데이터베이스 디자인

작업의 순서상 models.py를 편집하는 것은 후순위이다. 가장 먼저 고려해야 할 것은 이 앱에서 어떤 데이터들을 어떠한 방식(관계)으로 사용할 것인지 기획하는 일이다. 즉 데이데베이스를 디자인하는 일이다.

작지만 도서관 앱이라는 점을 잊지 않았다면 대강 다루어야 할 데이터(혹은 기능)들을 나열해 볼 수 있을 것이다. 먼저 ① 책에 관한 정보(제목, 요약, 저자, 작성된 언어, 분류, ISBN)를 저장할 필요가 있다. 다음은 도서관이 한 종의 책을 1권만 사는 것이 아니라는 점을 고려해야 한다. 하나의 서지정보를 가지는 책(종으로서의 책)이 여러 권(실물로서의 책)이 있을 수 있다는 점이다. 즉 ② 여러 개의 사본(책 인스턴스)을 사용할 수 있어야 한다(고유한 ID, 책의 가용성 상태 등). 그리고 ③ 책의 장르에 관한 데이터도 다루어야 할 것이다. 또한 ④ 저자에 대해서 그들의 이름 뿐만 아니라 더 많은 정보를 저장해야 할 수도 있다. 비슷한 이름이나 동명이인을 구별해야 하기 때문이다. 마지막으로 정보를 책 제목, 저자, 언어, 그리고 분류에 따라 ⑤ 정렬할 수도 있어야 할 것이다.

모델을 디자인할 때는 각각의 "객체(object: 관련된 정보의 모임)"를 분리된 모델로 구현하는 것이 효율적이다. 이 예시에서 명백히 확인할 수 있는 객체(object)들는 책, 책 인스턴스, 저자, 장르이다.

이들 데이터 간의 관계는 아래와 같이 도식화할 수 있다.

2. 모델 정의하기

위의 디자인을 models.py에 정의하는 단계이다. 이들은 django.db.models.Model의 서브 클래스로 구현되며 필드, 메소드 그리고 메타데이터를 포함할 수 있다. 아래의 코드 조각은 MyModelName라고 이름지어진 일반적인 모델의 경우이다.

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

2-1. 필드(Fields)

각 필드는 데이터베이스 목록(table)에 저장하길 원하는 데이터 열(column)에 해당한다. 각각의 데이터베이스 레코드(행, row)는 각 필드 값들 중 하나가 된다.

my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')

위의 코드에서는 my_field_name이라는 하나의 필드만을 가지며 models.CharField 타입(영숫자(alphanumeric) 문자열을 포함한다는 의미)이다. 필드 타입은 필드가 어떻게 저장되고 사용될지 지정하는 인자를 사용할 수 있다. 위에서는 필드에 두 가지 인자를 주었다.

  • max_length=20: 이 필드값의 최대 길이는 20로 제한함.
  • help_text='Enter field documentation': 이 값이 HTML 양식(form)에서 사용자들에게 입력될 때 어떤 값을 입력해야 하는지 사용자들에게 알려주기 위해 보여주는 텍스트 라벨을 제공함.

note: 필드 이름은 쿼리 및 탬플릿에서 이를 참조하는데 쓰인다. 필드는 또한 인자로 지정된 라벨(verbose_name)을 가지고 있거나, 또는 필드 변수 이름의 첫자를 대문자로 바꾸고 밑줄을 공백으로 바꿔서 기본 라벨을 추정할 수 있습니다(예를 들어 my_field_nameMy field name을 기본 라벨로 가지고 있습니다) .

note: 필드 타입들은 특정한 클래스들을 사용하여 등록되며, HTML 양식(form)에서 값을 수신할 때 사용할 유효성 검증 기준과 함께 데이터베이스에 데이터를 저장하는데 사용되는 레코드의 타입을 결정한다.

참고

A. 필드 타입

  • CharField: 작거나 중간 크기의 고정된 길이의 문자열을 정의할 때 사용합니다. 저장되기 위해서는 데이터의 최대 길이(max_length)를 정해주어야 합니다.

  • TextField: 임의의 긴 문자열에 사용됩니다. 필드의 최대 길이(max_length)를 지정해야 할 수도 있지만, 그것은 필드가 양식(form) 안에 표시될 때만 지정하면 됩니다(데이터베이스 레벨에서 강제되지 않습니다).

  • IntegerField: 정수값(모든 숫자)을 저장하는 필드입니다. 그리고 양식(form)에 입력된 값이 정수임을 검증하기도 합니다.

  • DateFieldDateTimeField: 날짜와 날짜시간 정보를 저장, 표현하는데 사용됩니다 (각각 파이썬 datetime.datedatetime.datetime 객체로). 이 필드들은 추가적으로 (서로 독점적인) auto_now=True (모델이 저장될 때 마다 필드를 현재 날짜로 설정하기 위해), auto_now_add (모델이 처음 생성되었을 때만 날짜를 설정하기 위해) , 그리고 default (사용자에 의해 변경될 수 있는 기본 날짜를 설정하기 위해) 매개 변수를 선언할 수 있습니다.

  • EmailField: 이메일 주소를 저장하고 검증하기 위해 사용합니다.

  • FileFieldImageField: 각각 파일과 이미지를 업로드하기 위해 사용됩니다 (ImageField는 단지 업로드된 파일이 이미지임을 확인하는 추가 검증을 더할 뿐입니다). 이것들은 업로드된 파일들이 어디에 어떻게 저장되는지 정의하는 매개 변수를 가집니다.

  • AutoField는 자동적으로 증가하는 IntegerField의 특별한 타입입니다. 이 타입의 primary key는 명시적으로 지정하지 않는 이상 모델에 자동적으로 추가됩니다.

  • ForeignKey: 다른 데이터베이스 모델과 일대다 관계를 지정하기 위해 사용됩니다 (예시: 차는 하나의 제조사를 갖고 있지만 제조사는 많은 차들을 만들 수 있습니다). 일대다에서 "일"쪽이 key를 포함하는 모델입니다.

  • ManyToManyField: 다대다 관계를 지정하기 위해 사용됩니다 (예시: 책은 여러 장르를 가질 수 있고, 각각의 장르에도 많은 책들이 있습니다). 우리 예제인 도서관 어플리케이션에서는 이 필드를 ForeignKeys와 매우 유사하게 사용할 겁니다. 하지만 그룹 사이의 관계를 보여주기 위해서는 더욱 복잡한 방식으로 사용될 수 있습니다. 이것은 레코드가 삭제됐을 때 어떤 일이 일어나는지 정의하기 위해 on_delete 매개변수를 가집니다 (예시: models.SET_NULL의 값은 단순히 NULL값으로 설정될 겁니다).

B. 필드 인자

  • help_text: 위에서 다뤘던 것 처럼, HTML 양식(form)에 대해 텍스트 라벨을 제공합니다 (예시 : 관리자 사이트).
  • verbose_name: 필드 라벨 안에서 사용되는 인간이 읽을 수 있는 필드 이름입니다. 지정되지 않았다면, 장고가 기본 verbose_name을 필드 이름으로부터 유추합니다.
  • default: 필드를 위한 기본값입니다. 이것은 값 또는 호출 가능한 객체일 수 있습니다. 이때 객체는 새로운 레코드가 생성될 때 마다 호출됩니다.
  • null: 만약 True라면, 장고는 빈 NULL 값을 필드를 위한 데이터베이스에 저장할 것입니다(CharField는 대신 빈 문자열을 저장할 것입니다). 기본값은 False입니다.
  • blank: 만약 True라면, 필드는 양식(form) 안에서 비워두는 것이 허락됩니다. 기본값은 False이며, 이것은 장고의 양식(form) 검증이 값을 입력하도록 강제한다는 뜻입니다. 이것은 종종 null=True와 함께 사용됩니다. blank 값을 허락할 때, 데이터베이스에서도 공백값을 적절하게 표시할 수 있어야 하기 때문입니다.
  • choices: 필드를 위한 선택들의 모임입니다. 이 인수가 제공된다면, 대응하는 기본 양식(form) 위젯은 표준 텍스트 필드가 아닌 이 선택 항목을 가진 선택 상자입니다.
  • primary_key: 만약 True라면, 현재 필드를 모델의 primary key로 설정합니다(primary key는 모든 다른 테이블 레코드들을 고유하게 확인하도록 지정된 특별한 데이터베이스 열입니다). primary key로 지정된 필드가 없다면 장고가 자동적으로 이 목적의 필드를 추가합니다.

2-2. 메타데이터

여러가지 메타데이터 옵션들은 모델에 무슨 데이터베이스를 사용해야만 하는가 그리고 데이터가 어떻게 저장되는가를 제어한다(이것들은 모델을 기존 데이터베이스에 매핑할 때만 유용하다).

위 코드에서는 class Meta를 선언하여 모델에 대한 모델-레벨의 메타데이타를 선언하였다.

class Meta:
    ordering = ['-my_field_name']

이 메타데이터의 유용한 기능들 중 하나는 모델 타입을 쿼리(query)할 때 반환되는 기본 레코드 순서를 제어(정렬)하는 것이다. 위와 같이 필드 이름 목록의 일치 순서를 ordering 속성에 지정하면 된다. 반대로 정렬하고 싶다면 마이너스 기호(-)를 필드 이름 앞에 접두사로 붙이면 된다.
즉 아래와 같이 사용할 수 있다.

class Meta:
  ordering = ['title', '-pubdate']

먼저 첵 제목이 A~Z순으로 정렬되고 다음으로는 발행이 가장 최근 것부터 오래된 것 순으로 정렬된다.

2-3. 메서드

  • __str__(): 최소한, 모든 모델마다 표준 파이썬 클래스의 메소드인 __str__()을 정의하여 각각의 object가 사람이 읽을 수 있는 문자열을 반환(return)하도록 해야 한다. 이 문자열은 관리자 사이트에 있는 개별적인 레코드들을 보여주는 데 사용됩니다. 종종 이것은 모델에서 제목(title)이나 이름 필드(name field)를 반환할 것입니다.
def __str__(self):
    return self.field_name
  • get_absolute_url(): 웹사이트의 개별적인 모델 레코드들을 보여주기 위한 URL을 반환하는 메소드이다. 이 메소드를 정의했다면 장고는 관리자 사이트 안의 모델 레코드 수정 화면에 View on Site 버튼을 자동적으로 추가할 것이다.

    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)])

3. locallibrary의 모델 정의

locallibrary/catalog/models.py 파일을 편집기로 열고 아래의 코드를 차근차근 삽입한다. 페이지 상단의 표준 코드(boilerplate)는 이 모델이 상속받을 모델 기본 클래스 models.Model을 포함하는 models 모듈을 가져옵니다.

3-1. 장르 모델

이 모델은 책 카테고리에 관한 정보를 저장하는데 사용된다. 예를 들어 소설인지, 논픽션인지, 로맨스인지 등등. 책의 장르를 자유 텍스트나 선택 목록으로 만들지 않고 모델을 이용해 만들었다. 이는 가능한 데이터(각각의 장르)들이 하드코딩되기보다는 데이터베이스를 통해 관리되도록 하기 위해서이다.

class Genre(models.Model):
    """Model representing a book genre."""
    name = models.CharField(max_length=200, help_text='Enter a book genre (e.g. Science Fiction)')

    def __str__(self):
        """String for representing the Model object."""
        return self.name

하나의 CharField 필드(name)을 가지며, 이는 장르의 이름이 된다. 200자로 제한되어 있고 help_text를 가진다. 모델의 마지막에서 우리는__str__() 메소드가 선언되었다. 이 메소드는 특정한 레코드에 의해 정의된 장르의 이름을 반환합니다.

3-2. 책 모델

책 모델은 일반적으로 사용 가능한 책에 대한 모든 정보들을 보여준다. 하지만 대여 가능한 특정한 물리적 인스턴스(instance)복사본(copy)를 의미하지는 않는다. 모델은 CharField를 사용하여 책의 titleisbn을 나타낸다(isbn이 이름을 지정하지 않은 첫 번째 매개변수를 사용하여 라벨을 ISBN으로 지정한 것에 주목. 만약 그러지 않았다면 기본 라벨은 "Isbn"이었을 것이다). summery는 텍스트가 상당히 길 것이기 때문에 TextField를 사용한다.

from django.urls import reverse # Used to generate URLs by reversing the URL patterns

class Book(models.Model):
    """Model representing a book (but not a specific copy of a book)."""
    title = models.CharField(max_length=200)
    author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)

    # Foreign Key used because book can only have one author, but authors can have multiple books
    # Author as a string rather than object because it hasn't been declared yet in the file.
    summary = models.TextField(max_length=1000, help_text='Enter a brief description of the book')
    isbn = models.CharField('ISBN', max_length=13, help_text='13 Character <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>')

    # ManyToManyField used because genre can contain many books. Books can cover many genres.
    # Genre class has already been defined so we can specify the object above.
    genre = models.ManyToManyField(Genre, help_text='Select a genre for this book')

    # added
    language = models.ForeignKey('Language', on_delete=models.SET_NULL, null=True)

    def __str__(self):
        """String for representing the Model object."""
        return self.title

    def get_absolute_url(self):
        """Returns the url to access a detail record for this book."""
        return reverse('book-detail', args=[str(self.id)])

장르는 책이 여러 개의 장르를 가지고, 장르도 여러 개의 책을 가질 수 있는 다대다 필드(ManyToManyField)이다. 따라서 아래와 같이 정의되었다.

    genre = models.ManyToManyField(Genre, help_text='Select a genre for this book')

저자는 ForeignKey로 선언되었다. 따라서 각각의 책은 하나의 저자만 가질 수 있지만, 저자는 여러 개의 책들을 가질 수 있다(실제로는 책에 공저가 있는 경우가 있으나 이를 구현한 것은 아니다).

    author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
  • null=True: 어떤 저자도 선택되지 않았다면 데이터베이스에 Null 값을 저장하도록 함.
  • on_delete=models.SET_NULL: 관련된 저자 레코드가 삭제되었을 때 저자의 값을 Null로 설정함.

메소드 get_absolute_url()은 이 모델의 세부 레코드에 접근하는 데에 사용될 수 있는 URL을 반환한다. 이것이 작동하도록 하기 위해선 book-detail이라는 이름의 URL 매핑을 정의하고, 관련 뷰와 탬플릿을 정의해야 한다.

3-3. 책 인스턴스 모델

BookInstance은 특정한 책의 복사본을 나타낸다. 그리고 복사본이 사용 가능한지 여부, 언제 되돌려받을 수 있을지, 출판사(imprint) 또는 버전 세부 사항, 그리고 도서관 안에 있는 책의 고유 id에 대한 정보를 포함한다.

import uuid # Required for unique book instances

class BookInstance(models.Model):
    """Model representing a specific copy of a book (i.e. that can be borrowed from the library)."""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text='Unique ID for this particular book across whole library')
    book = models.ForeignKey('Book', on_delete=models.SET_NULL, null=True) 
    imprint = models.CharField(max_length=200)
    due_back = models.DateField(null=True, blank=True)

    LOAN_STATUS = (
        ('m', 'Maintenance'),
        ('o', 'On loan'),
        ('a', 'Available'),
        ('r', 'Reserved'),
    )

    status = models.CharField(
        max_length=1,
        choices=LOAN_STATUS,
        blank=True,
        default='m',
        help_text='Book availability',
    )

    class Meta:
        ordering = ['due_back']

    def __str__(self):
        """String for representing the Model object."""
        return f'{self.id} ({self.book.title})'
  • UUIDField: id 필드가 이 모델의 primary_key로 설정되는 데 사용됩니다. 이 타입의 필드는 각 인스턴스에 전역적으로 고유한 값을 할당합니다 (도서관에서 찾을 수 있는 모든 책 마다 하나씩).

  • DateField: due_back(만기일) 날짜에 사용됩니다 (책이 빌려지거나 유지 보수된 이후 사용할 수 있을 것으로 예상되는 날짜). 이 값은 blanknull이 될 수 있습니다(책을 사용할 수 있는 경우 필요).

  • 메타데이터 모델 (Class Meta): 레코드들이 쿼리에서 반환되었을 때 레코드들을 정렬하기 위해서 이 필드를 사용합니다.

  • status: 선택/선택 목록(choice/selection list)을 정의하는 CharField입니다. 보시다시피, 우리는 열쇠-값(key-value) 쌍의 튜플을 포함하는 튜플을 정의해서 choices인자에 전달합니다. 열쇠-값(key-value) 쌍에서 값(value)은 사용자가 선택할 수 있는 표시값인 반면, 열쇠(key)는 그 옵션이 선택되었을 때 실제로 저장되는 값입니다. 또한 책이 선반에 저장되기 전에는 사용할 수 없으므로 기본값인 'm'(유지 관리, maintenanace)을 설정했습니다.

3-4. 저자 모델

모델은 저자를 이름(first name), 성(last name), 생일, 그리고 (선택적으로) 사망일을 가진다고 정의한다. 기본적으로 __str__()name을 성(last name), 이름(first name) 순서로 반환한다. get_absolute_url() 메소드는 개별 저자를 나타내기 위한 URL을 가져오기 위해 author-detail URL 매핑을 반대로 한다.

class Author(models.Model):
    """Model representing an author."""
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    date_of_birth = models.DateField(null=True, blank=True)
    date_of_death = models.DateField('Died', null=True, blank=True)

    class Meta:
        ordering = ['last_name', 'first_name']

    def get_absolute_url(self):
        """Returns the url to access a particular author instance."""
        return reverse('author-detail', args=[str(self.id)])

    def __str__(self):
        """String for representing the Model object."""
        return f'{self.last_name}, {self.first_name}'

3-5. 언어 모델

책에 사용된 언어를 지정하는 모델이다.

class Language(models.Model):
    """Model representing a Language (e.g. English, French, Japanese, etc.)"""
    name = models.CharField(max_length=200,
                            help_text="Enter the book's natural language (e.g. English, French, Japanese etc.)")

    def __str__(self):
        """String for representing the Model object (in Admin site etc.)"""
        return self.name

여기까지 편집을 마쳤다면 아래와 같이 데이터베이스 마이그레이션을 해주어야 한다.

python3 manage.py makemigrations
python3 manage.py migrate

2020/08/13 - [Django] - localLibrary 관리자 페이지

 

localLibrary 관리자 페이지

2020/08/04 - [Django] - localLibrary 모델(models.py) 편집 localLibrary 모델(models.py) 편집 2020/08/03 - [Django] - localLibrary 뼈대 만들기 localLibrary 뼈대 만들기 본 내용은 Django 튜토리얼 사이트..

editor752.tistory.com

 


댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함
05-18 17:43