mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
category/tag CRUD
This commit is contained in:
parent
ba0fb52b65
commit
10c86773ea
12 changed files with 315 additions and 37 deletions
|
@ -8,6 +8,7 @@ const categoryURLs = {
|
||||||
getAll: `${prefix}`,
|
getAll: `${prefix}`,
|
||||||
getCategory: category => `${prefix}/${category}`,
|
getCategory: category => `${prefix}/${category}`,
|
||||||
deleteCategory: category => `${prefix}/${category}`,
|
deleteCategory: category => `${prefix}/${category}`,
|
||||||
|
updateCategory: category => `${prefix}/${category}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const categoryAPI = {
|
export const categoryAPI = {
|
||||||
|
@ -24,6 +25,15 @@ export const categoryAPI = {
|
||||||
let response = await apiReq.get(categoryURLs.getCategory(category));
|
let response = await apiReq.get(categoryURLs.getCategory(category));
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
async update(name, newName, overrideRequest = false) {
|
||||||
|
let response = await apiReq.put(categoryURLs.updateCategory(name), {
|
||||||
|
name: newName,
|
||||||
|
});
|
||||||
|
if (!overrideRequest) {
|
||||||
|
store.dispatch("requestCategories");
|
||||||
|
}
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
async delete(category) {
|
async delete(category) {
|
||||||
let response = await apiReq.delete(categoryURLs.deleteCategory(category));
|
let response = await apiReq.delete(categoryURLs.deleteCategory(category));
|
||||||
store.dispatch("requestCategories");
|
store.dispatch("requestCategories");
|
||||||
|
@ -37,6 +47,7 @@ const tagURLs = {
|
||||||
getAll: `${tagPrefix}`,
|
getAll: `${tagPrefix}`,
|
||||||
getTag: tag => `${tagPrefix}/${tag}`,
|
getTag: tag => `${tagPrefix}/${tag}`,
|
||||||
deleteTag: tag => `${tagPrefix}/${tag}`,
|
deleteTag: tag => `${tagPrefix}/${tag}`,
|
||||||
|
updateTag: tag => `${tagPrefix}/${tag}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const tagAPI = {
|
export const tagAPI = {
|
||||||
|
@ -53,6 +64,15 @@ export const tagAPI = {
|
||||||
let response = await apiReq.get(tagURLs.getTag(tag));
|
let response = await apiReq.get(tagURLs.getTag(tag));
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
async update(name, newName, overrideRequest = false) {
|
||||||
|
let response = await apiReq.put(tagURLs.updateTag(name), { name: newName });
|
||||||
|
|
||||||
|
if (!overrideRequest) {
|
||||||
|
store.dispatch("requestTags");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
async delete(tag) {
|
async delete(tag) {
|
||||||
let response = await apiReq.delete(tagURLs.deleteTag(tag));
|
let response = await apiReq.delete(tagURLs.deleteTag(tag));
|
||||||
store.dispatch("requestTags");
|
store.dispatch("requestTags");
|
||||||
|
|
|
@ -1,21 +1,34 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<v-dialog v-model="dialog" :width="modalWidth + 'px'">
|
<v-dialog v-model="dialog" :width="modalWidth + 'px'">
|
||||||
<v-app-bar dark :color="color" class="mt-n1 mb-2">
|
<v-card class="pb-2">
|
||||||
<v-icon large left v-if="!loading">
|
<v-app-bar dark :color="color" class="mt-n1 mb-2">
|
||||||
{{ titleIcon }}
|
<v-icon large left v-if="!loading">
|
||||||
</v-icon>
|
{{ titleIcon }}
|
||||||
<v-progress-circular
|
</v-icon>
|
||||||
v-else
|
<v-progress-circular
|
||||||
indeterminate
|
v-else
|
||||||
color="white"
|
indeterminate
|
||||||
large
|
color="white"
|
||||||
class="mr-2"
|
large
|
||||||
>
|
class="mr-2"
|
||||||
</v-progress-circular>
|
>
|
||||||
<v-toolbar-title class="headline"> {{ title }} </v-toolbar-title>
|
</v-progress-circular>
|
||||||
<v-spacer></v-spacer>
|
<v-toolbar-title class="headline"> {{ title }} </v-toolbar-title>
|
||||||
</v-app-bar>
|
<v-spacer></v-spacer>
|
||||||
|
</v-app-bar>
|
||||||
|
<slot> </slot>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-btn text color="grey" @click="dialog = false">
|
||||||
|
Cancel
|
||||||
|
</v-btn>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn color="success" @click="$emit('submit')">
|
||||||
|
Submit
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
<slot name="below-actions"> </slot>
|
||||||
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -39,6 +52,7 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
dialog: false,
|
dialog: false,
|
||||||
|
loading: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -163,6 +163,11 @@ export default {
|
||||||
to: "/admin/settings",
|
to: "/admin/settings",
|
||||||
title: this.$t("settings.site-settings"),
|
title: this.$t("settings.site-settings"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: "mdi-tools",
|
||||||
|
to: "/admin/toolbox",
|
||||||
|
title: this.$t("settings.toolbox.toolbox"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: "mdi-account-group",
|
icon: "mdi-account-group",
|
||||||
to: "/admin/manage-users",
|
to: "/admin/manage-users",
|
||||||
|
|
|
@ -218,6 +218,10 @@
|
||||||
"test-webhooks": "Test Webhooks",
|
"test-webhooks": "Test Webhooks",
|
||||||
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
|
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "The URLs listed below will receive webhooks containing the recipe data for the meal plan on it's scheduled day. Currently Webhooks will execute at",
|
||||||
"webhook-url": "Webhook URL"
|
"webhook-url": "Webhook URL"
|
||||||
|
},
|
||||||
|
"toolbox": {
|
||||||
|
"toolbox": "Toolbox",
|
||||||
|
"new-name": "New Name"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
|
|
169
frontend/src/pages/Admin/ToolBox/CategoryTagEditor/index.vue
Normal file
169
frontend/src/pages/Admin/ToolBox/CategoryTagEditor/index.vue
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
<template>
|
||||||
|
<v-card outlined class="mt-n1">
|
||||||
|
<base-dialog
|
||||||
|
ref="renameDialog"
|
||||||
|
title-icon="mdi-tag"
|
||||||
|
:title="renameTarget.title"
|
||||||
|
modal-width="800"
|
||||||
|
@submit="renameFromDialog(renameTarget.slug, renameTarget.newName)"
|
||||||
|
>
|
||||||
|
<v-form ref="renameForm">
|
||||||
|
<v-card-text>
|
||||||
|
<v-text-field
|
||||||
|
:placeholder="$t('settings.toolbox.new-name')"
|
||||||
|
:rules="[existsRule]"
|
||||||
|
v-model="renameTarget.newName"
|
||||||
|
></v-text-field>
|
||||||
|
</v-card-text>
|
||||||
|
</v-form>
|
||||||
|
<template slot="below-actions">
|
||||||
|
<v-card-title class="headline">
|
||||||
|
{{ renameTarget.recipes.length || 0 }} Recipes Effected
|
||||||
|
</v-card-title>
|
||||||
|
<MobileRecipeCard
|
||||||
|
class="ml-2 mr-2 mt-2 mb-2"
|
||||||
|
v-for="recipe in renameTarget.recipes"
|
||||||
|
:key="recipe.slug"
|
||||||
|
:slug="recipe.slug"
|
||||||
|
:name="recipe.name"
|
||||||
|
:description="recipe.description"
|
||||||
|
:rating="recipe.rating"
|
||||||
|
:route="false"
|
||||||
|
:tags="recipe.tags"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</base-dialog>
|
||||||
|
<v-app-bar flat>
|
||||||
|
<v-spacer> </v-spacer>
|
||||||
|
<v-btn @click="titleCaseAll" small color="success">
|
||||||
|
Title Case All
|
||||||
|
</v-btn>
|
||||||
|
</v-app-bar>
|
||||||
|
<v-card-text>
|
||||||
|
<v-row>
|
||||||
|
<v-col
|
||||||
|
:sm="6"
|
||||||
|
:md="6"
|
||||||
|
:lg="4"
|
||||||
|
:xl="3"
|
||||||
|
v-for="item in allItems"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
<v-card>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-card-title class="py-1">{{ item.name }}</v-card-title>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn small text color="info" @click="openRename(item)"
|
||||||
|
>Rename</v-btn
|
||||||
|
>
|
||||||
|
<v-btn small text color="error" @click="deleteItem(item.slug)"
|
||||||
|
>Delete
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MobileRecipeCard from "@/components/Recipe/MobileRecipeCard";
|
||||||
|
import BaseDialog from "@/components/UI/Dialogs/BaseDialog";
|
||||||
|
import { api } from "@/api";
|
||||||
|
import { validators } from "@/mixins/validators";
|
||||||
|
export default {
|
||||||
|
mixins: [validators],
|
||||||
|
components: {
|
||||||
|
BaseDialog,
|
||||||
|
MobileRecipeCard,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
isTags: {
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
renameTarget: {
|
||||||
|
title: "",
|
||||||
|
name: "",
|
||||||
|
slug: "",
|
||||||
|
newName: "",
|
||||||
|
recipes: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
allItems() {
|
||||||
|
return this.isTags
|
||||||
|
? this.$store.getters.getAllTags
|
||||||
|
: this.$store.getters.getAllCategories;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async openRename(item) {
|
||||||
|
let fromAPI = {};
|
||||||
|
if (this.isTags) {
|
||||||
|
fromAPI = await api.tags.getRecipesInTag(item.slug);
|
||||||
|
} else {
|
||||||
|
fromAPI = await api.categories.getRecipesInCategory(item.slug);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renameTarget = {
|
||||||
|
title: `Rename ${item.name}`,
|
||||||
|
name: item.name,
|
||||||
|
slug: item.slug,
|
||||||
|
newName: "",
|
||||||
|
recipes: fromAPI.recipes,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$refs.renameDialog.open();
|
||||||
|
},
|
||||||
|
async deleteItem(name) {
|
||||||
|
if (this.isTags) {
|
||||||
|
await api.tags.delete(name);
|
||||||
|
} else {
|
||||||
|
await api.categories.delete(name);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renameFromDialog(name, newName) {
|
||||||
|
if (this.$refs.renameForm.validate()) {
|
||||||
|
this.rename(name, newName);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async rename(name, newName) {
|
||||||
|
if (this.isTags) {
|
||||||
|
await api.tags.update(name, newName);
|
||||||
|
} else {
|
||||||
|
await api.categories.update(name, newName);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
titleCase(lowerName) {
|
||||||
|
return lowerName.replace(/(?:^|\s|-)\S/g, x => x.toUpperCase());
|
||||||
|
},
|
||||||
|
async titleCaseAll() {
|
||||||
|
const renameList = this.allItems.map(x => ({
|
||||||
|
slug: x.slug,
|
||||||
|
name: x.name,
|
||||||
|
newName: this.titleCase(x.name),
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (this.isTags) {
|
||||||
|
renameList.forEach(async element => {
|
||||||
|
await api.tags.update(element.slug, element.newName, true);
|
||||||
|
});
|
||||||
|
this.$store.dispatch("requestTags");
|
||||||
|
} else {
|
||||||
|
renameList.forEach(async element => {
|
||||||
|
await api.categories.update(element.slug, element.newName, true);
|
||||||
|
});
|
||||||
|
this.$store.dispatch("requestCategories");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
|
@ -1,15 +1,47 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<v-card flat>
|
||||||
|
<v-tabs
|
||||||
|
v-model="tab"
|
||||||
|
background-color="primary"
|
||||||
|
centered
|
||||||
|
dark
|
||||||
|
icons-and-text
|
||||||
|
>
|
||||||
|
<v-tabs-slider></v-tabs-slider>
|
||||||
|
|
||||||
</div>
|
<v-tab>
|
||||||
|
{{ $t("recipe.categories") }}
|
||||||
|
<v-icon>mdi-tag-multiple-outline</v-icon>
|
||||||
|
</v-tab>
|
||||||
|
|
||||||
|
<v-tab>
|
||||||
|
{{ $t("recipe.tags") }}
|
||||||
|
<v-icon>mdi-tag-multiple-outline</v-icon>
|
||||||
|
</v-tab>
|
||||||
|
</v-tabs>
|
||||||
|
|
||||||
|
<v-tabs-items v-model="tab">
|
||||||
|
<v-tab-item><CategoryTagEditor :is-tags="false"/></v-tab-item>
|
||||||
|
<v-tab-item><CategoryTagEditor :is-tags="true" /> </v-tab-item>
|
||||||
|
</v-tabs-items>
|
||||||
|
</v-card>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
import CategoryTagEditor from "./CategoryTagEditor";
|
||||||
|
export default {
|
||||||
}
|
components: {
|
||||||
|
CategoryTagEditor,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tab: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style>
|
||||||
|
|
||||||
</style>
|
</style>
|
|
@ -7,9 +7,10 @@ import Profile from "@/pages/Admin/Profile";
|
||||||
import ManageUsers from "@/pages/Admin/ManageUsers";
|
import ManageUsers from "@/pages/Admin/ManageUsers";
|
||||||
import Settings from "@/pages/Admin/Settings";
|
import Settings from "@/pages/Admin/Settings";
|
||||||
import About from "@/pages/Admin/About";
|
import About from "@/pages/Admin/About";
|
||||||
|
import Toolbox from "@/pages/Admin/Toolbox";
|
||||||
import { store } from "../store";
|
import { store } from "../store";
|
||||||
|
|
||||||
export const adminRoutes = {
|
export const adminRoutes = {
|
||||||
path: "/admin",
|
path: "/admin",
|
||||||
component: Admin,
|
component: Admin,
|
||||||
beforeEnter: (to, _from, next) => {
|
beforeEnter: (to, _from, next) => {
|
||||||
|
@ -72,6 +73,13 @@ export const adminRoutes = {
|
||||||
title: "settings.site-settings",
|
title: "settings.site-settings",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "toolbox",
|
||||||
|
component: Toolbox,
|
||||||
|
meta: {
|
||||||
|
title: "settings.toolbox.toolbox",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "about",
|
path: "about",
|
||||||
component: About,
|
component: About,
|
||||||
|
|
|
@ -11,28 +11,28 @@ site_settings2categories = sa.Table(
|
||||||
"site_settings2categoories",
|
"site_settings2categoories",
|
||||||
SqlAlchemyBase.metadata,
|
SqlAlchemyBase.metadata,
|
||||||
sa.Column("sidebar_id", sa.Integer, sa.ForeignKey("site_settings.id")),
|
sa.Column("sidebar_id", sa.Integer, sa.ForeignKey("site_settings.id")),
|
||||||
sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")),
|
sa.Column("category_id", sa.String, sa.ForeignKey("categories.id")),
|
||||||
)
|
)
|
||||||
|
|
||||||
group2categories = sa.Table(
|
group2categories = sa.Table(
|
||||||
"group2categories",
|
"group2categories",
|
||||||
SqlAlchemyBase.metadata,
|
SqlAlchemyBase.metadata,
|
||||||
sa.Column("group_id", sa.Integer, sa.ForeignKey("groups.id")),
|
sa.Column("group_id", sa.Integer, sa.ForeignKey("groups.id")),
|
||||||
sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")),
|
sa.Column("category_id", sa.String, sa.ForeignKey("categories.id")),
|
||||||
)
|
)
|
||||||
|
|
||||||
recipes2categories = sa.Table(
|
recipes2categories = sa.Table(
|
||||||
"recipes2categories",
|
"recipes2categories",
|
||||||
SqlAlchemyBase.metadata,
|
SqlAlchemyBase.metadata,
|
||||||
sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")),
|
sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")),
|
||||||
sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")),
|
sa.Column("category_id", sa.String, sa.ForeignKey("categories.id")),
|
||||||
)
|
)
|
||||||
|
|
||||||
custom_pages2categories = sa.Table(
|
custom_pages2categories = sa.Table(
|
||||||
"custom_pages2categories",
|
"custom_pages2categories",
|
||||||
SqlAlchemyBase.metadata,
|
SqlAlchemyBase.metadata,
|
||||||
sa.Column("custom_page_id", sa.Integer, sa.ForeignKey("custom_pages.id")),
|
sa.Column("custom_page_id", sa.Integer, sa.ForeignKey("custom_pages.id")),
|
||||||
sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")),
|
sa.Column("category_id", sa.String, sa.ForeignKey("categories.id")),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,6 +52,9 @@ class Category(SqlAlchemyBase):
|
||||||
self.name = name.strip()
|
self.name = name.strip()
|
||||||
self.slug = slugify(name)
|
self.slug = slugify(name)
|
||||||
|
|
||||||
|
def update(self, name, session=None) -> None:
|
||||||
|
self.__init__(name, session)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_ref(session, slug: str):
|
def get_ref(session, slug: str):
|
||||||
return session.query(Category).filter(Category.slug == slug).one()
|
return session.query(Category).filter(Category.slug == slug).one()
|
||||||
|
|
|
@ -11,7 +11,7 @@ recipes2tags = sa.Table(
|
||||||
"recipes2tags",
|
"recipes2tags",
|
||||||
SqlAlchemyBase.metadata,
|
SqlAlchemyBase.metadata,
|
||||||
sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")),
|
sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")),
|
||||||
sa.Column("tag_slug", sa.Integer, sa.ForeignKey("tags.slug")),
|
sa.Column("tag_id", sa.Integer, sa.ForeignKey("tags.id")),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,6 +31,9 @@ class Tag(SqlAlchemyBase):
|
||||||
self.name = name.strip()
|
self.name = name.strip()
|
||||||
self.slug = slugify(self.name)
|
self.slug = slugify(self.name)
|
||||||
|
|
||||||
|
def update(self, name, session=None) -> None:
|
||||||
|
self.__init__(name, session)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_if_not_exist(session, name: str = None):
|
def create_if_not_exist(session, name: str = None):
|
||||||
test_slug = slugify(name)
|
test_slug = slugify(name)
|
||||||
|
|
|
@ -33,6 +33,18 @@ def get_all_recipes_by_category(category: str, session: Session = Depends(genera
|
||||||
return db.categories.get(session, category)
|
return db.categories.get(session, category)
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/{category}", response_model=RecipeCategoryResponse)
|
||||||
|
async def update_recipe_category(
|
||||||
|
category: str,
|
||||||
|
new_category: CategoryIn,
|
||||||
|
session: Session = Depends(generate_session),
|
||||||
|
current_user=Depends(get_current_user),
|
||||||
|
):
|
||||||
|
""" Updates an existing Tag in the database """
|
||||||
|
|
||||||
|
return db.categories.update(session, category, new_category.dict())
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{category}")
|
@router.delete("/{category}")
|
||||||
async def delete_recipe_category(
|
async def delete_recipe_category(
|
||||||
category: str, session: Session = Depends(generate_session), current_user=Depends(get_current_user)
|
category: str, session: Session = Depends(generate_session), current_user=Depends(get_current_user)
|
||||||
|
|
|
@ -29,21 +29,21 @@ async def create_recipe_tag(
|
||||||
return db.tags.create(session, tag.dict())
|
return db.tags.create(session, tag.dict())
|
||||||
|
|
||||||
|
|
||||||
@router.put("")
|
|
||||||
async def update_recipe_tag(
|
|
||||||
tag: TagIn, session: Session = Depends(generate_session), current_user=Depends(get_current_user)
|
|
||||||
):
|
|
||||||
""" Creates a Tag in the database """
|
|
||||||
|
|
||||||
return "NOT IMPLEMENTED"
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{tag}", response_model=RecipeTagResponse)
|
@router.get("/{tag}", response_model=RecipeTagResponse)
|
||||||
def get_all_recipes_by_tag(tag: str, session: Session = Depends(generate_session)):
|
def get_all_recipes_by_tag(tag: str, session: Session = Depends(generate_session)):
|
||||||
""" Returns a list of recipes associated with the provided tag. """
|
""" Returns a list of recipes associated with the provided tag. """
|
||||||
return db.tags.get(session, tag)
|
return db.tags.get(session, tag)
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/{tag}", response_model=RecipeTagResponse)
|
||||||
|
async def update_recipe_tag(
|
||||||
|
tag: str, new_tag: TagIn, session: Session = Depends(generate_session), current_user=Depends(get_current_user)
|
||||||
|
):
|
||||||
|
""" Updates an existing Tag in the database """
|
||||||
|
|
||||||
|
return db.tags.update(session, tag, new_tag.dict())
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{tag}")
|
@router.delete("/{tag}")
|
||||||
async def delete_recipe_tag(
|
async def delete_recipe_tag(
|
||||||
tag: str, session: Session = Depends(generate_session), current_user=Depends(get_current_user)
|
tag: str, session: Session = Depends(generate_session), current_user=Depends(get_current_user)
|
||||||
|
|
|
@ -2,6 +2,7 @@ from typing import List, Optional
|
||||||
|
|
||||||
from fastapi_camelcase import CamelModel
|
from fastapi_camelcase import CamelModel
|
||||||
from mealie.schema.recipe import Recipe
|
from mealie.schema.recipe import Recipe
|
||||||
|
from pydantic.utils import GetterDict
|
||||||
|
|
||||||
|
|
||||||
class CategoryIn(CamelModel):
|
class CategoryIn(CamelModel):
|
||||||
|
@ -15,6 +16,13 @@ class CategoryBase(CategoryIn):
|
||||||
class Config:
|
class Config:
|
||||||
orm_mode = True
|
orm_mode = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def getter_dict(_cls, name_orm):
|
||||||
|
return {
|
||||||
|
**GetterDict(name_orm),
|
||||||
|
"total_recipes": len(name_orm.recipes),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class RecipeCategoryResponse(CategoryBase):
|
class RecipeCategoryResponse(CategoryBase):
|
||||||
recipes: Optional[List[Recipe]]
|
recipes: Optional[List[Recipe]]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue