2018-02-05 TIL WPS. 21 Models

모델 이해하기

모델은 데이터에 대한 단일 정보 소스이며 저장하려는 데이터의 필수 필드와 동작이 포함되어 있다.

모델 기초

  • 각 모델은 django.db.models.Model의 하위클래스로 묶는 파이썬 클래스이다.
  • 모델의 각 속성들은 데이터베이스 필드를 나타낸다.
  • 장고는 자동으로 생성된 데이터베이스 액세스 API를 제공함

예제

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
  • 테이블 이름은 myapp_person으로 저절로 정의됨. 그러나 재정의 가능
  • id(기본 키) field는 자동으로 생성되지만 다른 이름으로 덮어질 수 있다. (기본 키는 관계형 DB에서 레코드의 식별자로 이용됨)

모델 사용하기

모델을 정의한 후에 INSTALLED_APPS에 패키지(앱) 이름을 추가해야함.

필드 옵션

null과 blank

blank = True면 값을 안넣어도 진행할 수 있게 함 -> form에서 빈값 전송

텍스트가 들어갈 수 있는 필드면 blank = True가 통하지만 그렇지 않은 몇몇의 필드에서는 에러를 발생시킨다.
특히 DateField는 빈 텍스트를 저장할 수 없기 때문에 null=True를 해주어야함

blank= True는 장고에서 그냥 처리가능 null = True는 데이터 베이스에서 Null값을 받을 수 있게 바꾸는 것이므로 migrate를 해주어야함

choices

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'

primary_key

primary_key가 True면 기본 키가 모델에 사용되는 것을 의미한다. 하지만 설정하지 않더라도 자동으로 IntegerField가 추가되어 기본 키가 설정 될 것이다.

pk는 읽기만 가능하다. 만약에 바꾸고 값을 저장한다면 새로운 객체가 만들어진다.

unique

유일한 값을 가지게 하는 속성

Verbose field names

ForeignKey, ManyToManyField, OneToOneField를 제외한 필드 타입의 선택적 첫번째 인수 ex)’이름’ -> 자세한 이름 속성

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)

ForeignKEy, ManyToManyField, OneToOneField의 필드 타입은 항상 첫 번째 인수로 모델의 클래스를 받기 때문에 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",
)

Relationships

관계형 데이터의 힘은 테이블을 서로 연관시키는 데 있다. Django는 세가지 관계형 데이터베이스를 정의하는 방법을 제공함

  1. many-to-one
  2. many-to-many
  3. one-to-one

Many-to-one relationships

다대일 관계는 ForeignKey를 사용하여 정의할 수 있음. 클래스 속성으로 추가되는 걸로 여느 다른 필드 타입처럼 사용될 수 있다.

ForeignKey는 모델과 관련한 위치인자를 인수로 받는다.

class Person(models.Model):
	# 자기 자신을 다대일로 연결하는 필드
	# 비어있어도 무관, 연결된 객체가 삭제되어도 함께 삭제되지 않는다.
	# 해당 필드를 비움
	name = models.CharField(max_length=60)
	teacher = models.ForeignKey(
		'self',
		on_delete=models.SET_NULL,
		null=True,
		blank=True,
	)
	
	def __str__(self):
		return f'{self}'

Many-to-many relationships

ManyToManyField를 이용해서 many-to-many 관계를 정의할 수 있다.

마찬가지로 위치인자를 받는다.

예를 들어서 토핑은 여러가지 피자 메뉴에 들어갈 수 있고 피자 메뉴에는 여러가지 토핑이 들어갈 수 있다. 이와 같은 관계를 many-to-many관계라고 한다.

class Topping(models.Model):
    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name

class Pizza(models.Model):
    name = models.CharField(max_length=50)
    toppings = models.ManyToManyField(Topping)

    def __str__(self):
        return self.name

쉘에서 연습해보기

from many_to_many.models import Topping, Pizza
>>> cheese = Topping.objects.create(name='치즈')
>>> pepperoni = Topping.objects.create(name='페페로니')
>>> ham = Topping.objects.create(name='햄')
>>> pineapple = Topping.objects.create(name='파인애플')
>>> shrimp = Topping.objects.create(name='새우')
>>> cheese_pizza = Pizza.objects.create(name='치즈피자')
>>> cheese_pizza.toppings.add(cheese)
# many to many field에서 내용을 추가할 때는 save()가 필요 없다. 넣자마자 바로 save됨
>>> pepperoni_pizza = Pizza.objects.create(name= '페페로니 피자')
>>> pepperoni_pizza.toppings.add(cheese)
>>> pepperoni_pizza.toppings.add(pepperoni)
>>> master_pizza = Pizza.objects.create(name='피자왕')
>>> master_pizza.topping.add(cheese, pepperoni, shrimp, ham, mushroom, pineapple)

Extra fields on many-to-many relationships

그냥 단순히 피자와 토핑의 관계같은 단순한 다대다관계를 다루려면 standard ManyToManyField를 사용하면 그만이다. 하지만 두 모델 간의 연관된 정보가 필요할 경우도 있다.

예를 들면, 음악가들이 속해있는 음악단을 조사하는 경우를 생각해보자. 이러한 다대다 경우는 음악가와 음악단 사이의 입단날짜와 같은 세부정보가 존재할 수 있다.

이러한 경우에는 그냥 through를 사용한 중급 다대다관계 모델을 사용하면 된다.

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로 확실하게 두 모델의 관계를 정의할 수 있다.

쉘에서 연습해보기

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

One-to-one relationships

일대일 관계의 예를 먼저 들면 장소(Place)에 대한 데이터베이스를 생성한다고 할 때, 주소, 전화번호와 같은 고유한 것들이 함께 고려된다.

만약 레스토랑의 데이터베이스를 구축하기를 원한다면 레스토랑 모델에서 자신을 반복하고 해당 필드를 복제하는 대신 장소(Place)에 OneToOneField를 걸 수 있다.

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

    def __str__(self):
        return f'{self.name} the place'

class Restaurant(models.Model):
    place = models.OneToOneField(
        Place,
        on_delete=models.CASCADE,
        primary_key = True,
    )
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

    def __str__(self):
        return f'{self.place.name} the restaurant'

class Waiter(models.Model):
    restaurant = models.ForeignKey(
        Restaurant,
        on_delete=models.CASCADE)
    name = models.CharField(max_length=50)


    def __str__(self):
        return f'{self.name} the waiter at {self.restaurant}'

쉘에서 실습해보기

>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
>>> p1.save()
>>> p2 = Place(name='Ace Hardware', address='1013 N. Ashland')
>>> p2.save()

>>> r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
>>> r.save()

장소와 레스토랑을 생성한다.

레스토랑에서 장소에 접근할 수 있다.

>>> r.place
<Place: Demon Dogs the place>

장소에서도 레스토랑에 접근할 수 있다.

>>> p1.restaurant
<Restaurant: Demon Dogs the restaurant>

할당 개념으도 장소를 설정할 수 있다. 장소는 레스토랑의 primary key로 있기 때문이다(?)

You can query the models using lookups across relationships: (검색을 이용해 관계들 사이를 뛰어넘어 질의할 수 있다?)

>>> Restaurant.objects.get(place=p1)
<Restaurant: Demon Dogs the restaurant>
>>> Restaurant.objects.get(place__pk=1)
<Restaurant: Demon Dogs the restaurant>
>>> Restaurant.objects.filter(place__name__startswith="Demon")
<QuerySet [<Restaurant: Demon Dogs the restaurant>]>
>>> Restaurant.objects.exclude(place__address__contains="Ashland")
<QuerySet [<Restaurant: Demon Dogs the restaurant>]>

Comments