Fully functional implementation of the foreignKey

This commit is contained in:
2025-01-21 18:14:31 +01:00
committed by ewandor
parent 829d16c1c4
commit 38c5a69130
8 changed files with 41 additions and 33 deletions

View File

@@ -1,10 +1,10 @@
from uuid import UUID, uuid4
from enum import Enum
from sqlmodel import Field, SQLModel, select
from sqlmodel import Field, SQLModel, select, Relationship
from pydantic import Field as PydField
from category.models import CategoryRead
from category.models import CategoryRead, Category
class AccountType(Enum):
@@ -38,6 +38,7 @@ class AccountBaseId(AccountBase):
id: UUID | None = Field(default_factory=uuid4, primary_key=True)
class Account(AccountBaseId, table=True):
default_category: Category | None = Relationship()
@classmethod
def create(cls, account, session):
@@ -50,7 +51,7 @@ class Account(AccountBaseId, table=True):
@classmethod
def list(cls):
return select(Account)
return select(Account).join(Category)
@classmethod
def get(cls, session, account_id):
@@ -70,8 +71,7 @@ class Account(AccountBaseId, table=True):
session.commit()
class AccountRead(AccountBaseId):
# default_category: CategoryRead
pass
default_category: CategoryRead
class AccountWrite(AccountBase):
default_category_id: UUID = PydField(default=None, json_schema_extra={

View File

@@ -12,8 +12,8 @@ router = APIRouter()
@router.post("")
def create_account(account: AccountCreate, session: SessionDep, current_user=Depends(get_current_user)) -> AccountRead:
Account.create(account, session)
return account
result = Account.create(account, session)
return result
@router.get("")
def read_accounts(session: SessionDep, current_user=Depends(get_current_user)) -> Page[AccountRead]:

View File

@@ -4,7 +4,6 @@ from fastapi import APIRouter, HTTPException, Depends
from fastapi_filter import FilterDepends
from fastapi_pagination import Page
from fastapi_pagination.ext.sqlmodel import paginate
from pydantic import BaseModel
from category.models import Category, CategoryCreate, CategoryRead, CategoryUpdate, CategoryFilters
from db import SessionDep

View File

@@ -6,7 +6,7 @@ from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin, models, ex
from fastapi_users.authentication import BearerTransport, AuthenticationBackend
from fastapi_users.authentication.strategy.db import AccessTokenDatabase, DatabaseStrategy
from .models import User, get_user_db, AccessToken, get_access_token_db, UserRead, UserUpdate, UserCreate
from user.models import User, get_user_db, AccessToken, get_access_token_db, UserRead, UserUpdate, UserCreate
from db import get_session
SECRET = "SECRET"

View File

@@ -2,8 +2,8 @@ import uuid
from fastapi import APIRouter, Depends, HTTPException
from .models import User, UserCreate, UserRead, UserUpdate
from .manager import get_user_manager, get_current_user, get_current_superuser
from user.models import User, UserCreate, UserRead, UserUpdate
from user.manager import get_user_manager, get_current_user, get_current_superuser
from fastapi_pagination import Page
from fastapi_pagination.ext.sqlmodel import paginate

View File

@@ -1,8 +1,7 @@
import TextWidget from "@rjsf/core/lib/components/widgets/TextWidget";
import {FormContextType, getTemplate, RJSFSchema, StrictRJSFSchema, WidgetProps} from "@rjsf/utils";
import {ForeignKeyWidget} from "./foreign-key";
import { FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps } from "@rjsf/utils";
import { ForeignKeyWidget } from "./foreign-key";
export default function CrudTextWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
props: WidgetProps<T, S, F>

View File

@@ -1,45 +1,55 @@
import { RJSFSchema, UiSchema, WidgetProps, RegistryWidgetsType } from '@rjsf/utils';
import validator from '@rjsf/validator-ajv8';
import {Autocomplete, AutocompleteRenderInputParams, debounce} from "@mui/material";
import {useAutocomplete} from "@refinedev/mui";
import { Controller } from "react-hook-form";
import {useState, useEffect, useCallback} from "react";
import { WidgetProps } from '@rjsf/utils';
import { Autocomplete } from "@mui/material";
import { useState, useEffect } from "react";
import TextField from "@mui/material/TextField";
import {axiosInstance} from "@refinedev/simple-rest";
import {useList} from "@refinedev/core";
import { useList, useOne } from "@refinedev/core";
export const ForeignKeyWidget = (props: WidgetProps) => {
const [inputValue, setInputValue] = useState("");
const [selectedValue, setSelectedValue] = useState<string | null>(null);
const [debouncedInputValue, setDebouncedInputValue] = useState(inputValue);
const resource = props.schema.foreign_key.reference.resource
const labelField = props.schema.foreign_key.reference.label
const valueResult = useOne({
resource: resource,
id: props.value
});
const [inputValue, setInputValue] = useState<string>("");
const [selectedValue, setSelectedValue] = useState(valueResult.data?.data || null);
const [debouncedInputValue, setDebouncedInputValue] = useState<string>(inputValue);
useEffect(() => {
const handler = setTimeout(() => setDebouncedInputValue(inputValue), 300); // Adjust debounce delay as needed
return () => clearTimeout(handler);
}, [inputValue]);
const { data, isLoading } = useList({
const listResult = useList({
resource: resource,
pagination: { current: 1, pageSize: 10 },
filters: [{ field: "name", operator: "contains", value: debouncedInputValue }],
sorters: [{ field: "name", order: "asc" }],
});
const options = data?.data || [];
const options = listResult.data?.data || [];
const isLoading = listResult.isLoading || valueResult.isLoading;
if(! selectedValue && valueResult.data) {
setSelectedValue(valueResult.data?.data)
}
return (
<Autocomplete
value={selectedValue}
onChange={(event, newValue) => setSelectedValue(newValue)}
inputValue={inputValue}
onChange={(event, newValue) => {
setSelectedValue(newValue)
props.onChange(newValue ? newValue.id : "")
}}
//inputValue={inputValue}
onInputChange={(event, newInputValue) => setInputValue(newInputValue)}
options={options}
getOptionLabel={(option) => option.name}
getOptionLabel={(option) => option ? option[labelField] : ""}
loading={isLoading}
renderInput={(params) => (
<TextField {...params} label="Search" variant="outlined" />
<TextField {...params} label={ props.label } variant="outlined" />
)}
/>
);

View File

@@ -55,7 +55,7 @@ export const dataProvider: DataProvider = {
if (filters && filters.length > 0) {
filters.forEach((filter) => {
if ("field" in filter && filter.value.length > 0 && filter.operator === "contains") {
if ("field" in filter && filter.value && filter.operator === "contains") {
params.append(filter.field + "__like", "%" + filter.value + "%");
}
});