SSAMKO의 개발 이야기

Django에서 하나의 폼으로 여러 테이블에 데이터 작성하기 | transaction.atomic 본문

Django

Django에서 하나의 폼으로 여러 테이블에 데이터 작성하기 | transaction.atomic

SSAMKO 2020. 3. 29. 10:55
반응형

회원가입 시 하나의 폼 작성으로 school과 adminuser 그리고 Comroom 테이블에 동시에 데이터를 생성해야 했다.

회원가입 폼

form_valid단계에서 각 field의 정보들로 세 개의 Model에 대한 인스턴스를 생성해서 저장하면 되는 그리 어렵지 않은 문제였지만, 

만일 하나의 테이블에는 정상적으로 생성이 되고, 다른 테이블에서는 실패한다면 다시 회원가입을 시도하려고 할 때 문제가 발생할 수 있다. 

이런 문제를 해결하기 위해 데이터베이스 트랜젝션Database transaction을 이용해야 한다. 

데이터베이스 트랜젝션이란, 두 개 이상의 Query를 처리할 때 하나라도 실패하면 전체를 되돌리도록 query를 단일화 하는 작업으로, ACID라는 특성을 갖는다.

 

Atomic(원자성), Consistent(일관성), Isolated(독립성), Durable(지속성)
- from <Two scoops of django>

이러한 트랜젝션을 쉽게 처리할 수 있다는 것이 django의 장점 중 하나이다. 

 

그럼 실제 어떻게 사용되는지 살펴보자.

 

# forms.py
class RegisterForm(forms.Form):
	# for School
    province = forms.CharField(label='교육청',
                               widget=forms.Select(choices=province_list)
    name = forms.CharField(max_length=64, label='학교명')
    ea = forms.IntegerField(label='교내 컴퓨터실 수',
        help_text='특별실(스마트패드)을 포함한 수. 최대 5개.',
        validators=[MaxValueValidator(5)])
    
    # for AdminUser
    user = forms.CharField(max_length=64, label='아이디') 
    password = forms.CharField(widget=forms.PasswordInput, label='비밀번호')
    re_password = forms.CharField(widget=forms.PasswordInput, label='비밀번호 확인')
    realname = forms.CharField(max_length=64, label='담당자 이름')
    email = forms.EmailField(label='이메일')
# views.py
from django.db import transaction

class RegisterView(FormView):
    template_name = 'register.html'
    form_class = RegisterForm
    success_url = '/' # 사용되지 않음

    def form_valid(self, form):
        with transaction.atomic(): #트랜잭션
			# School query
            school = School(
                # Put proper data from cleaned_data here
            )
            school.save()
            adminUser = AdminUser(
                # Put proper data from cleaned_data here
            )
            
            # AdminUser query
            adminUser.save()
            
            # Comroom Query
            for i in range(int(form.cleaned_data.get('ea'))):
                comroom = Comroom(
                    school=school,
                    roomNo=i+1,
                    name=f"컴{i+1}실",
                    caption='위치, 교실 이용방법, 이용시 주의사항 등'
                )
                comroom.save()
                
            # Message process
            return render(self.request, 'notice.html', {'message': message})
            

views.py에서 form_valid안에 with 구문을 사용해 transaction.atomic처리를 해주었다.

이렇게 하면 school.save(), adminUser.save(), comroom.save() 중 하나라도 실패할 경우 모든 commit을 roll-back해서 원래 상태도 되돌린다. 

 

이렇게 하면 동시에 여러 개의 query를 ACID를 유지하며 처리할 수 있다.

 

실제 구동화면 보러가기

반응형
Comments