ORM이란 Object-Relational Mapping의 약자로 객체(Object)와 관계형 데이터베이스(Relational Database)의 데이터를 매핑(Mapping)해주는 것을 의미한다.
간단히 말하면 데이터베이스에 저장된 정보를 조작하기 위해서는 SQL 쿼리문으로 직접 조작할 수 있다. 하지만 우리는 django 에서 ORM으로 DB의 정보들을 조작할 수 있다. 여기서는 ORM이 뭔지 정도만 알아보고 넘어가겠다.
테이블 생성
그 중 ManytoMany 필드의 참조를 알아보자..
의사와 환자의 예약 관계를 나타내보자.
class Doctor(models.Model):
name = models.CharField(max_length=10)
class Patient(models.Model):
name = models.CharField(max_length=10)
class Reservation(models.Model):
doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
patient = models.ForeignKey(Patient, on_delete=models.CASCADE)
의사 - 환자들 / 환자 - 의사들로 직접 접근하기 위해서는 ManyToManyField
를 사용한다. through
옵션을 통해 중개 모델을 선언한다.
class Doctor(models.Model):
name = models.CharField(max_length=10)
class Patient(models.Model):
name = models.CharField(max_length=10)
reservation = models.ManyToManyField('Doctor', through='Reservation')
class Reservation(models.Model):
doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
patient = models.ForeignKey(Patient, on_delete=models.CASCADE)
date = models.DateField()
이런식으로 관리해주면 가능하다.
지금은 다시 참조에 대해서 알아보자.
DATABASE
Doctor 테이블
id | name |
---|---|
1 | Dr.kim |
2 | Dr.han |
3 | Dr.park |
Patient 테이블
id | name |
---|---|
1 | 김코드 |
2 | 한장고 |
3 | 박장군 |
Reservation 중간 테이블
id | doctor_id | patient_id |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
3 | 2 | 3 |
예약 정보를 테이블에 만들고 싶다면 다음과 같이 만들면 된다.
d1 = Doctor.objects.create(name='Dr.kim')
d2 = Doctor.objects.create(name='Dr.han')
d3 = Doctor.objects.create(name='Dr.park')
p1 = Patient.objects.create(name='김코드')
p2 = Patient.objects.create(name='한장고')
p3 = Patient.objects.create(name='박장군')
Reservation.objects.create(doctor=d1, patient=p1)
Reservation.objects.create(doctor=d1, patient=p2)
Reservation.objects.create(doctor=d2, patient=p3)
만약 서로 참조하고 싶다면 다음과 다음같이 하면 된다, 다만 위에서 through로 중개 테이블을 설정한 경우와 하지 않은 경우는 참조에서 조금 차이가 난다. 먼저 through를 사용하지 않고 외래키로만 중간테이블을 설정하였을 경우 참조를 알아보자.
through
사용하지 않았을 경우이 경우 다대다 관계의 테이블로 직접 참조하지 못하고 중간테이블로 참조하여야한다.
d1.reservation_set.all()
p1.reservation_set.all()
for reservation in d1.reservation_set.all():
print(reservation.patient.name)
정참조든 역참조든 참조를 하여 filter 나 all 메서드로 복수의 정보를 가져온다면 데이터는 QuerySet에 담기게 된다. 우리는 QuerySet에 담긴 데이터를 직접 보고싶다면 for문으로 풀어서 접근하여야한다.
만약 get 메서드를 이용하여 데이터를 가져온다면 바로 접근이 가능하다.
through
사용하였을 경우p1 = Patient.objects.get(id=1)
d1 = Doctor.objects.get(id=1)
p1.doctors.all()
중개 모델을 활용하였기 때문에 바로 doctor 테이블에 접근이 가능하다.
정말 중요한 개념이다. through
의 활용이 여기서 발생한다.
또한 all로 가져왔으니 담긴 데이터에 접근하려면 for문으로 풀어서 접근하여야한다.
d1.patient_set.all()
대신 의사의 환자 정보를 알려면 참조하고 있는 테이블로 역참조하여야한다.
역참조는 위의 양식대로 _set
을 사용하면 역참조하겠다는 의미이다.
class Doctor(models.Model):
name = models.TextField()
class Patient(models.Model):
name = models.TextField()
doctors = models.ManyToManyField(Doctor, related_name='patients')
중간테이블에서 id값만 가지고 테이블을 관리해도 될 떄 다음과 같이 생각하게 되면 patient_doctors
의 이름으로 django에서 테이블을 자동으로 만들어준다. 그렇게 되면 우리는 중간테이블의 column을 추가할 수 없고 id값으로만 관리하게 된다.
마지막으로 장고에서 역참조 설정이 반드시 되어야하는 상황이 있다. 그때는
related_name
옵션을 적용해주자.
class Doctor(models.Model):
name = models.TextField()
class Patient(models.Model):
name = models.TextField()
# 역참조 설정. related_name
doctors = models.ManyToManyField(Doctor,
through='Reservation',
related_name='patients')
class Reservation(models.Model):
doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
patient = models.ForeignKey(Patient, on_delete=models.CASCADE)
related_name
은 참조하고 있는 모델에서 역참조를 할 경우 직관적인 이름으로 참조하기 위해 사용한다. 그렇기 때문에 역참조할때 프로젝트에서 좀 더 직관적인 이름이 있다면 원하는 이름으로 사용하면 된다. 하지만 보통 클래스 네임으로 설정한 이름을 사용하는 것이 더 직관적이로 편리할 경우가 많아 그냥 사용하는 경우가 많다.
하지만 필수적으로 사용해야할 때가 있다.
class User(models.Model):
name = models.CharField(max_length = 50)
job = models.ForeignKey('Occupation', on_delete = models.CASCADE)
[*] choice_2nd = models.ForeignKey('Occupation', on_delete = models.CASCADE, null = True)
created_at = models.DateTimeField(auto_now_add = True)
class Occupation(models.Model):
name = models.CharField(max_length = 50)
다음과 같은 job
과 choice_2nd
가 둘 다 Occupation
을 참조하고 있다. 이럴 경우 related_name
을 설정하지 않을 경우 애초에 마이그레이션이 되지 않고 related_name
을 지정하라는 오류가 뜬다.
이럴 경우 related_name
설정이 필수적인 경우이다.
class User(models.Model):
name = models.CharField(max_length = 50)
job = models.ForeignKey(
'Occupation',
on_delete = models.CASCADE,
related_name = 'appliers' ------------------------- [Key Point !]
)
choice_2nd = models.ForeignKey(
'Occupation',
on_delete = models.CASCADE,
null = True
related_name = 'second_appliers'
)
created_at = models.DateTimeField(auto_now_add = True)