mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-11-04 03:03:18 -05:00 
			
		
		
		
	feat: implemented "order by" API parameters for recipe, food, and unit queries (#1356)
* Added API params to order by different properties * fix for incorrect var name * removed invalid default order_by * implemented fallback for invalid user input
This commit is contained in:
		@@ -56,7 +56,9 @@ class RepositoryGeneric(Generic[Schema, Model]):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return {**dct, **kwargs}
 | 
					        return {**dct, **kwargs}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_all(self, limit: int = None, order_by: str = None, start=0, override=None) -> list[Schema]:
 | 
					    def get_all(
 | 
				
			||||||
 | 
					        self, limit: int = None, order_by: str = None, order_descending: bool = True, start=0, override=None
 | 
				
			||||||
 | 
					    ) -> list[Schema]:
 | 
				
			||||||
        # sourcery skip: remove-unnecessary-cast
 | 
					        # sourcery skip: remove-unnecessary-cast
 | 
				
			||||||
        eff_schema = override or self.schema
 | 
					        eff_schema = override or self.schema
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -65,10 +67,19 @@ class RepositoryGeneric(Generic[Schema, Model]):
 | 
				
			|||||||
        q = self._query().filter_by(**fltr)
 | 
					        q = self._query().filter_by(**fltr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if order_by:
 | 
					        if order_by:
 | 
				
			||||||
            if order_attr := getattr(self.model, str(order_by)):
 | 
					            try:
 | 
				
			||||||
                order_attr = order_attr.desc()
 | 
					                order_attr = getattr(self.model, str(order_by))
 | 
				
			||||||
 | 
					                if order_descending:
 | 
				
			||||||
 | 
					                    order_attr = order_attr.desc()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    order_attr = order_attr.asc()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                q = q.order_by(order_attr)
 | 
					                q = q.order_by(order_attr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            except AttributeError:
 | 
				
			||||||
 | 
					                self.logger.info(f'Attempted to sort by unknown sort property "{order_by}"; ignoring')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return [eff_schema.from_orm(x) for x in q.offset(start).limit(limit).all()]
 | 
					        return [eff_schema.from_orm(x) for x in q.offset(start).limit(limit).all()]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def multi_query(
 | 
					    def multi_query(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,7 +90,9 @@ class RepositoryRecipes(RepositoryGeneric[Recipe, RecipeModel]):
 | 
				
			|||||||
            override_schema=override_schema,
 | 
					            override_schema=override_schema,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def summary(self, group_id, start=0, limit=99999, load_foods=False) -> Any:
 | 
					    def summary(
 | 
				
			||||||
 | 
					        self, group_id, start=0, limit=99999, load_foods=False, order_by="date_added", order_descending=True
 | 
				
			||||||
 | 
					    ) -> Any:
 | 
				
			||||||
        args = [
 | 
					        args = [
 | 
				
			||||||
            joinedload(RecipeModel.recipe_category),
 | 
					            joinedload(RecipeModel.recipe_category),
 | 
				
			||||||
            joinedload(RecipeModel.tags),
 | 
					            joinedload(RecipeModel.tags),
 | 
				
			||||||
@@ -100,11 +102,27 @@ class RepositoryRecipes(RepositoryGeneric[Recipe, RecipeModel]):
 | 
				
			|||||||
        if load_foods:
 | 
					        if load_foods:
 | 
				
			||||||
            args.append(joinedload(RecipeModel.recipe_ingredient).options(joinedload(RecipeIngredient.food)))
 | 
					            args.append(joinedload(RecipeModel.recipe_ingredient).options(joinedload(RecipeIngredient.food)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            if order_by:
 | 
				
			||||||
 | 
					                order_attr = getattr(RecipeModel, order_by)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                order_attr = RecipeModel.date_added
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        except AttributeError:
 | 
				
			||||||
 | 
					            self.logger.info(f'Attempted to sort by unknown sort property "{order_by}"; ignoring')
 | 
				
			||||||
 | 
					            order_attr = RecipeModel.date_added
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if order_descending:
 | 
				
			||||||
 | 
					            order_attr = order_attr.desc()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            order_attr = order_attr.asc()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            self.session.query(RecipeModel)
 | 
					            self.session.query(RecipeModel)
 | 
				
			||||||
            .options(*args)
 | 
					            .options(*args)
 | 
				
			||||||
            .filter(RecipeModel.group_id == group_id)
 | 
					            .filter(RecipeModel.group_id == group_id)
 | 
				
			||||||
            .order_by(RecipeModel.date_added.desc())
 | 
					            .order_by(order_attr)
 | 
				
			||||||
            .offset(start)
 | 
					            .offset(start)
 | 
				
			||||||
            .limit(limit)
 | 
					            .limit(limit)
 | 
				
			||||||
            .all()
 | 
					            .all()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -194,7 +194,14 @@ class RecipeController(BaseRecipeController):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @router.get("", response_model=list[RecipeSummary])
 | 
					    @router.get("", response_model=list[RecipeSummary])
 | 
				
			||||||
    def get_all(self, q: RecipeGetAll = Depends(RecipeGetAll)):
 | 
					    def get_all(self, q: RecipeGetAll = Depends(RecipeGetAll)):
 | 
				
			||||||
        items = self.repo.summary(self.user.group_id, start=q.start, limit=q.limit, load_foods=q.load_food)
 | 
					        items = self.repo.summary(
 | 
				
			||||||
 | 
					            self.user.group_id,
 | 
				
			||||||
 | 
					            start=q.start,
 | 
				
			||||||
 | 
					            limit=q.limit,
 | 
				
			||||||
 | 
					            load_foods=q.load_food,
 | 
				
			||||||
 | 
					            order_by=q.order_by,
 | 
				
			||||||
 | 
					            order_descending=q.order_descending,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        new_items = []
 | 
					        new_items = []
 | 
				
			||||||
        for item in items:
 | 
					        for item in items:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,7 +39,7 @@ class IngredientFoodsController(BaseUserController):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @router.get("", response_model=list[IngredientFood])
 | 
					    @router.get("", response_model=list[IngredientFood])
 | 
				
			||||||
    def get_all(self, q: GetAll = Depends(GetAll)):
 | 
					    def get_all(self, q: GetAll = Depends(GetAll)):
 | 
				
			||||||
        return self.repo.get_all(start=q.start, limit=q.limit)
 | 
					        return self.repo.get_all(start=q.start, limit=q.limit, order_by=q.order_by, order_descending=q.order_descending)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @router.post("", response_model=IngredientFood, status_code=201)
 | 
					    @router.post("", response_model=IngredientFood, status_code=201)
 | 
				
			||||||
    def create_one(self, data: CreateIngredientFood):
 | 
					    def create_one(self, data: CreateIngredientFood):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,7 +39,7 @@ class IngredientUnitsController(BaseUserController):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @router.get("", response_model=list[IngredientUnit])
 | 
					    @router.get("", response_model=list[IngredientUnit])
 | 
				
			||||||
    def get_all(self, q: GetAll = Depends(GetAll)):
 | 
					    def get_all(self, q: GetAll = Depends(GetAll)):
 | 
				
			||||||
        return self.repo.get_all(start=q.start, limit=q.limit)
 | 
					        return self.repo.get_all(start=q.start, limit=q.limit, order_by=q.order_by, order_descending=q.order_descending)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @router.post("", response_model=IngredientUnit, status_code=201)
 | 
					    @router.post("", response_model=IngredientUnit, status_code=201)
 | 
				
			||||||
    def create_one(self, data: CreateIngredientUnit):
 | 
					    def create_one(self, data: CreateIngredientUnit):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,10 @@
 | 
				
			|||||||
 | 
					from typing import Optional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from mealie.schema._mealie import MealieModel
 | 
					from mealie.schema._mealie import MealieModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class GetAll(MealieModel):
 | 
					class GetAll(MealieModel):
 | 
				
			||||||
    start: int = 0
 | 
					    start: int = 0
 | 
				
			||||||
    limit: int = 999
 | 
					    limit: int = 999
 | 
				
			||||||
 | 
					    order_by: Optional[str]
 | 
				
			||||||
 | 
					    order_descending: Optional[bool] = True
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user