1)
OneToOneField는 외래키에서 unique=True 옵션을 설정한 것과 유사하지만 reverse의 경우에서 다르다. User:Profile 관계라고 했을 때, 외래키에서 profile.user_set.first()의 결과값은 O2O에서는 profile.user의 결과값과 같다.(물론 에러가 발생하는 양상도 다르다.)
2)
from django.conf import settings
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
address = models.CharField(max_length=100)
zipcode = models.CharField(max_length=6)
+
from .models import Profile
@admin.register(Profile)
class ProfileAdmin(admin.ModelAdmin):
pass
accounts 앱을 만들어주고 프로젝트 폴더의 settings.py에 설정해주었다. 그리고 앱에 urls.py 파일을 만들고 urlspatterns를 정의해준 다음 프로젝트 폴더의 urls.py에서 include할 수 있도록 해주었다.
accounts 앱의 models.py에는 Profile 클래스와 user 필드를 정의해주었다. 이 과정에서 django.conf의 settings를 가져와서 AUTH_USER_MODEL을 참조했다. 현재 활성화된 모델을 얻는 함수가 get_user_model()임을 알아두자.
모델을 정의한 뒤에는 makemigrations, migrate로 마무리해주면 db에 accounts_profile로 테이블이 생성됐음을 예상할 수 있다.
이를 admin.py에 등록해주고 마무리했다.
3)
class Item(models.Model):
tag_set = models.ManyToManyField('Tag', blank=True)
+
class Tag(models.Model):
name = models.CharField(max_length=10, unique=True)
Tag 클래스가 더 뒤에 정의되어 있어서 'Tag' 형식으로 참조했고, Tag 없이도 Item 인스턴스를 생성할 수 있도록 blank=True로 처리했다. Tag는 저장소 개념으로 사용되기 때문에 unique=True로 처리했다.
makemigrations, migrate를 진행하고 db brower for sqllite에서 확인해보면, gallery_item와 gallery_tag db table 말고 gallery_item_tag_set db table이 생겼음을 확인할 수 있다. 이렇게 중간 테이블이 필요한 부분이 외래키와 O2O 필드와 다른 점이다.
db를 확인할 때 post.tag_set.all()은 정의된 이름 tag_set을 그대로 사용하는 것이고, tag.post_set.all()은 related_name으로 'post'와 'set'이 합쳐진 것으로 이해하면 좋을 것이다.
4)
최초 프로젝트를 생성하고 migrate(앱 이름 없이)를 진행하기 전에는 sqlite 파일이 존재하지 않다가 진행 후에 생기는 이유는 settings.py의 INSTALLED_APPS에 포함된 기본 앱들(django.contrib) 에 모델들과 migrations 내역이 있기 때문이다.
migrations 파일을 열어보면 migration 클래스가 migrations.Migration을 상속하는 부분과 dependencies, operations로 이루어졌음을 확인할 수 있다.