You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Prisma模型关联外键设为可选?及表单默认值报错解决

解决NextJS + Prisma中Product模型可选字段与useForm类型不兼容问题

1. 确认Prisma Schema修改及客户端生成

首先确保你的Prisma Schema修改正确,为sizeId、colorId及关联字段添加?标记:

model Product {
  id        Int      @id @default(autoincrement())
  name      String
  sizeId    Int?
  colorId   Int?
  size      Size?    @relation(fields: [sizeId], references: [id])
  color     Color?   @relation(fields: [colorId], references: [id])
  // 其他业务字段
}

model Size {
  id    Int      @id @default(autoincrement())
  name  String
  products Product[]
}

model Color {
  id    Int      @id @default(autoincrement())
  name  String
  products Product[]
}

修改完成后必须重新生成Prisma客户端,否则类型不会更新:

npx prisma generate

2. 适配useForm的defaultValues类型

根据表单是创建新商品还是编辑已有商品,分别处理:

场景1:创建新商品

直接在defaultValues中为sizeId、colorId设置undefined或null,匹配Prisma生成的可选类型:

import { useForm } from 'react-hook-form';
import type { Product } from '@prisma/client';

// 排除表单不需要的字段(如id、创建时间等)
type ProductFormInput = Omit<Product, 'id' | 'createdAt' | 'updatedAt'>;

export default function ProductForm() {
  const { register, handleSubmit } = useForm<ProductFormInput>({
    defaultValues: {
      name: '',
      sizeId: undefined,
      colorId: undefined,
      // 其他字段默认值
    },
  });

  const onSubmit = handleSubmit(async (data) => {
    await prisma.product.create({ data });
    // 提交后的逻辑(如跳转)
  });

  return (
    <form onSubmit={onSubmit}>
      {/* 表单输入组件 */}
      <input {...register('name')} placeholder="商品名称" />
      {/* 尺寸、颜色选择组件 */}
    </form>
  );
}

场景2:编辑已有商品

从数据库获取商品数据后,直接将其作为defaultValues传入,数据库返回的sizeId/colorId可能为null,天然匹配可选类型:

import { useForm } from 'react-hook-form';
import type { Product } from '@prisma/client';
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import { prisma } from '@/lib/prisma';

export default function EditProductForm() {
  const router = useRouter();
  const productId = router.query.id as string;
  const [product, setProduct] = useState<Product | null>(null);

  useEffect(() => {
    if (productId) {
      const fetchProduct = async () => {
        const data = await prisma.product.findUnique({
          where: { id: parseInt(productId) },
        });
        setProduct(data);
      };
      fetchProduct();
    }
  }, [productId]);

  const { register, handleSubmit } = useForm<Product>({
    defaultValues: product,
    mode: 'onChange',
  });

  const onSubmit = handleSubmit(async (data) => {
    await prisma.product.update({
      where: { id: parseInt(productId) },
      data,
    });
    router.push('/products');
  });

  return (
    <form onSubmit={onSubmit}>
      {/* 表单输入组件 */}
    </form>
  );
}

3. 其他类型兼容处理

如果手动定义表单类型,确保sizeId和colorId的类型为number | undefined | null,与Prisma生成的类型完全一致。若遇到临时类型冲突,可使用类型断言(尽量少用):

defaultValues: {
  name: '',
  sizeId: undefined as number | undefined,
  colorId: undefined as number | undefined,
}

常见误区

  • 未执行npx prisma generate:修改Schema后必须重新生成客户端,否则类型还是旧的必填类型。
  • 错误设置默认值:比如给sizeId设为0,这会被视为有效Int值,与可选类型的预期不符,应使用undefined或null。

内容的提问来源于stack exchange,提问作者Jean Mako

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 19:56:15