Today I Learned/django

(장고) ORM tutorial #4. ManyToManyField()

하나719 2023. 8. 21. 17:56
반응형

https://www.pythontutorial.net/django-tutorial/django-many-to-many/

 

Django Many-to-Many Relationships By Practical Examples

In this tutorial, you'll learn how to use ManyToManyField to model a Django Many-to-Many relationship.

www.pythontutorial.net

한 명의 직원은 다양한 보상체계를 얻을 수 있고 , 보상체계에도 여러 직원이 포함될 수 있는 N:N 관계이다.

이때 각각의 테이블은 직접적으로 연결되지 않고, 중간에 연결해주는 테이블(hr_employee_compensations) 을 거친다. 

 

1. Compensation 모델 생성

class Compensation(models.Model):
    name = models.CharField(max_length=255)

    def __str__(self):
        return self.name

2. Employee 모델에 연결 추가 

  • ManyToManyField(): 자동으로 가운데 join table 만들어줌 
class Employee(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    contact = models.OneToOneField(Contact, on_delete=models.SET_NULL, null=True) #contact 지워져도 employee 연결된 안지워짐
    department = models.ForeignKey(Department, on_delete=models.CASCADE, default= '' ) # department 지워지면 연결된 employee 모두 삭제됨
   
    ## 추가 
    compensations = models.ManyToManyField(Compensation)
    def __str__(self):
        return f'{self.first_name} {self.last_name}'

3. Shell 에서 데이터 입력

## Compensation 데이터 입력
c1 = Compensation(name='stock')
c2 = Compensation(name='Bonuses')
c3 = Compensation(name='Profit Sharing')

c1.save()
c2.save()
c3.save() 

## Employee 객체에 연결
e = Employee.objects.get(id=5)
e.compensations.add(c1)
e.compensations.add(c2)
e.save()

e = Employee.objects.get(id=6)
e.compensations.add(c1)
e.save()

## c1 보상체계 연결된 employee 추출
c1.employee_set.all()

## e 객체에서 c2 객체 지우기 
e.compensations.remove(c2)

위에 방법은 ManyToMany() 필드를 통해 자동으로 중간 테이블을 만들어주고, 중간테이블을 직접 만들어서 다른 추가적인 컬럼까지 정의해서 사용할 수 있다. 

위 다이어그램에서 Job 과 Employee 조인테이블로 Assignment를 사용했으며, employee_id, position_id (job id에 매칭) 이외에 begin_date, end_date 컬럼이 추가되어 있는걸 볼 수 있다. 

이렇게 두 테이블을 N:N으로 연결하면서도 다른 컬럼을 정의해서 사용하고 싶을때 직접 정의해서 사용할 수 있는데, 각각 테이블에 ForeignKey로 1:1 연결을 하면 된다.

 

1. Job 모델 생성

-> Employee와 ManyToMany관계인데, through 로 중간 조인테이블을 생성해줌

class Job(models.Model):
    title = models.CharField(max_length=255)
    employees = models.ManyToManyField(Employee, through='Assignment')

    def __str__(self):
        return self.title

2. Assignment 모델 생성

각각 연결해줄 employee, position(job)을 ForeignKey로 정의함

추가해줄 컬럼 정의해줌 

Job모델에는 employees 컬럼이 있어서 job객체에서 employees 컬럼을 통해 접근이 가능한데, Employee 모델에는 Job 관련 정의가 되어 있지 않으므로 related_name을 정의해줘서 employee 객체에서 job 에 접근할수 있도록 한다. 

사용 예시: e.assignment.all().first().position / j.employees.all().first().last_name

class Assignment(models.Model):
    employee = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name = 'assignment')
    position = models.ForeignKey(Job, on_delete=models.CASCADE)
    begin_date = models.DateField()
    end_date = models.DateField(default = date(9999,12,31))

3. Shell에서 데이터 생성  

3-1) job 객체 생성 

j1 = Job(title='Software Engineer I')
j1.save()
j2 = Job(title='Software Engineer II') 
j2.save() 
j3 = Job(title='Software Engineer III')
j3.save()
Job.objects.all()

3-2) employee 객체 생성

e1 = Employee.objects.filter(first_name='John',last_name='Doe').first()
e1
<Employee: John Doe>
e2 = Employee.objects.filter(first_name='Jane', last_name='Doe').first()
e2
<Employee: Jane Doe>

3-3) assignment 연결 

from datetime import date
a1 = Assignment(employee=e1,job=j1, begin_date=date(2019,1,1), end_date=date(2021,12,31))
a1.save()
a2 = Assignment(employee=e1,job=j2, begin_date=date(2022,1,1))
a2.save()
a3 = Assignment(employee=e2, job=j1, begin_date=date(2019, 3, 1))
a3.save()

4. 객체 접근 , 수정, 삭제

j1.employees.all()

# j1 에서 e1 객체 삭제
j1.employees.remove(e1) 

# j1 객체에 연결된 employees 모두 삭제
j1.employees.clear()
반응형