Adding filter capacities to api client and server

This commit is contained in:
2025-01-20 16:08:42 +01:00
parent 35448385c5
commit 0a4bec62c1
7 changed files with 32 additions and 36 deletions

View File

@@ -1,12 +1,10 @@
from uuid import UUID from uuid import UUID
from fastapi import APIRouter, HTTPException, Depends
from fastapi import APIRouter, HTTPException, Depends, Query
from sqlmodel import Field, Session, SQLModel, create_engine, select
from fastapi_pagination import Page from fastapi_pagination import Page
from fastapi_pagination.ext.sqlmodel import paginate from fastapi_pagination.ext.sqlmodel import paginate
from .models import Account, AccountCreate, AccountRead, AccountUpdate from account.models import Account, AccountCreate, AccountRead, AccountUpdate
from db import SessionDep from db import SessionDep
from user.manager import get_current_user from user.manager import get_current_user

View File

@@ -1,6 +1,8 @@
from uuid import UUID, uuid4 from uuid import UUID, uuid4
from enum import Enum from enum import Enum
from typing import Optional
from fastapi_filter.contrib.sqlalchemy import Filter
from sqlmodel import Field, SQLModel, select from sqlmodel import Field, SQLModel, select
class CategoryBase(SQLModel): class CategoryBase(SQLModel):
@@ -21,8 +23,8 @@ class Category(CategoryRead, table=True):
return category_db return category_db
@classmethod @classmethod
def list(cls): def list(cls, filters):
return select(Category) return filters.filter(select(cls))
@classmethod @classmethod
def get(cls, session, category_id): def get(cls, session, category_id):
@@ -49,3 +51,10 @@ class CategoryCreate(CategoryWrite):
class CategoryUpdate(CategoryWrite): class CategoryUpdate(CategoryWrite):
pass pass
class CategoryFilters(Filter):
name__like: Optional[str] = None
class Constants(Filter.Constants):
model = Category
search_model_fields = ["name"]

View File

@@ -1,11 +1,12 @@
from uuid import UUID from uuid import UUID
from fastapi import APIRouter, HTTPException, Depends from fastapi import APIRouter, HTTPException, Depends
from fastapi_filter import FilterDepends
from fastapi_pagination import Page from fastapi_pagination import Page
from fastapi_pagination.ext.sqlmodel import paginate from fastapi_pagination.ext.sqlmodel import paginate
from pydantic import BaseModel
from category.models import Category, CategoryCreate, CategoryRead, CategoryUpdate from category.models import Category, CategoryCreate, CategoryRead, CategoryUpdate, CategoryFilters
from db import SessionDep from db import SessionDep
from user.manager import get_current_user from user.manager import get_current_user
@@ -17,8 +18,10 @@ def create_category(category: CategoryCreate, session: SessionDep, current_user=
return category return category
@router.get("") @router.get("")
def read_categories(session: SessionDep, current_user=Depends(get_current_user)) -> Page[CategoryRead]: def read_categories(session: SessionDep,
return paginate(session, Category.list()) filters: CategoryFilters = FilterDepends(CategoryFilters),
current_user=Depends(get_current_user)) -> Page[CategoryRead]:
return paginate(session, Category.list(filters))
@router.get("/{category_id}") @router.get("/{category_id}")
def read_category(category_id: UUID, session: SessionDep, current_user=Depends(get_current_user)) -> CategoryRead: def read_category(category_id: UUID, session: SessionDep, current_user=Depends(get_current_user)) -> CategoryRead:

View File

@@ -5,3 +5,4 @@ fastapi
fastapi-pagination fastapi-pagination
fastapi-users[sqlmodel] fastapi-users[sqlmodel]
fastapi-users-db-sqlmodel fastapi-users-db-sqlmodel
fastapi-filter[sqlalchemy]

View File

@@ -10,6 +10,6 @@ export default function CrudTextWidget<T = any, S extends StrictRJSFSchema = RJS
if (props.schema.hasOwnProperty("foreign_key")) { if (props.schema.hasOwnProperty("foreign_key")) {
return (<ForeignKeyWidget {...props} />); return (<ForeignKeyWidget {...props} />);
}else { }else {
return (<CrudTextWidget {...props} />); return (<TextWidget {...props} />);
} }
} }

View File

@@ -11,38 +11,24 @@ import {useList} from "@refinedev/core";
export const ForeignKeyWidget = (props: WidgetProps) => { export const ForeignKeyWidget = (props: WidgetProps) => {
const [inputValue, setInputValue] = useState(""); const [inputValue, setInputValue] = useState("");
const [selectedValue, setSelectedValue] = useState<string | null>(null); const [selectedValue, setSelectedValue] = useState<string | null>(null);
const [debouncedInputValue, setDebouncedInputValue] = useState(inputValue);
const resource = props.schema.foreign_key.reference.resource const resource = props.schema.foreign_key.reference.resource
useEffect(() => {
const handler = setTimeout(() => setDebouncedInputValue(inputValue), 300); // Adjust debounce delay as needed
return () => clearTimeout(handler);
}, [inputValue]);
const { data, isLoading } = useList({ const { data, isLoading } = useList({
resource: resource, resource: resource,
pagination: { current: 1, pageSize: 10 }, pagination: { current: 1, pageSize: 10 },
filters: [{ field: "name", operator: "contains", value: debouncedInputValue }],
sorters: [{ field: "name", order: "asc" }], sorters: [{ field: "name", order: "asc" }],
filters: [{ field: "name", operator: "contains", value: "input" }],
}); });
const options = data?.data || []; const options = data?.data || [];
// const fetchOptions = async (input: string) => {
// try {
//
// } catch (error) {
// console.error("Error fetching options:", error);
// }
// };
// // Debounced version of the fetch function
// const debouncedFetch = useCallback(debounce(fetchOptions, 300), []);
// // Trigger fetch whenever the inputValue changes
// useEffect(() => {
// if (inputValue) {
// debouncedFetch(inputValue);
// } else {
// setOptions([]); // Clear options when input is empty
// }
// }, [inputValue, debouncedFetch]);
return ( return (
<Autocomplete <Autocomplete
value={selectedValue} value={selectedValue}

View File

@@ -55,9 +55,8 @@ export const dataProvider: DataProvider = {
if (filters && filters.length > 0) { if (filters && filters.length > 0) {
filters.forEach((filter) => { filters.forEach((filter) => {
if ("field" in filter && filter.operator === "eq") { if ("field" in filter && filter.value.length > 0 && filter.operator === "contains") {
// Our fake API supports "eq" operator by simply appending the field name and value to the query string. params.append(filter.field + "__like", "%" + filter.value + "%");
params.append(filter.field, filter.value);
} }
}); });
} }