사용자 역할에 따른 DB 필드 수정

김의석 ·2025년 2월 11일

Hello! Poko Ver.2

목록 보기
28/28

1. DB 필드 추가

ROLE_CHOICES = (
        ("HEAD", "정교사"),
        ("ASSISTANT", "부교사"),
    )
    role = models.CharField(max_length=10, choices=ROLE_CHOICES, default="ASSISTANT")
head_teacher = models.ForeignKey(
        "self",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="assistance_teacher",
        limit_choices_to={"role": "HEAD"},
        # 제한 조건: {"role": "HEAD"}
        # role 필드의 값이 "HEAD"(정교사)인 객체만 선택하여 head_teacher 필드에 저장할 수 있다.
        # 즉, role 필드가 "ASSISTANT"(부교사)인 객체는 선택할 수 없습니다.
        # 부교사를 정교사로 설정하는 실수를 방지하기 위해 사용.
    )

related_name="assistance_teacher"

  • "role": "HEAD" 인 객체가 자신을 참조하는 부교사 객체를 역참조로 조회한다.
    ex) assistants = head_teacher.assistant_teachers.all()

2. API 수정하기

2-1 정/부교사에 따라 출석데이터 조회하기

role 필드를 추가

    def get_queryset(self):
        # user = "token@toen.com"
        user = self.request.user
        if user == "HEAD":
            queryset = Attendance.objects.filter(name__teacher__email=user)
            year = self.request.query_params.get("year", None)
            if year is not None:
                queryset = queryset.filter(date__year=year)
            return queryset
        else:
            queryset = Attendance.objects.filter(name__teacher__email=user.head_teacher)
            year = self.request.query_params.get("year", None)
            if year is not None:
                queryset = queryset.filter(date__year=year)
            return queryset

정교사/부교사에 따라 queryset 생성하기

# AttendanceViewSet의 get_queryset

def get_queryset(self):
 	user = self.request.user
    teacher_email = user.email if user.role == "HEAD" else user.head_teacher.email
    queryset = Attendance.objects.filter(name__teacher__email=teacher_email)
    return queryset

역할: role(정교사/부교사) 필드를 비교하여 적절한 email을 선택하고, 해당 이메일을 기반으로 queryset을 생성한다.

  • 요청에서 user 정보를 가져온다.
  • user.role == "HEAD"일 경우 user.email
  • user.role != "HEAD"일 경우 user.head_teacher.email
  • queryset은 role에 따라 분류된 email을 기준으로 Attendance 모델에서 필터링한 출석데이터 값을 갖는다.
  • filter_by_year 메서드를 통해 날짜로 필터링된 출석데이터를 return 한다.
# utils.py 내 filter_by_year 메서드

def filter_by_year(self, queryset):
	year = self.request.query_params.get("year", None)
    if year is not None:
    	return queryset.filter(date__year=year)
    return queryset

역할 : 요청된 연도(year)를 기준으로 queryset을 필터링하는 함수

  • HTTP 요청의 URL 쿼리 파라미터에서 year 값을 가져온다.
  • ueryset.filter(date__year=year) year 값이 존재하면 해당 연도의 데이터만 필터링하여 반환한다.

2-2 정/부교사에 따라 출석데이터 생성하기

context를 통해 get_queryset 전달하기

# AttendanceViewSet의 create 메서드
serializer = BulkAttendanceSerializer(
            data=request.data,
            context={"queryset": self.get_queryset()},

역할 : 정교사/부교사로 분류 된 user 객체가 담긴 쿼리셋을 전달.

  • view에서 serializer로 추가적인 데이터를 전달하는 경우에는 context를 사용한다.

serializser 내 검증 코드 수정

def create(self, validated_data):
        date = validated_data.get("date")
        attendance_list = validated_data.get("attendance")

        # context를 통해 요청한 사용자 정보 가져오기
        user = self.context["request"].user

        if user.role == "HEAD":
            if Attendance.objects.filter(name__teacher=user, date=date).exists():
                raise serializers.ValidationError(
                    {"detail": f"이미 {date}의 출석 데이터가 저장되어있습니다."},
                )
        else:
            if Attendance.objects.filter(
                name__teacher=user.head_teacher, date=date
            ).exists():
                raise serializers.ValidationError(
                    {"detail": f"이미 {date}의 출석 데이터가 저장되어있습니다."},
                )
  • 기존 코드는 self.context["request"].user를 통해 user의 role 필드를 검색하여 기존 데이터 존재 여부를 검증함
    def create(self, validated_data):
        date = validated_data.get("date")
        attendance_list = validated_data.get("attendance")

        # context를 통해 요청한 사용자 정보 가져오기
        queryset = self.context["queryset"]

        if queryset.filter(date=date).exists():
            raise serializers.ValidationError(
                {"detail": f"이미 {date}의 출석 데이터가 저장되어있습니다."},
            )
  • 수정 코드에서는 self.context["queryset"]로 이미 정교사/부교사가 분류 된 user를 기준으로 필터링 된 Attendance의 데이터를 전달받아 serializser 내에서 user의 role을 통한 검증이 불필요하고 filter() 호출을 최소화.

Django ORM

Django ORM은 ForeignKey 필드(name__teacher)에서 객체를 입력하면 자동으로 id 값으로 변환하여 쿼리를 실행한다.

두 코드 다 email을 기준으로 필터링 한다.

get_queryset

queryset = Attendance.objects.filter(name__teacher__email=user_email)

  • 이 코드에서 user_email = **@**.com 형식의 문자열로 객체가 아닌 문자열을 전달 받아 필터링한다.

serializser의 create 함수

if Attendance.objects.filter(name__teacher=user, date=date).exists():

  • 이 코드에서 user는 객체상태이며 ORM은 이를 자동으로 객체의 id 값으로 전환한다.

어떤 방식이 더 좋은가?

  • 정확한 id 값을 알고 있다면 객체(user)를 전달하는 것이 더 직관적이고 안정적
  • 범용적인 검색이 필요하다면 문자열(email)을 사용하여 필터링하는 것이 적절
  • 전반적으로 ORM이 자동 변환해주는 객체 방식(name__teacher=user)이 더 깔끔하고 유지보수하기 좋다.
profile
널리 이롭게

0개의 댓글