모델은 데이터에 대한 하나의 정보 소스입니다. 모델은 저장하고 있는 데이터의 필수적인 필드
와 동작(메소드)
을 포함하고 있어요.
일반적으로 각각의 모델은 하나의 데이터베이스 테이블에 Mapping
된답니다.
기본사항:
django.db.models.Model
의 subclasses입니다.queryset
이라고 해요.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
은 모델의 필드에요. 각 필드는 클래스 속성으로 지정되어 있으며, 각 속성은 각 데이터베이스의 column
에 매핑됩니다.
위의 Person
모델은 아래와 같이 데이터베이스 테이블을 생성합니다.
CREATE TABLE myapp_person (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
);ame" varchar(30) NOT NULL
);
데이터베이스 필드 목록
필드는 클래스 속성으로 지정됩니다. clean
, save
또는 delete
같이 모델 API 이름과 중복되게 설정되지 않도록 하세요. 아니면 충돌나요.
예제
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 클래스의 인스턴스 여야합니다. Django는 필드 클래스 유형을 사용하여 몇 가지를 결정합니다.
저장할 데이터의 종류 (예 : INTEGER, VARCHAR, TEXT)를 데이터베이스에 알려줍니다. 즉. 테이블 컬럼의 유형을 결졍하겠조?
렌더링 시 사용할 HTML widget 양식을 정해요. (예. <input type="text">, <select>
)
Django admin에서 사용되거나 다른 유효성 검사에서 사용되어지는 부분이에요.
각 필드는 키워드 arguments 형식을 이용할 수 있어요. 예를 들어, CharField (및 하위 클래스)에는 데이터를 저장하는 데 사용되는 VARCHAR 데이터베이스 필드의 크기를 지정하는 max_length 인수가 필수적으로 필요합니다.
True면 Django는 데이터베이스에 빈 값을 NULL로 저장합니다. 기본값은 False입니다.
True 인 경우 필드를 비워 둘 수 있습니다. 기본값은 False입니다.
이것은 null과 다른데요. null은 순전히 데이터베이스와 관련이있는 반면 blank는 유효성 검사와 관련
됩니다.
필드에 blank = True
widget(양식) 유효성 검사에서 빈 값을 입력 할 수 있어요.
필드에 blank = False
가있는 경우 해당 필드는 반드시 작성되어야해요.
이 필드에 대한 선택으로 사용할 - 튜플 시퀀스형이에요.
원래 기본적으로 표준 텍스트 필드 형식으로 제공 되던 위젯이 select box
로 기본 형식이 됩니다.
YEAR_IN_SCHOOL_CHOICES = [
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
]
선택 순서가 변경 될 때마다 new migration
이 생성됩니다.
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'
좀더 간결한 방식인 enumeration class를 정의해서 사용할 수도 있어요.
from django.db import models
class Runner(models.Model):
MedalType = models.TextChoices('MedalType', 'GOLD SILVER BRONZE')
name = models.CharField(max_length=60)
medal = models.CharField(blank=True, choices=MedalType.choices, max_length=10)
필드 기본값입니다. default는 값
또는 호출 가능한 객체
일 수 있습니다.
호출 가능하면 새 객체가 생성 될 때마다 호출됩니다.
help_text
form widget
과 함께 표시되는 도움말?! 텍스트 양식이에요.
필드가 사용되지 않는 경우에도 도움말 형식으로 표시됩니다.
primary_key
만약 값이 True
일 경우 model에서 primary key
로 잡히게되요.
참고!!
primary key
는 읽기 전용입니다. 기존 객체의 기본 키 값을 변경 한 다음 저장하면 이전 객체와 함께 새 객체가 생성됩니다.
예를 들면 :
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한 값만 입력하도록 설정해줘요.
이 세 가지 <ForeignKey, ManyToManyField and OneToOneField>
필드를 제외하고는 첫 번째 위치 인자 값으로 verbose name
을 사용할 수 있게되요.
만약 따로 설정하지 않으면 장고에서 기본값으로 필드 이름을 밑줄을 공백으로 바꿔 사용해 자동으로 생성해요.
요놈은 첫 번째 인자에 명시된 문자열로 verbose name
이 지정된거고요.
first_name = models.CharField("person's first name", max_length=30)
아래에서는 verbose name
이 'first name'
으로 잡혀있어요
first_name = models.CharField(max_length=30)
일대일, 일대다, 다대다의 verbose name?
명시적으로 키워드 인자로 지정해줘야해요. 위치인자로 정할 수 없습니다. 이유는 첫번째 인자로 클래스가 들어가기 때문이쥬~~!
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",
)
분명히 관계형 데이터베이스의 힘은 테이블을 서로 관련시키는 데 있습니다. Django는 가장 일반적인 세 가지 유형의 데이터베이스 관계를 정의하는 방법을 제공합니다.
다대일 관계를 정의하기 위해 django.db.model.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)
# ...
다대다 관계를 정의하려면 ManyToManyField
를 사용하십시오. 다른 필드 유형과 마찬가지로 모델의 클래스 속성으로 포함하여 사용합니다.
ManyToManyField
에는 위치 인수, 즉 모델이 관련된 클래스가 필요합니다.
예를 들어 피자에 여러 개의 토핑 개체가있는 경우, 즉 토핑은 여러 개의 피자에 있고 각 피자에는 여러 개의 토핑이있을 수 있습니다.이를 나타내는 방법은 다음과 같습니다.
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
ForeignKey와 마찬가지로 재귀 관계 (자체에 다 대다 관계가있는 개체) 및 아직 정의되지 않은 모델에 대한 관계를 만들 수도 있습니다.
ManyToManyField
의 이름은 관련 모델 객체 세트를 설명하는 복수형이어야하지만 필수는 아닙니다.
Note!!
ManyToManyField
어느 모델에다가 정의해도 상관 없지만 단! 딱 하나의 모델에만 정의해야합니다.
모델 2개에다가 중복해서 넣으면 안되요~~
위의 예에서 토핑이있는 피자를 생각하는 것이 더 자연 스럽기 때문에 피자클래스 안에 topping 속성이 있습니다. 위의 설정 방식에서 피자 양식을 사용하면 사용자가 토핑을 선택할 수 있게되는 방식이에요.
피자와 토핑을 mixing 및 matching하는 것과 같은 ManyToMany 관계만 다루는 경우 표준 ManyToManyField 만 있으면됩니다. 그러나 때로는 데이터를 두 모델 간의 관계와 연관시켜야 할 수도 있습니다.
예를 들어 뮤지션이 속한 음악 그룹을 추적하는 애플리케이션의 경우를 생각해보십시오. 개인과 구성원이 속한 그룹간에 다 대다 관계가 있으므로 ManyToManyField를 사용하여이 관계를 나타낼 수 있습니다. 그러나 그 사람이 그룹에 가입 한 날짜와 같이 수집 할 수있는 멤버십에 대한 많은 세부 정보가 있습니다.
이러한 상황에서 Django를 사용하면 다 대다 관계를 관리하는 데 사용할 모델을 지정할 수 있습니다. 그런 다음 중간 모델에 추가 필드를 넣을 수 있습니다. 중간 모델은 매개자 역할을 할 모델을 가리 키기 위해 through
인수를 사용하여 anyToManyField
와 연결됩니다. 뮤지션 예제의 경우 코드는 다음과 같습니다.
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)
중간에서 경유 할 수 있는 모델을 직접 만들 때는, 관계를 가지는 두 모델에 대한 ForeignKey필드를 선언하고 추가적인 필드를 선언하면 된다.
중간에서 경유 할 수 있는 모델 생성시 유의 사항
중간 경유 모델
은 반드시 한개의 소스 모델의 foreign key를 포함하거나(위 예시의 경우 Group이 된다) ManyToManyField.through_fields를 사용하여 장고가 사용할 foreign key 를 명시해줘야한다. 만약 한개 이상의 foreign key가 사용되었는데 through_fields에 명시되지 않았다면 에러가 발생한다.
모델이 자신이 스스로를 중재 모델을 통해 many-to-many 관계를 가질 경우(recursive) 두 개의 foreign key를 가지는 것이 허용된다 그러나 이 foreign key들은 각각 다른 모델에서의 many-to-many 관계로 취급될 것이다. (마찬가지로 through_fields를 통해 명시해줘야 한다)
모델에서 스스로 many-to-many 관계를 정의하려 할 때에는 symmetrical=False를 사용해야한다.
예제
>>> 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>]>
필수 필드 지정에 through_defaults
를 지정하는 한 add(), create () 또는 set ()
을 사용하여 관계를 만들 수도 있습니다.
>>> beatles.members.add(john, through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.create(name="George Harrison", through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.set([john, paul, ringo, george], through_defaults={'date_joined': date(1960, 8, 1)})
중간 모델의 인스턴스를 직접 생성하는 것이 좋습니다.
중간 모델에 의해 정의 된 사용자 정의 관통 테이블이 (model1, model2) 쌍에 unique를 적용하지 않고 여러 값을 허용하는 경우 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 deletes both of the intermediate model instances for Ringo Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>
clear()
메소드는 모든 다대다 관계의 인스턴스를 삭제해요.
>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>
다 대다 관계를 설정하면 쿼리를 실행할 수 있습니다. 일반적인 다 대다 관계와 마찬가지로 다 대다 관련 모델의 속성을 사용하여 쿼리를 짤 수 있습니다.
# 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 정보에 액세스해야하는 경우 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 객체에서 다 대다 역 관계를 쿼리하는 것입니다.
>>> 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.'
일대일 관계를 정의하려면 OneToOneField
를 사용하게되요.
다른 필드 유형과 마찬가지로 모델의 클래스 속성으로 포함하여 사용합니다.
이것은 객체가 어떤 방식으로든 다른 객체를 "확장"할 때 객체의 기본 키에서 가장 유용합니다.
OneToOneField
에는 위치 인수, 즉 모델이 관련된 클래스가 필요합니다.
예를 들어, "장소"의 데이터베이스를 구축하는 경우 데이터베이스에 주소, 전화 번호 등과 같은 매우 표준적인 항목을 구축합니다. 그런 다음 레스토랑 모델에서 자신을 반복하고 해당 필드를 복제하는 대신 장소 위에 레스토랑 데이터베이스를 구축하려면 레스토랑이 OneToOneField to Place를 갖도록 만들 수 있습니다. 사실,이를 처리하기 위해 일반적으로 암시적 일대일 관계를 포함하는 상속을 사용합니다.
ForeignKey
와 마찬가지로 재귀적 관계를 정의 할 수 있고 아직 정의되지 않은 모델에 대한 참조를 만들 수 있습니다.
OneToOneField
field는 optional
로 parent_link
인자를 사용할 수 있습니다.
OneToOneField
클래스는 자동적으로 모델상에서 primary key
가 됩니다. primary_key 인자를 사용할 수 있지만 필요하지 않습니다. 따라서 단일 모델에 OneToOneField 유형의 여러 필드를 포함 할 수 있습니다.
다른 앱에서 정의된 모델을 가져와 일대일, 다대일, 다대다 필드 타입 인자 값으로 지정할 수 있어요.
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,
)
Django는 모델 필드 이름에 몇 가지 제한을 둡니다.
class Example(models.Model):
pass = models.IntegerField() # 'pass' is a reserved word!
class Example(models.Model):
foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
다음과 같이 내부 클래스 Meta를 사용하여 모델 메타 데이터를 제공합니다.
from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ["horn_length"]
verbose_name_plural = "oxen"
모델 메타 데이터는 정렬 옵션 (순서 지정), 데이터베이스 테이블 이름 (db_table) 또는 사람이 읽을 수있는 단 복수 이름 (verbose_name 및 verbose_name_plural)과 같은 "필드가 아닌 모든 것"라고 보면되요.
모델에 Meta 클래스를 추가하는 것은 완전히 선택 사항입니다.
objects
기본적으로 모델의 필드
는 모두 모델 속성으로 정의한다고 했어요. 그런데 모델 속성 중에서 예외적 필드, 즉 테이블의 column
으로 mapping되지 않는 속성이 있는데 바로 Manager
속성이에요.
모든 모델은 반드시 Manager
속성을 가져야 해요. 만일 정의하지 않으면 기본값으로 그 이름은 objects
가 되요.
또 Manager 속성은 Model Class를 통해서만 접근되고 모델 객체(인스턴스)를 통해서는 액세스 할 수 없어요.
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)
__str__()
모든 객체의 문자열 표현을 반환하는 Python "매직 메서드"입니다. 이것은 Python과 Django가 모델 인스턴스를 강제 변환하고 일반 문자열로 표시해야 할 때마다 사용하는 것입니다. 특히 이것은 대화 형 콘솔이나 관리자에서 개체를 표시 할 때 발생합니다.
항상이 방법을 정의하고 싶을 것입니다. 기본값은 전혀 도움이되지 않습니다.
get_absolute_url
요 메소드는 Django에게 객체의 URL을 계산하는 방법을 알려줍니다. Django는 관리자 인터페이스에서 이것을 사용하며 객체의 URL을 알아 내야 할 때마다 사용합니다.
고유하게 식별하는 URL이있는 모든 개체는이 메서드를 정의해야합니다.
사용자가 지정하여 데이터베이스 동작을 캡슐화하는 다른 모델 메서드들이 있습니다. 특히 save()
및 delete()
작동 방식을 자주 변경하고 싶을 것입니다.
위 메서드 (및 다른 모델 메서드)를 커스터마이징하여 작동 방식을 변경할 수 있습니다.
기본 제공 메서드를 재정의하는 고전적인 사용 사례는 객체를 저장할 때마다 어떤 일이 발생하기를 원하는 경우입니다. 아래 save()
를 사용하여 커스터마이징 해봤어요.
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()
if/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.
객체가 데이터베이스에 계속 저장되도록하려면 super().save(* args, ** kwargs) 수퍼 클래스 메서드를 호출하는 것을 기억하는 것이 중요해요. 수퍼 클래스 메서드를 호출하는 것을 잊은 경우 기본 동작이 발생하지 않고 데이터베이스가 변경되지 않습니다.
모델 메서드에 인수를 전달하는 것도 중요합니다.
(*args, **kwargs)
Django는 때때로 내장 모델 메소드의 기능을 확장하여 새로운 인수를 추가합니다. 메서드 정의에서 *args, **kwargs를 사용하면 해당 인수가 추가 될 때 코드에서 해당 인수를 자동으로 지원합니다.
또 다른 일반적인 패턴은 모델 메서드 및 모듈 수준 메서드에서 사용자 지정 SQL 문을 작성하는 것입니다.
Django에서 SQL 사용
Django의 모델 상속은 Python에서 일반 클래스 상속이 작동하는 방식과 거의 동일하게 작동하지만 페이지 시작 부분의 기본 사항은 여전히 따라야합니다. 즉, 기본 클래스는 django.db.models.Model을 하위 클래스로 지정해야합니다.
당신이 내려야 할 유일한 결정은
부모 모델이 자신의 데이터베이스 테이블을 가진 모델이되기를 원하는지
아니면
부모가 단지 자식 모델을 통해서만 볼 수있는 공통 정보의 소유자인지 여부입니다.
Django에서는 3가지 방식의 상속 유형이 있어요.
종종 부모 클래스를 사용하여 각 자식 모델에 대해 입력 할 필요가없는 정보를 보유하고 싶을 것입니다. 이 클래스는 분리되어 사용되지 않으므로 추상 기본 클래스가 필요합니다.
기존 모델 (아마도 완전히 다른 애플리케이션의 모델)을 서브 클래 싱하고 각 모델이 자체 데이터베이스 테이블을 갖도록하려면 다중 테이블 상속이 적합합니다.
마지막으로 모델 필드를 변경하지 않고 모델의 Python 수준 동작 만 수정하려는 경우 프록시 모델을 사용할 수 있습니다.
기타 심화과정으로 아래 내용들이 있지만 추후 다룰 기회가 있을때 포스팅 해볼게요~