본문 바로가기
Study/Django

Django Web framework 2.0 Topic 번역 - Model

by 블리드카가 2018. 1. 16.
728x90


Models

모델은 당신의 데이터를 표현해주는 객체입니다. 모델은 저장하고자하는 데이터의 field와 behavior를 담고 있습니다.(비즈니스 로직이 담겨 있다는 말이겠지요?) 모델의 맵은 단일 데이터베이스의 테이블로 생성됩니다.
The basics:
  • 모델은 django.db.models.Model 상속합니다.
  • 모델의 각 속성은 데이터베이스의 field 입니다. 
  • Django는 자동으로 생성된 데이터베이스 엑세스 API를 제공합니다.; 참조: Making queries.

Quick 예제

이 예제는  first_name 과 last_name을 가진 Person을 정의합니다.
from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)


first_name 과 last_name 은 모델의  fields 입니다. 각 field는 클래스 속성이며, 이러한 각 속성은 데이터베이스 column으로 매칭됩니다.

위 Person 모델은 아래와 같이 데이터베이스 테이블을 생성합니다.
CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

Some technical notes:
  • 테이블의 이름인 'myapp_person'은 기본적으로 앱명_클래스명으로 자동 생성되지만, Meta 옵션을 통해 재정의 될 수 있습니다.  참조 : Table names 
  • id field 는 자동적으로 추가됩니다. 이것 또한 재정의 될 수 있습니다. 참조: Automatic primary key fields.
  • 이 예제에서 사용된 CREATE TABLE 구문은 PostgreSQL 로 작성되어졌습니다. 다른 데이터베이스를 사용하신다면 그에 맞게 변환되어 사용됩니다.

models사용하기

모델을 정의한 후에 Django에게 모델을 사용하는 것을 알려야 합니다. 당신의 setting file에서 INSTALLED_APPS  항목에서 당신의 models.py가 포함되어 있는 앱모듈 명이 입력되어 있어야 합니다.
예를 들면, 당신의 application 모델은 myapp.models(manage.py startapp script에 의해 생성되어진 myapp) 모듈안에 있다면  INSTALLED_APPS 아래와 같이 읽혀집니다.
INSTALLED_APPS = [
    #...
    'myapp',
    #...
]

당신이 새로운 앱을 INSTALLED_APPS 에 추가 했을 때,  manage.py migrate 명령어를 실행하세요,  부가적으로 manage.py makemigrations 을 사용하세요.

Field

 가장 중요한 부분은 데이터베이스 필드 목록입니다. Field들은 class 속성으로 정의 됩니다. field이름을 선택할 때 models API명(clean,save, delete와 같은...)과 충돌되지 않게 유의 하세요.
Example:
from django.db import models

class Musician(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    instrument = models.CharField(max_length=100)

class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()

Field 타입

모델의 각 필드는 해당 Field  클래스의 인스턴스 여야합니다. Django는 field class type을 사용하여 몇 가지를 결정합니다.
  • 데이터베이스에 저장할 데이터 종류 (예 : INTEGER, VARCHAR, TEXT)를 나타내는 열 유형입니다.
  • form field를 렌더링할때 사용할 기본 HTML 위젯입니다. (e.g. <input type="text"><select>).
  • Django admin과 자동으로 생성된 forms에 사용되어진 최소한의 유효성 검증 입니다.

Django 내장 Field 타입 : model field reference.
Custom model field 작성법 : Writing custom model fields.

Field 옵션

각 필드는 고유한 특정인자를 가질 수 있습니다. 예를 들면 CharField는 데이터를 저장하기 위해 사용 되어진 VARCHAR 데이터베이스 필드의 길이를 특정하기 위해  max_length 를 요구합니다.
모든 Field타입의 공통 인자 또한 있습니다. 모두 선택사항입니다. 
Field 참조 : reference
아래는 자주 사용되지는 것들 입니다.
null
True이면, Django는 빈 값을 null로 저장할 것입니다.Default 는 False.
blank
True이면, blank값('',empty value) 를 허용합니다.. Default 는 False.
Note null 과는 다릅니다.null 은 데이터베이스와 관계되어지고, blank is 유효성과 관계되어집니다.  blank=True 면, form 유효성 체크는 빈값을 허용합니다. blank=False 이면 필수 필드가 됩니다.
choices
choices로 사용될 필드는 2중 반복가능한 tuple입니다. 이것이 사용된다면, 기본 form 위젯은  select 박스가 될 것입니다.
A choices list looks like this:
YEAR_IN_SCHOOL_CHOICES = (
    ('FR', 'Freshman'),
    ('SO', 'Sophomore'),
    ('JR', 'Junior'),
    ('SR', 'Senior'),
    ('GR', 'Graduate'),
)

각 tuple의 첫번째 요소는 데이터베이스 값으로 저장되어집니다. 두번째 요소는 form 위젯에 표시되어 집니다.
model instance에서 표시되는 값은 get_필드명_display() 메소드로 확인 할 수 있습니다.
from django.db import models

class Person(models.Model):
    SHIRT_SIZES = (
        ('S', 'Small'),
        ('M', 'Medium'),
        ('L', 'Large'),
    )
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)

>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'

default
field의 default 값입니다.  이것은 값일 수도 callable object일 수도 있습니다.callable이라면 매번 새롭게 생성되어진 object가 호출될 것입니다.
help_text
form 위젯에 도움말로 표시 됩니다. It’s useful for documentation even if your field isn’t used on a form.
primary_key
True이면, 이 field는 모델의 primary key 로 지정됩니다.
어떠한 필드도 primary_key=True  를 지정하지 않는다면, Django는 자동적으로 primary key로 지정된 IntegerField 를 추가할것입니다. 그래서 기본 primary-key를 재정의하지 않는다면, primary_key=True  를 지정할 필요 없습니다. 참고 : Automatic primary key fields.
 primary key field 는 읽기 전용입니다. 기존 개체의 기본 키 값을 변경 한 다음 저장하면 새 개체가 이전 개체와 함께 만들어집니다. For example:
from django.db import models

class Fruit(models.Model):
    name = models.CharField(max_length=100, primary_key=True)

>>> fruit = Fruit.objects.create(name='Apple')
>>> fruit.name = 'Pear'
>>> fruit.save()
>>> Fruit.objects.values_list('name', flat=True)
<QuerySet ['Apple', 'Pear']>

unique
True 이면,  테이블 필드 내에서 값이 유일 해야합니다.
상세 필드 옵션 참조 : common model field option reference.

자동 primary key fields

기본적으로 Django는 각 모델에 아래와 같은 field가 부여됩니다.
id = models.AutoField(primary_key=True)
이것 은 자동 증가되는 primary key 입니다.
primary key를 원하는 field에 따로 지정하려면, 작성한 field중에 하나에 primary_key=True 를 지정하세요. Django가 Field.primary_key를 명시적으로 설정했다고 판단하면 자동 id 열을 추가하지 않습니다.
각 모델은 정확하게  primary_key=True 를 가진 하나의 field를 요구합니다.(명시적으로 선언되거나 자동으로 추가되거나..)

Verbose field names

ForeignKey, ManyToManyField 및 OneToOneField를 제외한 모든 필드 유형은 선택적인 첫 번째 위치한 인자로 verbose_name 을 사용합니다. 자세한 이름이 주어지지 않으면 Django는 밑줄을 공백으로 변환하여 필드의 속성 이름을 사용하여 장황한 이름을 자동으로 만듭니다.
예를 들면 아래의 first_name의 verbose_name은   "person's first name" 입니다.
first_name = models.CharField("person's first name", max_length=30)
아래의 verbose_name은  "first name":
first_name = models.CharField(max_length=30)
ForeignKeyManyToManyField and OneToOneField 는 첫번째 위치한 인자는 model class 입니다., 그래서 verbose_name은 keyword 인자로 사용해야합니다.:
poll = models.ForeignKey(
    Poll,
    on_delete=models.CASCADE,
    verbose_name="the related poll",
)
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(
    Place,
    on_delete=models.CASCADE,
    verbose_name="related place",
)
convention 은  verbose_name의 첫 글자가 대문자가 아닙니다.

Relationships(관계)

명확하게, 관계형 데이터베이스의 힘은 테이블간에 연결에 있습니다. Django는 다대일, 다대다, 일대일의 3가지 일반적인 유형을 정의하는 방법을 제공합니다.

다대일 관계 Many-to-one relationships

다대일 관계를 정의하기 위하여,  django.db.models.ForeignKey를 사용하세요. 다른 field 유형과 같이 모델 클래스의 속성으로 포함하여 사용합니다.
ForeignKey 는 첫번째 인자로 모델과 관련된 클래스를 위치인자로 요구합니다. 
예를들면 Car 모델은 Manufacturer를 가집니다. Manufacturer는 다수의 Car를 만듭니다. 하나의 Car는 하나의 Manufacturer를 가집니다.
from django.db import models

class Manufacturer(models.Model):
    # ...
    pass

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    # ...

또한 재귀 관계 (자체와 다 대일 관계가있는 객체)와 아직 정의되지 않은 모델과의 관계를 만들 수 있습니다. 참조 : the model field reference

class Car(models.Model):
    company_that_makes_it = models.ForeignKey(
        Manufacturer,
        on_delete=models.CASCADE,
    )
    # ...

See also
ForeignKey fields 의 기타 인자 :  the model field reference
연결된 모델 검색 상세 :  Following relationships backward example.
다대일 관계 예제 : Many-to-one relationship model example.

다대다 관계 Many-to-many relationships

다대다 관계를 사용하기 위해서,  ManyToManyField를 사용하세요다른 field 유형과 같이 모델 클래스의 속성으로 포함하여 사용합니다.
ManyToManyField 첫번째 인자로 모델과 관련된 클래스를 위치인자로 요구합니다.
예를들면 Pizza는 다수의 Topping 을 가진다면,  토핑 또한 다수의 Pizza에 있을 수 있습니다.
from django.db import models

class Topping(models.Model):
    # ...
    pass

class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)

또한 재귀 관계 (자체와 다 대일 관계가있는 객체)와 아직 정의되지 않은 모델과의 관계를 만들 수 있습니다. 참조 : the model field reference
ManyToManyField 의 이름은 (toppings와 같이) 복수형으로 제안되지만 필수는 아닙니다.
다대다 관계 모델에서 어떠한 모델이 ManyToManyField를 가지건 상관 없습니다. 하지만 하나의 모델에는 입력해야합니다. 양쪽은 아닙니다.
See also
다대다 관계 예제 : Many-to-many relationship model example 
ManyToManyField 필드 추가 옵션 참고 : the model field reference

다대다 관계상에서 추가 필드 

단순한 다대다 관계에서 pizzas와 toppings와 같은 기본적인 ManyToManyField 만 필요합니다. 하지만 때때로 두 모델 사이에 관계를 연결해야할 필요가 있을 수 있습니다.
예를 들면 뮤지션이 속한 뮤지컬 그룹을 트래킹 하는 어플리케이션을 고려하는 경우, 사람과 그룹 사이에는 다대다 관계 입니다. 이 때 당신은 이 관계를 표현하기 위해 ManyToManyField를 사용할 수 있습니다.그러나  사람이 그룹에 조인한 시점과 같은 정보를 수집하기를 원하는 경우가 있을 수 있습니다.
이러한 상황 들에서 Django는 다대다 관계에서 특정 모델을 지정하는 것을 허용합니다. 그리고 이 중간 모델에 추가 필드를 입력할 수 있습니다.  이러한 중간 모델은 ManyToManyField 에서  through 인자를 통해 연결됩니다. 아래의 뮤지션 예제를 확인하세요.
from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)

    def __str__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

    def __str__(self):
        return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)


중간 모델을 설정할 때, 명시적으로 다대다 관계에 관여하는 모델에 foreign key를 설정합니다. 이러한 명시적 선언이 두 모델이 어떻게 연결되었는지를 정의합니다.
중간모델의 몇 가지 제약사항
  • 중간 모델은 원본 모델에 대한 오직 하나의 foreign key를 가지거나(이 예제에서는 Group), Django가 ManyToManyField의 through_fields. 옵션을 사용하여 foreign key를 명확하게 지정 해야합니다. 만약 하나 이상의 foreign key를 가지고 있고, through_fields 가 지정되지 않았다면, validation error가 발생할 것입니다. 비슷한 제약사항이 대상 모델의 foreign key에 대해서도 적용됩니다. (이 예제에서는 Person)
  • 중간 모델을 통해 다대다 관계를 가지는 모델의 경우 동일 모델에 대해 두 개의 foreign key가 허용 됩니다. 그러나 이 것은 다대다 관계의 두가지 측면으로 다루어 집니다. 두 개 이상의 foreign key를 가진다면 위와 같이 through_fields 또한 지정해야하거나 그렇지 않으면 validation error를 발생시킬 것입니다.
  • 재귀적으로 다대다 관계를 설정할 때는 symmetrical=False  옵션을 사용해야합니다.(참조 : the model field reference).
이제 중간 모델을 사용하여  ManyToManyField를 설정했기에(이 경우는 Membership) 다대다 관계를 시작할 준비가 되었습니다.  중간 모델의 인스턴스를 만들고 아래를 수행합니다.
>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
...     date_joined=date(1962, 8, 16),
...     invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()
<QuerySet [<Group: The Beatles>]>
>>> m2 = Membership.objects.create(person=paul, group=beatles,
...     date_joined=date(1960, 8, 1),
...     invite_reason="Wanted to form a band.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>

일반적인 다대다 field 와 달리 관계를 생성 하기 위해 add()create(), or set()을 이용할 수 없습니다.
>>> # The following statements will not work
>>> beatles.members.add(john)
>>> beatles.members.create(name="George Harrison")
>>> beatles.members.set([john, paul, ringo, george])

왜일까요? 단순히 Person과 Group사이에 관계를 생성할수 없습니다.  Membership 모델에 필요한 모든 세부사항을 지정할 필요가 있습니다. add, create, set 호출은 추가 세부사항을 지정할 방법을 제공하지 않습니다. 중간 모델을 사용하는 다대다 관계에서는 사용이 불가능 합니다.이러한 관계유형을 만드는 유일한 방법은 중간모델의 instance를 만드는 것입니다.
remove() 메소드도 유사한 이유로 사용불가능입니다. 
>>> Membership.objects.create(person=ringo, group=beatles,
...     date_joined=date(1968, 9, 4),
...     invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This will not work because it cannot tell which membership to remove
>>> beatles.members.remove(ringo)

그러나 clear() 메소드를 통해 다대다 관계 모두를 지울 수 있습니다.
>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>

중간 모델을 통해 다대다 관계를 설정했을 때 query들을 실행할 수 있습니다. 일반적인 다대다 관계와 마찬가지로 다대다 관련 모델의 특성을 이용한 쿼리를 할 수 있습니다.
# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
<QuerySet [<Group: The Beatles>]>

중간 모델을 사용할 때 해당 속성에 대한 쿼리 또한 할 수 있습니다. 
# Find all the members of the Beatles that joined after 1 Jan 1961
>>> Person.objects.filter(
...     group__name='The Beatles',
...     membership__date_joined__gt=date(1961,1,1))
<QuerySet [<Person: Ringo Starr]>

만약  멤버쉽 정보가 필요하다면 Membership 모델에 직접 쿼리할 수 있습니다.
>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'

동일 정보에 접근하는 다른 방법으로 Person object를 통해 다대다 관계 역방향으로 쿼리를 실행하는 것입니다.
>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'

일대일 관계 One-to-one relationships

일대일 관계를 정의 하려면 OneToOneField를 사용하세요. 
To define a one-to-one relationship, use OneToOneField다른 필드 유형과 마찬가지로 모델의 클래스 속성으로 포함하여 사용합니다.
이것은 객체가 어떤 방식으로든 다른 객체를 확장할 때 객체의 기본키에서 가장 유용합니다.
OneToOneField 또한 위치 인자로 관계덴 모델 클래스를 요구합니다.
예를들면 "places"라는 데이터베이스를 구축했다면, 주소, 전화번호 기타등등의 기본적인 내용을 데이터베이스 상에 만들 것입니다.
그런 다음 만약 places 상에 restaurants 데이터베이스를 구축하기를 원한다면, Restaurant모델에 해당필드들을 복제하는 대신에  Place모델과 일대일 관계를 가질 수 있습니다.(왜냐하면 restaurant는 place중 하나입니다. 사실상 이것을 다루기 위해 상속을 사용하며 암시적인 일대일 관계를 가집니다.
 ForeignKey와 마찬가지로, 재귀관계를 정의 할수 있고,  아직 정의되지 않은 모델에 대한 참조 또한 만들어질 수 있습니다. 
OneToOneField  field는 선택적으로 parent_link 인자를 사용할 수 있습니다.
OneToOneField 클래스는 자동적으로 모델상에서 primary key가 됩니다. primary_key 인자를 사용할 수 있지만 필요하지 않습니다. 따라서 단일 모델에 OneToOneField  유형의 여러 필드를 포함 할 수 있습니다.

여러 파일의 모델 Models across files

모델을 다른앱의 모델과 연결하는 것은 물론 가능 합니다.  이렇게 하기 위해 관계된 모델을 file  상단에 import 하세요. 그리고 나서 필요한 곳에서 다른 모델을 참조하세요.
from django.db import models
from geography.models import ZipCode

class Restaurant(models.Model):
    # ...
    zip_code = models.ForeignKey(
        ZipCode,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )

Field 이름 제약 사항Field name restrictions

Django는 모델의 field이름에 두가지 제약사항이 있습니다.
  1. Python 예약어는 사용할수 없습니다. Python syntax error를 발생시킵니다. 예제 :
    class Example(models.Model):
        pass = models.IntegerField() # 'pass' is a reserved word!

  2. 한개를 초과하는  underscore 문자를 포함할 수 없습니다.Django의 쿼리 조회 구문의 작동 방식에 기인 합니다.예제 :
    class Example(models.Model):
        foo__bar = models.IntegerField() # 'foo__bar' has two underscores!

이러한 제약사항은 field이름이 데이터베이스 컬럼 이름과 같을 필요가 없기 때문에 해결할 수 있습니다. db_column 옵션을 참고하세요.

Custom field types

자체 필드 유형을 만들수 있습니다.
참고 자료 : Writing custom model fields.

Meta options

내부 클래스 Meta를 사용해 모델의 metadata를 설정할 수 있습니다. 
from django.db import models

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

모델 metadat는 field가 아닌 모든 것입니다.  정렬옵션(ordering),  테이블명(db_table), 단수 복수명 (verbose_name verbose_name_plural), 필수 사항은 없고, 선택적으로 Meta 클래스를 추가하면 됩니다.
Meta option 참조 : model option reference.

Model attributes

objects
model의 가장 중요한 속성은 Manager입니다. Django models에 제공되어지는 데이터베이스 쿼리 작동 인터페이스이며, 데이터베이스에서 인스턴스를 검색하는데 사용되어집니다. custom Manager가 정의된 것이 없다면, 기본 이름은 objects 입니다. Manager은 model class에서만 엑세스 가능하고 인스턴스에서는 가능하지 않습니다.

Model methods

모델에 '행수준' 의 커스텀한 기능을 추가하기 위해 모델에 custom한 메소드를 정의 하세요.  Manager 메소드는 테이블 전반적인 것을 다루는 메소드 이지만, Model 메소드는 Model 인스턴스에 한해 부분적으로 작동해야합니다.
이것은 비즈니스 로직을 한곳에 유지하기 위한 유용한 기술입니다. - 모델상에서.
예제
from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()

    def baby_boomer_status(self):
        "Returns the person's baby-boomer status."
        import datetime
        if self.birth_date < datetime.date(1945, 8, 1):
            return "Pre-boomer"
        elif self.birth_date < datetime.date(1965, 1, 1):
            return "Baby boomer"
        else:
            return "Post-boomer"

    @property
    def full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)

예제의 마지막 메소드는 property 이다.
 모델 인스턴스 참조에는 각 모델에 자동적으로 부여된 모든 메소드를 가지고 있습니다. 이러한 메소드들을 재정의 할수 있습니다.
아래는 자주 재정의 하는 두 가지
__str__()
모든 객체의 문자열을 표시해주는 파이썬의 마법 매소드. 이것은 콘솔이나 관리자페이지에서 표시되는 문자열
get_absolute_url()
이 메소드는 Django에게 모델 객체의 URL을 알려줍니다.Django 관리자 인터페이스에서 사용되고 object에서 URL을 설명할 필요가 있을 때 언제든지 사용됩니다.
URL을 고유하게 식별하는 객체는 이 메소드를 정의 해야합니다. 

미리 정의된 모델 메소드 오버라이딩 Overriding predefined model methods

커스터마이징 하기 원하는 또다른 캡슐화된 model methods들이 있습니다. 자주 save() 와 delete() 작업은 변경을 원합니다.
이러한 메소드들은 변경이 자유롭습니다.
내장메소드들을 오버라이딩 하는 일반적인 케이스는 모델을 저장할때마다 무언가 다른것을 원하는 경우입니다.예제 :
from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        do_something()
        super().save(*args, **kwargs)  # Call the "real" save() method.
        do_something_else()

저장을 금지하는 경우:
from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        if self.name == "Yoko Ono's blog":
            return # Yoko shall never have her own blog!
        else:
            super().save(*args, **kwargs)  # Call the "real" save() method.

객체가 데이터베이스에 여전히 저장되기 위해  superclass(부모 클래스) 메소드를 호출(super().save(*args, **kwargs) 와 같이)을 기억하는 것은 중요합니다. superclass(부모 클래스) 메소드 호출을 잊으면 데이터베이스를 저장하는 기본 동작이 수행되지 않습니다.
또한 모델 메소드에 전달할 수있는  인자를 전달하는 것이 중요합니다. Django는 때때로 내장된 모델메소드를 확장하여 새로운 인자를 추가 합니다. 만약 메소드 정의에서 *args, **kwargs을 사용한다면, 추가된 인자도 자동으로 지원되는 것을 보장합니다.
Overridden model methods are not called on bulk operations
Note that the delete() method for an object is not necessarily called when deleting objects in bulk using a QuerySet or as a result of a cascading delete. To ensure customized delete logic gets executed, you can use pre_delete and/or post_delete signals.
Unfortunately, there isn’t a workaround when creating or updating objects in bulk, since none of save(),pre_save, and post_save are called.

커스텀 SQL 실행 Executing custom SQL

참고 : using raw SQL.

Model 상속 Model inheritance

Django에서 Model의 상속은 파이썬 클래스의 일반적인 클래스의 상속과 거의 동일하게 동작합니다. 하지만 현재 페이지의 시작부분 basic 항목은 여전히 준수되어집니다. 그 의미는 상속되는 base클래스는 django.db.models.Model.을 상속받아야합니다.
오로지 자신만의 권한을 가진 모델이 되기 위한 부모모델을 원하거나 부모 모델이 자식 모델을 통해 보여지는 공통 정보만을 보유하는 보유자가 될지를 결정해야 합니다.
Django에서는 3가지 스타일로 상속할수 있습니다.
  1. 흔희  자식모델에서 타이핑 하기를 원치않는 정보를 포함 된 부모클래스를 원하는 경우가 있습니다. 이러한 고립되어 사용되지 않을 것입니다. 참조 : Abstract base classes 
  2. 존재하는 모델을 상속하게 되거나 자신의 데이터베이스 테이블을 가지는 각 모델을 원한다면 이글을 참조하세요 : Multi-table inheritance 
  3. 마지막으로, 모델 field변경 없이 모델의 파이썬 레벨의 행동만 수정하기를 원한다면 이 글을 참조하세요: Proxy models.

Abstract base classes

Abstract base class는 다수의 모델에 공통정보를 입력하기를 원할 때 유용합니다.  base class를 작성하고 Meta class에 abstract=True를 입력합니다. 이 모델은 데이터베이스 모델을 만드는데 사용되지 않습니다. 다른 모델의 base class로 사용될 때 그것의 field는 각 자식 클래스에 추가되어질 것입니다. abstract base class 와 동일한 이름의 field를 가지면 Django가 예외를 발생시킬 것입니다.
예제 :
from django.db import models

class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()

    class Meta:
        abstract = True

class Student(CommonInfo):
    home_group = models.CharField(max_length=5)

Student 모델은 name, age 그리고 home_group 필드를 가집니다. CommonInfo 모델은 일반적인 모델로서 사용되어질 수 없습니다. 테이블 생성 되지않고, 인스턴스화나 저장할 수 없습니다.

Meta 상속 Meta inheritance

abstract base class 을 생성했을 때, Django는 기본 클래스에서 선언되어진 속성으로서 유용한 Meta 내부 클래스를  사용할수 있게 만듭니다. 자식 클래스가 자신의 Meta 클래스를 선언하지 않는 다면, 부모의 Meta를 상속할 것입니다. 자식 클래스가 부모의 Meta클래스를 확장하기를 원한다면 그것을 상속할 수 있습니다. 예제 :
from django.db import models

class CommonInfo(models.Model):
    # ...
    class Meta:
        abstract = True
        ordering = ['name']

class Student(CommonInfo):
    # ...
    class Meta(CommonInfo.Meta):
        db_table = 'student_info'

Django 는 abstract base class의 Meta 클래스를 조정합니다. Meta 속성이 작성되기 전에 abstract=False 기본값으로 설정합니다. 이것은 자식클래스가 자동으로 abstract가 되지 않는 것을 의미 합니다. 물론 당신은 다른 abstract base class를 상속함으로써 abstract base class를 만들 수 있습니다. 그렇게 하려면 명시적으로 abstract=True 를 설정해야합니다.
일부 속성은 추상 기본 클래스의 메타 클래스에 포함하는 것이 타당하지 않습니다. 예를 들어, db_table을 포함하면 모든 자식 클래스 (자신의 메타를 지정하지 않은 클래스)가 동일한 데이터베이스 테이블을 사용한다는 것을 의미합니다. 

related_name 과 related_query_name에 유의하기

Be careful with related_name and related_query_name

related_name 또는 related_query_name 을 ForeignKey 또는 ManyToManyField 상에서 사용하기를 원한다면, 고유한 이름을 사용해야합니다. 대게 abstract base class를 사용하는데 있어 문제가 발생합니다. 해당 필드가 각 필드에  정확히 같은 이름으로 자식클래스에 포함되기 때문입니다.  (including related_name andrelated_query_name) .
이 문제를 해결하기 위하여, abstract base class에서  related_name 또는 related_query_name 을 사용할 때 값의 한부분으로 '%(app_label)s' 과 '%(class)s'을 포함해야합니다.
  • '%(class)s'는 소문자화 된 자식 클래스의 이름으로 대치 됩니다.
  • '%(app_label)s'은 자식 클래스가 속한 앱의 이름으로 대치 됩니다. 

    각 인스톨된 어플리케이션 이름은 유니크 하며 그 안에 모델 클래스 이름 또한 유니크 합니다. 그러므로 항상 다른이름 으로 출력될 수 있습니다. 
For example, given an app common/models.py:
from django.db import models

class Base(models.Model):
    m2m = models.ManyToManyField(
        OtherModel,
        related_name="%(app_label)s_%(class)s_related",
        related_query_name="%(app_label)s_%(class)ss",
    )

    class Meta:
        abstract = True

class ChildA(Base):
    pass

class ChildB(Base):
    pass

다른 앱에 속한 rare/models.py:
from common.models import Base

class ChildB(Base):
    pass

common앱에 ChildA 클래스 m2m  related_name : common_childa_related
                         relate_query_name : common_childas
common앱에 ChildB 클래스 m2m  related_name : common_childb_related
                         relate_query_name : common_childbs
rare앱에 ChildA 클래스 m2m  related_name : rare_childb_related
                         relate_query_name : rare_childbs
'%(class)s' 와 '%(app_label)s'사용하는 방법은 당신에게 달렸습니다. 만약 사용하지 않는다면, Django는 system 체크를 수행할 때 또는 migrate가 실행될 때 에러가 발생될 것입니다.
related_name 속성을 abstract base class의 필드에서 지정하지 않는다면 기본적으로 자식클래스 이름에 '_set'가 붙는 형태로 될 것입니다. 
예를 들면 위 코드에서 ChildA의 경우 childa_set, ChildB의경우 childb_set가 됩니다.

다중 테이블 상속 Multi-table inheritance

Django가 지원하는 상속모델의 두번째 타입은 계층 구조의 각각의 모델이 모두 하나하나의 개별적인 모델일 경우 입니다. 각 모델은 자신의 데이터베이스 테이블에 대응 되고 쿼리가 수행될수 있고, 개별적으로 데이터를 생성할 수 있습니다. 이러한 상속 관계는 자식 모델과 부모 모델 사이에 연결을 도입합니다. (자동으로 생성된 OneToOneField를 통해서)
For example:
from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

Place의 모든 필드는 데이터가 다른 데이터베이스 테이블에 있음에도 불구하고 Restaurant에서 또한 사용될 수 있습니다. 

>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")

Restaurant 가 있는 하나의 Place가 있다면 Place객체로 부터 모델이름을 소문자화한 이름으로 Restaurant객체를 얻을수 있습니다. 
>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>

Place에 연결된 Restaurant에서 자동 생성된  OneToOneField 이것과 같습니다.
place_ptr = models.OneToOneField(
    Place, on_delete=models.CASCADE,
    parent_link=True,
)

Restaurant에   parent_link=True 값을 가진 OneToOneField 를 선언함으로써 필드를 재정의 할 수 있습니다.

Meta and multi-table inheritance

multi-table 상속 상황에서, 자식 클래스가 부모의 Meta 클래스로부터 상속받는 것은 의미가 없습니다. 모든 Meta 옵션은 이미 부모클래스에서 적용되어 졌으며, 이를 다시 자식 클래스에 적용하면 모순된 행동으로 나타납니다.(이것은 기본 클래스가 존재하지 않는 abstract base class 와 대조적입니다.)
그래서 자식 모델은 부모의 Meta 클래스에 접근할 수 없습니다. 그러나 부모로부터 자식이 상속하는 몇가지 경우가 있습니다. ordering 속성 또는  get_latest_by 속성을 지정하지 않으면 그 특성들은 부모로부터 상속될 것입니다.
class ChildModel(ParentModel):
    # ...
    class Meta:
        # Remove parent's ordering effect
        ordering = []

Inheritance and reverse relations

multi-table 상속은 암시적으로 부모와 자식 사이에 OneToOneField  연결을 사용하기 때문에 위의 예제와 같이 부모에서 자식으로 이동할 수 있습니다. 그러나 이것은 ForeignKey 와 ManyToManyField 관계의 기본 related_name 값의 이름을 사용합니다. 부모 모델을 상속하는 관계 형태에서 이러한 관계( ForeignKey 와 ManyToManyField )를 넣고자 한다면 related_name 을 지정해야만 합니다. 이를 잊을 경우 Django는 에러를 발생시킬 것입니다.
예제
class Supplier(Place):
    customers = models.ManyToManyField(Place)

에러 결과:
Reverse query name for 'Supplier.customers' clashes with reverse query
name for 'Supplier.place_ptr'.

HINT: Add or change a related_name argument to the definition for
'Supplier.customers' or 'Supplier.place_ptr'.

customers필드에 related_name을 추가함으로 써 에러를 해결할 수 있습니다.
models.ManyToManyField(Place,related_name='provider').

부모 link field 지정하기 Specifying the parent link field

앞서 언급과 같이 Django는 자동적으로 자식클래스를 추상클래스가 아닌 부모모델에 연결하기 위하여  OneToOneField 를 만듭니다. 연결되는 속성의 이름을 제어하고자 한다면 직접 당신의 필드가 부모클래스의 링크임을 나타내기 위하여 OneToOneField 에 parent_link=True 옵션을 지정하여 생성할 수 있습니다. 

Proxy models

multi-table 상속을 사용했을 때 각 자식 클래스 마다 새로운 데이터베이스 테이블이 상속되어 집니다. 이것은 대게 원하는 동작 입니다. 그러나 때로는 모델의 파이썬 동작만 변경하기를 원하기도 합니다. default manager 변경 또는 새 메소드 추가 같은..
이러한 것이 proxy model 입니다. 원래 모델에 대한 proxy를 만듭니다.  proxy model instance를 통해 생성, 삭제, 수정 과 같은 동작을 할 수 있으며 원래모델을 사용하는 것과 같이 데이터가 저장되어질 것입니다. 차이점은 원본 수정 없이 기본 모델 순서 또는 기본 manager등을 proxy에서 변경할 수 있습니다.
프록시 모델은 일반적인 모델과 같이 선언합니다. 그리고 Meta 클래스 proxy 속성을 True값을 지정 합니다.
예제:
from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class MyPerson(Person):
    class Meta:
        proxy = True

    def do_something(self):
        # ...
        pass

MyPerson 클래스는 상위 Person 클래스와 동일한 데이터베이스 테이블에서 작동합니다. 특히 Person의 새로운 인스턴스는 MyPerson을 통해 액세스 할 수 있으며 그 반대의 경우도 가능합니다.
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>

부모의 모델과 다른 정렬을 사용하기 위해 proxy모델을 사용할 수 있습니다. 항상 Person모델을 정렬하기를 원하지 않을 것입니다. 프록시를 사용할때 규칙적으로 last_name에 의한 정렬을 할 수 있습니다. 
class OrderedPerson(Person):
    class Meta:
        ordering = ["last_name"]
        proxy = True

Person은 정렬되어 있지 않고 OrderPerson은 last_name에 의해 정렬됩니다. 
Now normal Person queries will be unordered and OrderedPerson queries will be ordered by last_name.
Proxy Meta 속성 상속 일반 모델 같은 방법으로 상속 합니다. 참조 :in the same way as regular models.

QuerySets still return the model that was requested

There is no way to have Django return, say, a MyPerson object whenever you query for Person objects. A queryset for Person objects will return those types of objects. The whole point of proxy objects is that code relying on the original Person will use those and your own code can use the extensions you included (that no other code is relying on anyway). It is not a way to replace the Person (or any other) model everywhere with something of your own creation.

Base class restrictions

proxy 모델은 정확히 하나의 비 추상 모델 클래스를 상속 받아야합니다. 여러 비추상 모델을 상속 받을수 없습니다. 프록시 모델은 모델 필드를 정의 하지 않는 여러개의 추상 클래스는 상속할 수 있습니다. 프록시 모델은 공통의 비추상 부모클래스를 공유하는 여러개의 proxy 모델 또한 상속 받을 수 있습니다.

Proxy model managers

proxy 모델에서 어떠한 model manager를 지정하지 않는다면, 모델의 부모로부터 manager를 상속 받을 것입니다. proxy모델 상에서 manager를 정의한다면,  부모 클래스 상에서 정의 된 어떠한 manager가 유효하더라도, 새롭게 정의된 manager가 기본 manager가 될 것입니다. 
위를 통해 우리의 예제를 계속하면 Person 모델을 질의 했을 때 사용되는 기본 manager를 변경할 수 있습니다.
from django.db import models

class NewManager(models.Manager):
    # ...
    pass

class MyPerson(Person):
    objects = NewManager()

    class Meta:
        proxy = True

기본 manager를 교체함 없이 Proxy에 새로운 manager를 추가하고 싶다면,  custom manager 문서에서 기술된 테크닉을 사용할 수 있습니다. 새로운 manager가 포함된 base 클래스를 작성하고, 주된 기본 클래스 다음에 상속하십시오.
# Create an abstract class for the new manager.
class ExtraManagers(models.Model):
    secondary = NewManager()

    class Meta:
        abstract = True

class MyPerson(Person, ExtraManagers):
    class Meta:
        proxy = True


Differences between proxy inheritance and unmanaged models

Proxy 모델 상속은 Meta 클래스의 managed 속성 False값을 사용하여 관리되지 않는 모델을 생성하는 것과 유사하게 보일 것입니다.
Meta.db_table의 설정하면 존재하는 모델을 숨기는 관리되지 않는 모델을 생성할수 있고, 그것에 Python 메소드를 추가할 수 있습니다. 그러나 변경사항이 있는 경우 두 복사본을 동기화해야할 필요가 있기 때문에 반복적이고 오류가 생길수 있습니다.
반면에 proxy 모델은 프록시 중인 모델과 정확하게 동일하게 행동합니다.그것은 직접적으로 필드와 manager를 상속하기 때문에 부모 모델과항상 동기화 됩니다.
일반적인 규칙:
  1. 기존 모델 또는 데이터베이스 테이블을 미러링 하고 모든 데이터베이스 칼럼들을 원하지 않는다면, Meta클래스에 managed=False 옵션을 사용하세요. 이 옵션은 일반 적으로 Django가 제어하지 않는 뷰나 테이블을 모델링할 때 유용합니다.
  2. 모델의 파이썬 전용 동작을 변경하려고 한다면, 하지만 원본과 동일한 필드를 모두 유지하려면 Meta.proxy = True를 사용하십시오. 이렇게하면 데이터를 저장할 때 프록시 모델이 원본 모델의 저장소 구조와 정확히 일치하도록 설정됩니다.

다중 상속 Multiple inheritance

파이썬의 상속과 같이 Django 모델은 다중 상속이 가능 합니다. 일반적인 파이썬 이름 규칙을 적용된다는 것을 명심하세요. 특정 이름의 첫번째 베이스 클래스가 사용됩니다. 예를 들면 야러 부모 Meta 클래스가 포함이 되면 오직 첫번째만 사용되고 나머지 다른 것은 무시되는 것을 의미 합니다.
일반적으로 다중 상속을 사용하지 않아도 됩니다. 유용한 사용케이스인 mix-in 클래스 입니다. mix-in을 상속받는 모든 클래스에 특정 추가필드나 메소드를 추가하는 것입니다. 상속 계층을 최대한 단순 직관적으로 유지하여 특정정보가 어디에서 왔는지 알아내려고 노력하지 않아도 되도록 노력하세요.
공통의 id primary key 필드를 가진 다수의 모델을 상속 받는다면 에러를 발생시킬 것입니다. 다중 상속을 적절히 사용하기 위해 base 클래스에서 명시적으로 AutoField 를 사용할 수 있습니다.
class Article(models.Model):
    article_id = models.AutoField(primary_key=True)
    ...

class Book(models.Model):
    book_id = models.AutoField(primary_key=True)
    ...

class BookReview(Book, Article):
    pass

또는  AutoField를 유지하는 공통의 조상을 사용합니다.  이렇게하려면 자동으로 생성되고 하위에서 상속되는 필드 사이의 충돌을 피하기 위해 각 상위 모델에서 공통 조상으로 명시적으로 OneToOneField 를 사용해야합니다.
class Piece(models.Model):
    pass

class Article(Piece):
    article_piece = models.OneToOneField(Piece, on_delete=models.CASCADE, parent_link=True)
    ...

class Book(Piece):
    book_piece = models.OneToOneField(Piece, on_delete=models.CASCADE, parent_link=True)
    ...

class BookReview(Book, Article):
    pass

필드 이름 'hiding'는 허용되지 않습니다.

Field name “hiding” is not permitted

일반적인 파이썬 클래스 상속에서 자식클래스는 부모클래스의 어떠한 속성도 재정의할 수 있습니다. Django에서는 모델 필드에서는 허용되지 않습니다. 비추상적인 모델 베이스 클래스에서 author라는 필드가 있다면 해당 클래스로부터 상속받은 어떠한 클래스에서도 author라고 호출되어지는 속성을 생성하거나 정의할 수 없습니다.
이러한 제약사항은 abstract 모델을 상속하는 필드에서는 적용되지 않습니다. 그러한 필드는 다른 필드 또는 값으로 재정의 되거나 fiend_name = None이라는 형태로 제거 됩니다.

Warning
Model manager는 abstract base class들로 부터 상속됩니다. 상속받은 Manager에 의해 참조되어지는 상속된 필드를 재정의 하는 경우 미묘한 버그를 발생시킬 수 있습니다. 참조 :  custom managers and model inheritance.
Note
일부 필드는 모델의 추가 속성을 정의 합니다. 예를 들면 ForeignKey 는 _id가 필드 이름으로 추가된 속성으로 foreign모델 상의 related_name 과 related_query_name 과 같이 정의 됩니다. 
이러한 추가 속성을 정의하는 필드가 변경되거나 제거되어 더 이상 추가 속성을 정의하지 않는 한 이러한 추가 속성을 겹쳐 쓸 수 없습니다.
부모 모델의 필드를 재정의 하는 것은 새로운 인스턴스를 초기화(Model.__init__에서 초기화되는 필드를 지정)하고 직렬화 하는데 어려움이 발생합니다. 이러한 것들은 일반 파이썬 클래스 상속이 동일한 방식으로 다룰 필요가 없는 특성입니다. 그래서 Django와 파이썬 클래스 상속의 차이는 독단적이지 않습니다.
이러한 제약사항은 Field 인스턴스 속성에만 적용됩니다. 원한다면 일반 파이썬 속성은 재정의 될수 있습니다. 또한 파이썬이 인식하는 속성의 이름에만 적용 됩니다 : 데이터베이스 컬럼 이름을 수동으로 지정한다면,multi table 상속을 받은 자식과 부모 모델에서 동일한 컬럼 이름을 가질수 있습니다. (그것들은 두개의 다른 데이터베이스 테이블의 컬럼입니다.)
Django는 조상모델에서 모델필드를 재정의 하면  FieldError 가 발생합니다.

패키지로 모델 구성하기 Organizing models in a package

manage.py startapp  명령어는 models.py 파일을 포함하는 어플리케이션 구조를 생성합니다.만약 많은 모델들을 가지고 있다면, 개별 파일로 그것을 구성하는 것이 유용합니다.
그렇게 하기 위해서는 model package를 생성 하세요. models.py를 지우고  __init__.py 파일과 model들을 저장할 파일을 포함한 myapp/models/ 디렉토리를 생성하고 __init__.py 파일에 import해야합니다.  
예제 :model 디렉토리에  organic.py 와 synthetic.py 가짐
myapp/models/__init__.py
from .organic import Person
from .synthetic import Robot

 .models import *를 사용하지 않고 명시 적으로 각 모델을 가져 오면 네임 스페이스가 복잡해지지 않아 코드를 읽기 쉽고 코드 분석 도구를 유용하게 유지할 수 있다는 이점이 있습니다..
참고자료 : The Models Reference ( 모델 필드, 관련 객체, 쿼리셋을 포함한 모델과 관련된 api전부)




728x90

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

Django Web framework 2.0 Topic 번역 - Django 설치  (0) 2018.01.15