메인기능인 식단생성을 구현하면서 마주한 문제와 해결했던 경험을 적어보고자 합니다.
위의 4가지를 중점적으로 기획/구현 했습니다.
하나하나 어떻게 구현했지 작성해 보겠습니다.
이 문제는 한가지 식단을 주에 21번 반복하면서 생긴 문제였고 내가 생각한 해결방법은 다음과 같다.
등이 있었지만 2번방법을 택했다.
왜냐하면 결제자체를 쉽게만들 수 있는 방법이 1번이 가장 적합했기 때문이다.
그렇게 3가지의 카테고리를 선택해서 2번 추가하는 로직으로 만들었다.


하나의 diet에는 N개의 식품이있기 때문에 여러개의 식단이 만들어지는 특성상 manytomanyfield로 구현 되었다.
그렇게 테스트를 돌려보니중복되는 식단을 계속해서 만들어지는 버그가 있었다.
내 검색의 한계로는 결국 식단의 ID가 다르기 때문에 같은 음식이 들어와 있어도 다르다라고 인식한다는 것을 알았다
결국에는 식단을 만든 이후 그 식단과 길이와 음식의 리스트가 똑같은 것이 있다면 그 식단을 리턴하는 로직을 해결했다.
def make_instance(self, min_nutrient, meal_count, category, bulk=False):
diet_data = init_nutrient()
meal_list = []
for _, nutrient_range in zip(self.__meals, self.__meals_nutrient):
need_nutrient = cal_nutrient_range(min_nutrient, nutrient_range)
meal_getter = MealGetter(Meal)
_meal = meal_getter.get_data(need_nutrient, category)
add_nutrient(diet_data, _meal)
meal_list.append(_meal)
if bulk:
return diet_data
queryset = self.model.objects.filter(meals__in=meal_list, meal_count=meal_count, category=category)
if queryset.exists():
for diet in queryset:
if len(diet.meals.all()) == len(meal_list):
return diet
diet = self.model.objects.create(
kcal=diet_data["kcal"],
protein=diet_data["protein"],
fat=diet_data["fat"],
carbs=diet_data["carbs"],
meal_count = meal_count,
category = category
)
diet.meals.set(meal_list)
diet.save()
return diet
코드자체는 구질구질하지만 다음에는 멘토링을 받아볼만한 코드라고 생각한다.
https://www.youtube.com/watch?v=dJoOsjMBA9U&list=PLeRLqDfWwd9P-2Y5n5FYH1RxBte0klGjB
https://www.youtube.com/watch?v=yTWdaj7iMYo&list=PLeRLqDfWwd9P-2Y5n5FYH1RxBte0klGjB
위의 2가지를 토대로 구현했지만 프론트를 처음하다 보니 이거 맞나 싶을정도록 사용성이 너무 후지다.
ver0.3 부터는 프로젝트를 같이 만들어 볼생각이다. (아래는 0.1.2)

이번 프로젝트를 진행하면서 어러번의 수정을 거듭했다.
스파게티가 너무 많아져 대대적으로 코드를 작성하는 방법을 배웠다.
다음은 최적화를 한부분 들이다.

영양소계산이나 기초대사량을 구하는 것은 유틸로 빼자니 크기가 있어서 core쪽으로 분리 했다.
전체적인 식단을 생성하는 로직은 다음과 같다.
1. 옵션에 해당하는 주간 식단이 있는가?(옵션 : 끼니, 식품카테고리들)
2. 없다면 주간식단을 생성한다.
3. 옵션에 해당하는 식단이 있는가? (옵션 : 끼니, 식품카테고리)
4. 없다면 식단을 생성한다.
5. 옵션에 해당하는 식사가 있는가? (옵션 : 식품카테고리)
6. 없다면 식사를 생성한다.
7. 식품 카테고리 옵션에 해당하는 식품을 정렬하고 유저의 기초대사량에 맞도록 식품데이터를 돌린다.
주기적으로 반복되는 있는가? 만든다! 를 getter와 maker로 분리하여서 진행했다.(글을 작성하고 있는데 인터페이스가 없다고 생각된다.)
다음은 기본적인 getter이다
# base_getter.py
class GetterBase(ManagerBase):
def __init__(self, _model):
super().__init__(_model)
def find_instance(self, model, min_nutrient, max_nutrient, meal_count=None, category=None):
q = Q()
for nutrient in ["kcal", "protein", "fat", "carbs"]:
nutrient_min = "{}".format(nutrient)
nutrient_max = "{}".format(nutrient)
q &= Q(**{"{}__gte".format(nutrient_min): min_nutrient[nutrient], "{}__lte".format(nutrient_max): max_nutrient[nutrient]})
if category is not None:
q &= Q(category=category)
if meal_count is not None:
q &= Q(meal_count=meal_count)
return model.objects.filter(q)
@abstractmethod
def get_data(self):
pass
get_data : 말그대로 데이터를 가져오는거다 네이밍이 아쉽지만...
find_instance : 영양소를 비교하여 가저오는 로직이다.
아래는 WeekDietGetter이다.
#weekdiet_getter.py
class WeekDietGetter(GetterBase):
def __init__(self):
pass
def find_instance(self, model:WeekDiet, min_nutrient, max_nutrient, meal_count=None, categories=None):
q = Q()
for nutrient in ["kcal", "protein", "fat", "carbs"]:
nutrient_min = "{}".format(nutrient)
nutrient_max = "{}".format(nutrient)
q &= Q(**{"{}__gte".format(nutrient_min): min_nutrient[nutrient], "{}__lte".format(nutrient_max): max_nutrient[nutrient]})
q &= Q(categories__in=categories)
if meal_count is not None:
q &= Q(meal_count=meal_count)
return model.objects.filter(q)
def get_data(self, meal_count, min_nutrient, max_nutrient, categories):
week_min_nutrient = cal_nutrient_range(min_nutrient, 6)
week_max_nutrient = cal_nutrient_range(max_nutrient, 6)
week_diet = self.find_instance(WeekDiet, week_min_nutrient, week_max_nutrient, meal_count, categories)
# TODO : 0번째인지는 미정이다.
if week_diet.count() > 0:
return week_diet[0]
else :
week_maker = WeekDietMaker(WeekDiet)
return week_maker.make_instance(meal_count, min_nutrient, max_nutrient, categories)
왜 find_instance를 재정의 했냐면 weekdiet만 카테고리가 manytomany filed로 지정되어 있어서 그렇다
diet나 meal 같은 경우에는 아래와 같이 구현했다.
class DietGetter(GetterBase):
def __init__(self, model, meal_count):
super().__init__(model)
self.maker = DietMaker(model, meal_count)
def get_data(self, min_nutrient, max_nutrient, meal_count, category):
diet = self.find_instance(Diet, min_nutrient, max_nutrient, meal_count, category)
if diet.count() > 0:
return diet.first()
else :
return self.maker.make_instance(min_nutrient, meal_count, category)
다음은 기본적인 maker이다.
class MakerBase(ManagerBase):
def __init__(self, _model):
super().__init__(_model)
@abstractmethod
def make_instance(self):
pass
위를 상속해서 만든 weekdiet_maker이다.
class WeekDietMaker(MakerBase):
def __init__(self, model):
super().__init__(model)
def make_instance(self, meal_count, min_nutrient, max_nutrient, categories):
week_nutrient_data = init_nutrient()
diet_list = []
for category in categories:
diet_getter = DietGetter(Diet, meal_count)
_diet = diet_getter.get_data(min_nutrient, max_nutrient, meal_count, category)
# 2번 더해주는 것이 옳다.
add_nutrient(week_nutrient_data, _diet)
add_nutrient(week_nutrient_data, _diet)
diet_list.append(_diet)
diet_list = diet_list * 2
week_diet = self.model.objects.create(
kcal=week_nutrient_data["kcal"],
protein=week_nutrient_data["protein"],
fat=week_nutrient_data["fat"],
carbs=week_nutrient_data["carbs"],
meal_count = meal_count,
)
week_diet.diets.set(diet_list)
return week_diet
v0.1 버전 회고를 마무리하고 다음 포스트때 돌아오겠습니다. (취업하고 싶다.)