Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added apps/meal/__init__.py
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Generated by Django 4.2.23 on 2025-11-25 17:44

import datetime
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone


class Migration(migrations.Migration):

dependencies = [
('meal', '0002_remove_cafeteriamenu_day_remove_cafeteriamenu_time_and_more'),
]

operations = [
migrations.CreateModel(
name='Restaurant',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(db_index=True, default=django.utils.timezone.now, verbose_name='생성 시간')),
('updated_at', models.DateTimeField(auto_now=True, db_index=True, verbose_name='수정 시간')),
('deleted_at', models.DateTimeField(db_index=True, default=datetime.datetime(1, 1, 1, 0, 0, tzinfo=datetime.timezone.utc), verbose_name='삭제 시간')),
('restaurant_name', models.CharField(max_length=32, unique=True, verbose_name='식당 이름')),
],
options={
'ordering': ('-created_at',),
'abstract': False,
},
),
migrations.RemoveField(
model_name='course',
name='restaurant_name',
),
migrations.AddField(
model_name='course',
name='course_name',
field=models.CharField(default=str, max_length=32, verbose_name='코스 이름'),
preserve_default=False,
),
migrations.AlterField(
model_name='course',
name='meal_time',
field=models.CharField(choices=[('BREAKFAST', 'BREAKFAST'), ('LUNCH', 'LUNCH'), ('DINNER', 'DINNER')], max_length=10, null=True, verbose_name='식사 시간대'),
),
migrations.AddField(
model_name='cafeteriamenu',
name='restaurant_id',
field=models.ForeignKey(default=str, on_delete=django.db.models.deletion.CASCADE, related_name='cafeteria_menus', to='meal.restaurant', verbose_name='식당 이름'),
preserve_default=False,
),
migrations.AddField(
model_name='course',
name='restaurant_id',
field=models.ForeignKey(default=str, on_delete=django.db.models.deletion.CASCADE, related_name='courses', to='meal.restaurant', verbose_name='식당 이름'),
preserve_default=False,
),
]
7 changes: 4 additions & 3 deletions apps/meal/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .restaurant import Restaurant
from .course import Course
from .cafeteria_menu import CafeteriaMenu, MealType
from .course import Course, MealType
from .menu_allergy import MenuAllergy
from .menu import Menu
from .menu import Menu
from .menu_allergy import MenuAllergy
7 changes: 7 additions & 0 deletions apps/meal/models/cafeteria_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ class MealType(str, Enum):
DINNER = "DINNER" # 석식

class CafeteriaMenu(MetaDataModel):
restaurant_id = models.ForeignKey(
to="meal.Restaurant",
on_delete=models.CASCADE,
related_name="cafeteria_menus",
verbose_name="식당 이름",
db_index=True,
)
menu_name = models.TextField(
verbose_name="메뉴명",
)
Expand Down
18 changes: 10 additions & 8 deletions apps/meal/models/course.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ class MealType(str, Enum):
LUNCH = "LUNCH" # 중식
DINNER = "DINNER" # 석식

class Course(MetaDataModel):
class MealType(models.IntegerChoices):
BREAKFAST = 0, "조식"
LUNCH = 1, "중식"
DINNER = 2, "석식"

restaurant_name = models.CharField(
class Course(MetaDataModel):
restaurant_id = models.ForeignKey(
to="meal.Restaurant",
on_delete=models.CASCADE,
related_name="courses",
verbose_name="식당 이름",
max_length=32,
db_index=True,
)
course_name = models.CharField(
verbose_name="코스 이름",
max_length=32,
)
price = models.PositiveIntegerField(
verbose_name="가격",
blank=True,
Expand Down
12 changes: 12 additions & 0 deletions apps/meal/models/restaurant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from django.db import models
from ara.db.models import MetaDataModel

class Restaurant(MetaDataModel):
restaurant_name = models.CharField(
verbose_name="식당 이름",
max_length=32,
unique=True,
)

def __str__(self):
return self.restaurant_name
Empty file.
38 changes: 38 additions & 0 deletions apps/meal/serializers/meal_serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from rest_framework import serializers
from ..models import Course, Menu, CafeteriaMenu

class BaseMenuSerializer(serializers.ModelSerializer):
"""알러지 코드 추출 및 경고 플래그 표시 여부 확인"""
allergy_codes = serializers.SerializerMethodField()
has_user_allergy = serializers.SerializerMethodField()

def get_allergy_codes(self, obj):
return list(obj.allergy_set.values_list('allergen_code', flat=True))

def get_has_user_allergy(self, obj):
user_allergies = self.context.get('user_allergies', [])
if not user_allergies:
return False
menu_allergies = set(obj.allergy_set.values_list('allergen_code', flat=True))
return bool(menu_allergies.intersection(set(user_allergies)))

class MenuSerializer(BaseMenuSerializer):
class Meta:
model = Menu
fields = ('menu_name', 'allergy_codes', 'has_user_allergy')

class CafeteriaMenuSerializer(BaseMenuSerializer):
class Meta:
model = CafeteriaMenu
fields = ('menu_name', 'price', 'allergy_codes', 'has_user_allergy')

class CourseSerializer(serializers.ModelSerializer):
menus = MenuSerializer(source='filtered_menus', many=True, read_only=True)

class Meta:
model = Course
fields = ('course_name', 'price', 'menus')

class DailyMealResponseSerializer(serializers.Serializer):
courses = CourseSerializer(many=True)
cafeteria_menus = CafeteriaMenuSerializer(many=True)
6 changes: 6 additions & 0 deletions apps/meal/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.urls import path, include
from .views.router import router

urlpatterns = [
path("", include(router.urls)),
]
Empty file added apps/meal/views/__init__.py
Empty file.
54 changes: 54 additions & 0 deletions apps/meal/views/meal_viewset.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기
try:
query_date = date_type(int(date_str[:4]), int(date_str[4:6]), int(date_str[6:]))
except (ValueError, TypeError):
return Response({'error': 'Invalid date'}, status=status.HTTP_400_BAD_REQUEST)

이부분에서 잘못된 형식의 데이터에 대해서 에러 메서지를 조금 더 자세히 명시해 주세요

ex) 날짜가 틀린 경우 어떤 형식으로 와야 하는지

+) 테스트 케이스도 만들어 주세요!!

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from rest_framework import viewsets, status
from rest_framework.response import Response
from django.db.models import Prefetch
from datetime import date as date_type
from ..models import Course, Menu, CafeteriaMenu
from ..serializers.meal_serializers import CourseSerializer, CafeteriaMenuSerializer

class MealViewSet(viewsets.ViewSet):

def list(self, request):
date_str = request.query_params.get('date')
restaurant_name = request.query_params.get('restaurant_name')
meal_time = request.query_params.get('meal_time')

if not all([date_str, restaurant_name, meal_time]):
return Response({'error': 'Missing parameters'}, status=status.HTTP_400_BAD_REQUEST)
try:
query_date = date_type(int(date_str[:4]), int(date_str[4:6]), int(date_str[6:]))
except (ValueError, TypeError):
return Response({'error': 'Invalid date'}, status=status.HTTP_400_BAD_REQUEST)

"""알러지 필터 context 생성"""
raw_codes = request.query_params.get('allergy_codes', '')
user_allergies = [int(c.strip()) for c in raw_codes.split(',') if c.strip()]
context = {'user_allergies': user_allergies}

"""일반 코스 메뉴 조회"""
all_menus_qs = Menu.objects.all().prefetch_related('allergy_set')

course_queryset = Course.objects.filter(
restaurant_id__restaurant_name=restaurant_name,
date=query_date,
meal_time=meal_time
).prefetch_related(
Prefetch('menu_set', queryset=all_menus_qs, to_attr='filtered_menus')
)

course_serializer = CourseSerializer(course_queryset, many=True, context=context)

"""카페테리아 메뉴 조회"""
cafeteria_queryset = CafeteriaMenu.objects.filter(
restaurant_id__restaurant_name=restaurant_name,
date=query_date,
meal_time=meal_time,
).prefetch_related('allergy_set')

cafeteria_serializer = CafeteriaMenuSerializer(cafeteria_queryset, many=True, context=context)


return Response({
'restaurant': restaurant_name,
'courses': course_serializer.data,
'cafeteria_menus': cafeteria_serializer.data,
}, status=status.HTTP_200_OK)
10 changes: 10 additions & 0 deletions apps/meal/views/router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from rest_framework import routers
from apps.meal.views import meal_viewset

router = routers.DefaultRouter()

router.register(
prefix=r"",
viewset=meal_viewset.MealViewSet,
basename="meal",
)
Loading