jc.jang

6강 가입과 동시에 Profile만들기 본문

Django/Django - 인증편

6강 가입과 동시에 Profile만들기

jangstory 2019. 8. 29. 18:01

주제

  • 가입과 동시에 Profile만들기

노트

  • accounts앱에 Profile 모델을 만들고 User와 연결한다.
  • 기존 User의 Profile을 생성하고 신규 User 가입 시 가입과 동시에 Profile을 만들어주자.

 

  • accounts/models.py
from django.db import models
from django.conf import settings

class Profile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    bio = models.TextField(blank=True)
    website_url = models.URLField(blank=True)
  • 장고의 유저 모델을 불러올 때는 settings.AUTH_USER_MODEL로 불러온다.
  • 유저당 프로필은 하나이므로 OneToOneField로 지정한다.

 

> python manage.py makemigrations accounts
> python manage.py migrate accounts
> python manage.py shell
> from accounts.models import Profile
> Profile.objects.all()
> <QuerySet []>

> from django.contrib.auth import get_user_model
> User = get_user_model()
> User.objects.first().profile
> django.contrib.auth.models.User.profile.RelatedObjectDoesNotExist: User has no profile.
  • 모델을 변경하고 마이그레이션을 진행했는데 프로필이 존재하지 않는 것으로 나온다.
  • 또한, User에 profile 조회 시 DoesNotExist에러가 발생한다.
  • 왜냐하면, 마이그레이션 할 때 기존 User에 Profile을 만들어 준 것이 아니기 때문이다.
  • 나는 Profile을 만들고, 마이그레이션 할 때 기존 User에 Profile을 지정해주고 싶다.

 

  • Django Migration Operations RunPython을 참고하여 Profile 테이블 생성 시 실행 할 파이썬 코드를 작성한다.
  • 우선, 직전에 했던 마이그레이션 명령을 취소한다. 혹은 Profile이 생성된 마이그레이션 파일 직전 번호를 옵션으로 준다.
> python manage.py migrate zero

 

  • accounts/migrations/0001_initial.py(혹은 Profile 모델 생성 시 만들어진 파일)
# Generated by Django 2.1 on 2019-08-29 03:34

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion

def forwards_func(apps, schema_editor):
    # 현재 모든 유저에 대해 Profile을 만들어주자.
    # settings.AUTH_USER_MODEL = 'auth.User'
    auth_user_model = settings.AUTH_USER_MODEL.split('.')
    # *는 언팩킹함
    User = apps.get_model(*auth_user_model)
    Profile = apps.get_model('accounts', 'Profile')

    for user in User.objects.all():
        print('create profile for user#{}'.format(user.pk))
        Profile.objects.create(user=user)

def backwards_func(apps, schema_editor):
    pass


class Migration(migrations.Migration):

    initial = True

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='Profile',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('bio', models.TextField(blank=True)),
                ('website_url', models.URLField(blank=True)),
                ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
            ],
        ),
        migrations.RunPython(forwards_func, backwards_func),
    ]
  • apps.get_model()로 모델을 가져올 때는 (앱, 모델 클래스)를 인자로 입력한다. 
  • forwards_func에 정방향 마이그레이션이 진행될 때 수행할 파이썬 코드를 작성한다.
  • 역방향 마이그레이션 수행 시 Profile 테이블을 삭제하면 되므로 backwards_func에는 작성할 필요가 없다.

 

> python manage.py migrate accounts
> python manage.py shell
> from accounts.models import Profile
> Profile.objects.all()
> Profile 확인됨
  • 기존 User의 Profile을 생성했다.

 

  • 신규 User 가입 시 Profile을 만들어주는 코드를 작성해보자.
  • Django의 signal을 사용해서 작성한다. 참고한 블로그 글
  • signal은  의존적인 관계에 있는 대상에게 변화를 감지하여 적절한 처리를 하도록 하는 것을 말한다.
  • sender가 신호(signal)를 보내면, receiver가 그것을 받고 무언가를 수행한다.
  • > User save method를 마쳤을 때, Profile을 연결해준다.

 

  • accounts/models.py
from django.conf import settings
from django.db import models
from django.db.models.signals import post_save

class Profile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    bio = models.TextField(blank=True)
    website_url = models.URLField(blank=True)


def on_post_save_for_user(sender, **kwargs):
    if kwargs['created']:
        user = kwargs['instance']
        Profile.objects.create(user=user)

# User 회원 가입 시 Profile 생성하기
post_save.connect(on_post_save_for_user, sender=settings.AUTH_USER_MODEL)
  • User의 save() method가 끝난 후 Profile을 만들어야 한다.
  • post_save는 모델 생성 후 시그널을 보낸다.
  • 시그널을 받기 위해, signal.connect method를 사용하여 리시버를 등록할 수 있다.
  • post_save.connect(receiver, sender)
  • sender가 리시버에게 전달하는 인자들은 sender, instance, created, 등등이 있다.

질문

  • 시그널을 검색해보니 observer design patterns도 나왔는데 이 디자인 패턴은 무엇일까?

 

요약

  • runpython을 이용해 custom migration을 작성할 수 있다.
  • 시그널을 이용해 모델 시스템의 변화를 감지하여 적절한 연산을 수행할 수 있다.

날짜

  • 오후 6시, 20190829

 

Comments