Skip to content

后端模型

定义模型类

模型类与数据库关系

  • 每一个模型类(继承类django.db.models.Model)都映射一张数据库表
  • 模型类的每个属性都相当于一个数据库的字段。

以下是Django常用字段类型及其描述:

字段类型描述
AutoField一个自增的整数类型字段,通常用于主键。如果不指定,Django会自动创建一个名为id的AutoField字段。
BooleanField布尔值字段,表示True或False。
CharField字符串字段,用于较短的文本。必须指定max_length参数。
TextField大文本字段,用于存储较长的文本。
IntegerField整数字段。
BigIntegerField大整数字段,适用于整数范围超过IntegerField上限的场景。
SmallIntegerField小整数字段,适用于整数范围较小的场景。
PositiveIntegerField正整数字段。
PositiveSmallIntegerField正小整数字段。
FloatField浮点数字段。
DecimalField用于精确表示小数,指定小数位数。必须指定max_digits和decimal_places参数。
DateTimeField日期和时间字段,对应Python的datetime.datetime实例。
DateField日期字段,对应Python的datetime.date实例。
TimeField时间字段,对应Python的datetime.time实例。
DurationField时间间隔字段,用于表示时间段,对应Python的timedelta。
EmailField电子邮件字段,使用CharField进行验证。
FileField文件上传字段。
ImageField图像上传字段,继承自FileField,但会对上传的文件进行验证,确保其为有效的图像。
URLFieldURL字段,使用CharField进行验证。
UUIDFieldUUID字段,用于存储全局唯一标识符。
ForeignKey外键字段,用于表示一对多关系。
ManyToManyField多对多字段,用于表示多对多关系。
OneToOneField一对一字段,用于表示一对一关系。

示例:进入目录django-vue3-admin\backend\my_demo,文件models.py

  • CrudDemoModel是一个模型类,将在数据库中创建对应的一张数据库表goods
  • goods、inventory、goods_price、purchase_goods_date属性,分别对应数据库一个字段
python
from django.db import models
from dvadmin.utils.models import CoreModel


class CrudDemoModel(CoreModel):
    goods = models.CharField(max_length=255, verbose_name="商品")
    inventory = models.IntegerField(verbose_name="库存量")
    goods_price = models.FloatField(verbose_name="商品价格")
    # 日期类型为DateField,时间类型为DateTimeField
    purchase_goods_date = models.DateField(verbose_name="进货日期")

    class Meta:
        db_table = "goods"
        verbose_name = "商品表"
        verbose_name_plural = verbose_name
        ordering = ("-create_datetime",)

使用模型

一旦你定义了你的模型,你需要告诉 Django 你准备 使用 这些模型。你需要修改设置文件中的 INSTALLED_APPS ,在这个设置中添加包含 models.py 文件的模块名称。

python
INSTALLED_APPS = [
    # ...
    "my_demo",
    # ...
]

当你向 INSTALLED_APPS 添加新的应用的时候,使用以下命令进行迁移

python
python manage.py makemigrations
python manage.py migrate

image-20240923155931832

迁移

迁移是 Django 将你对模型的修改(例如增加一个字段,删除一个模型)应用至数据库架构中的方式。将迁移看作是数据库架构的版本控制系统。 makemigrations 负责将模型修改打包进独立的迁移文件中——类似提交修改,而 migrate 负责将其应用至数据库。以下是几个常用的与迁移交互的命令:

每执行一次makemigrations命令,会生成迁移文件myapp/migrations/xxxx_initial.py。迁移文件中的dependencies指定了它们所依赖的其他哪些迁移。

sh
python manage.py makemigrations

#执行示例1
PS E:\python_workspace\django\dvadmin\django-vue3-admin\backend> python .\manage.py makemigrations
<class 'list'>
No changes detected  #模型没有修改,所以不会产生迁移文件

#执行示例2
PS E:\python_workspace\django\dvadmin\django-vue3-admin\backend> python .\manage.py makemigrations
<class 'list'>
Migrations for 'my_demo':
  my_demo\migrations\0002_cruddemomodel_brand.py #生成迁移文件
    - Add field brand to cruddemomodel

生成的迁移文件示例

python
# 0001_initial.py迁移文件
initial = True
dependencies = [
    migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]


# 0002_cruddemomodel_brand.py迁移文件
dependencies = [
    ('my_demo', '0001_initial'),
]

每执行一次migrate命令,会在数据库中的django_migrations表创建一条记录,并将迁移文件应用到数据库。

sh
python manage.py migrate

image-20240924081654141

撤销迁移

撤销迁移并还原数据库参考步骤

  1. 登录数据库,查看表django_migrations,查看目前已执行的迁移文件

  2. image-20240927153214236

  3. 根据app、name字段,找到对应的迁移文件,并查看其内容

  4. image-20240927153401341

  5. 根据迁移文件内容,手动还原数据库相应操作。

  6. 删除表django_migrations相应的记录。

前端表单

表单配置文件

配置表单界面对应文件django-vue3-admin\web\src\views\MyDemo\CrudDemoModelViewSet\crud.tsx

ts和tsx文件区别

在TypeScript中,.ts 和.tsx 文件扩展名代表两种不同的文件类型:.ts:这是一个标准的TypeScript文件,它可以包含TypeScript代码,该代码最终会被编译成 JavaScript。.ts 文件通常不包含JSX代码,JSX是一种JavaScript的语法扩展,允许你在JavaScript代码中写类似HTML的标记。.tsx:这是一个包含JSX语法的TypeScript文件。

image-20240923161450042

表单类型input/number/text/date

表单类型

  • input:短文本输入
  • number:整数选择
  • text:长文件输入
  • date:日期选择
typescript
columns: {
    goods: {
        title: '商品',
        type: 'input',
        search: { show: true },
        column: {
            minWidth: 120,
            sortable: 'custom',
        },
        form: {
            helper: {
                render() {
                    return <div style={'color:blue'}>商品是必需要填写的</div>;
                },
            },
            rules: [{ required: true, message: '商品名称必填' }],
            component: {
                placeholder: '请输入商品名称',
            },
        },
    },
    inventory: {
        title: '库存量',
        type: 'number',
        search: { show: false },
        column: {
            minWidth: 120,
            sortable: 'custom',
        },
        form: {
            rules: [{ required: true, message: '库存量必填' }],
            component: {
                placeholder: '请输入库存量',
            },
        },
    },

    goods_price: {
        title: '商品定价',
        type: 'text',
        search: { show: false },
        column: {
            minWidth: 120,
            sortable: 'custom',
        },
        form: {
            rules: [{ required: true, message: '商品定价必填' }],
            component: {
                placeholder: '请输入商品定价',
            },
        },
    },
    purchase_goods_date: {
        title: '进货时间',
        type: 'date',
        search: { show: false },
        form: {
            // rules: [{ required: true, message: '进货时间必填' }],
            component: {
                //显示格式化
                format: 'YYYY-MM-DD',
                //输入值格式
                valueFormat: 'YYYY-MM-DD',
                placeholder: '请输入进货时间',
            },
        },
        column: {
            align: 'center',
            width: 120,
            component: { name: 'fs-date-format', format: 'YYYY-MM-DD' },
        },
    },
},

表单类型dict-radio

表单类型dict-radio:单选按钮

ts
is_shelves: {
    title: '是否上架',
    search: { show: false },
    type: 'dict-radio',
    dict: dict({
        data: [
            {
                label: '是',
                value: true,
                color: 'success',
            },
            {
                label: '否',
                value: false,
                color: 'danger',
            },
        ],
    }),
    column: {
        width: 130,
        sortable: 'custom',
    },
    form: {
        rules: [{ required: true, message: '是否上架必填' }],
        value: false,
    },
},

后端models.py配置

python
# 是否上架,默认为True
    is_shelves = models.BooleanField(verbose_name="是否上架", default=True)

查看效果

image-20240925080826633

表单类型dict-select

表单类型dict-select:下拉选择

ts
import { dictionary } from '/@/utils/dictionary';

goods_status: {
    title: '商品状态',
    type: 'dict-select',
    dict: dict({
        value: 'label',
        label: 'label',
        data: dictionary('goods_status'),
    }),
    form: {
        component: {
            span: 12,
        },
    },
    component: { props: { color: 'auto' } }, // 自动染色
},

后端models.py配置

python
# 商品状态(Status):商品的状态(在售、下架、缺货、其它)
goods_status = models.CharField(
    max_length=255, verbose_name="商品状态", blank=True, null=True
)

启动项目,登录--常规配置--字典管理,添加

image-20240924141655983

刚才添加的字典右侧--字典配置,依次添加

image-20240924142111553

image-20240924142156290

按ctrl+F5强制刷新网页,查看效果

image-20240924142346811

表单类型dict-select一对一关联

表单类型dict-select一对一关联:下拉选择时,只显示未被引用的对象。

配置后端

python
# 配置模型层:文件models.py
class TraceabilityModel(CoreModel):
    supplier = models.CharField(max_length=255, verbose_name="供应商")
    batch_number = models.CharField(max_length=255, verbose_name="批次号")

    class Meta:
        db_table = "traceability"
        verbose_name = "商品溯源表"
        verbose_name_plural = verbose_name
        ordering = ("-create_datetime",)
class CrudDemoModel(CoreModel):
    #一对一关联字段,对应数据库字段名称为 traceability_id :增加这句
    traceability = models.OneToOneField(
        TraceabilityModel,
        on_delete=models.PROTECT,
        verbose_name="商品溯源",
        blank=True,
        null=True,
    )
    
    
# 配置序列化器:文件serializers.py
class TraceabilityModelSerializer(CustomModelSerializer):
    """TraceabilityModel的序列化器"""
    class Meta:
        model = TraceabilityModel
        fields = "__all__"

        
# 配置视图:文件views.py
class TraceabilityModelViewSet(CustomModelViewSet):
    queryset = TraceabilityModel.objects.all()
    serializer_class = TraceabilityModelSerializer
    # 改写get_queryset方法,返回未关联的TraceabilityModel对象
    def get_queryset(self):
        return TraceabilityModel.objects.filter(cruddemomodel=None)

    
# 配置URL路由:文件urls.py
from django.urls import path
from rest_framework import routers
from .views import (
    CrudDemoModelViewSet,
    BookViewSet,
    TraceabilityModelViewSet,
    StudentListCreate,
    StudentRetrieveUpdateDestroy,
)
router = routers.SimpleRouter()
router.register("api/CrudDemoModelViewSet", CrudDemoModelViewSet)
router.register("api/traceability", TraceabilityModelViewSet) #增加这句
router.register("api/books", BookViewSet)
urlpatterns = [
    path("api/students/", StudentListCreate.as_view()),
    path("api/students/<int:pk>/", StudentRetrieveUpdateDestroy.as_view()),
]
urlpatterns = urlpatterns + router.urls

执行数据迁移

python
python manage.py makemigrations
python manage.py migrate

image-20240925090628846

前端配置

进入目录django-vue3-admin\web\src\views\MyDemo\CrudDemoModelViewSet\,编辑文件crud.tsx,添加如下代码。

tsx
traceability_id: {
    title: '商品溯源',
    search: {
        // disabled: true,
        show: true,
    },
    type: 'dict-select',
    column: {
        minWidth: 150, //最小列宽
        component: {
            dict: dict({
                isTree: true,
                url: '/api/traceability/',
                value: 'id',
                label: 'supplier',
                getData: async ({ url }: { url: string }) => {
                    return request({
                        url: url,
                    }).then((ret: any) => {
                        return ret.data;
                    });
                },
            }),
        },
    },
    form: {
        /* 功能说明:这里添加了一个按钮做跳转至相应的对象,以便随时方便预览、新增、编辑、对象*/
        suffixRender({ value }) {
            //这里把当前的value传进去
            function goto({ value }: { value: any }) {
                if (!value) {
                    // 这里判断当选项没有值是,用户又点了跳转按钮,那说明是需要新增对象
                    // value = 0;
                    value = 1;
                }
                // 这里是跳转至相应的路由,把value给到id
                //这里整个配置主要name:'FormNewPageEdit'便可
                router.push({ name: 'FormNewPageEdit', params: { id: value } });
            }
            return (
                // 这里配置一个跳转的按钮及引用函数
                <el-button-group style={'padding-left:5px'}>
                    <fs-button onClick={() => goto({ value })}>跳转</fs-button>
                </el-button-group>
            );
        },
        component: {
            filterable: true,
            placeholder: '请选择',
            props: {
                props: {
                    value: 'id',
                    label: 'licence_num',
                },
            },
            dict: dict({
                prototype: true,
                isTree: true,
                url: '/api/traceability/',
                value: 'id',
                label: 'supplier',
                getData: async ({ url, form }: { url: string; form: any }) => {
                    // 这里专门传了当前的form进来,是为了拿到他的值form.soft_licence然后再进行拼接路由,这样后端就可以拿到当前form的某个值进行筛选了。
                    // 注意这里要想拿到form.soft_licence字段是根据自己的model表解决了,要根本使用情况而修改
                    let pkid = 1; //这时初始设置为0,配置拿不到值的情况
                    if (form.soft_licence) {
                        pkid = form.soft_licence; //这是配置拿到值的情况
                    }
                    return request({
                        url: url + pkid + '/', //这是做了一个拼接路由URL地址
                    }).then((ret: any) => {
                        return ret.data;
                    });
                },
            }),
        },
    },
},

测试效果:(待补充)

排错指南

makemigrations时提示需要提供默认值

错误描述

执行命令python manager.py makemigrations时提示如下

sh
PS E:\python_workspace\django\dvadmin\django-vue3-admin\backend> python .\manage.py makemigrations
<class 'list'>
It is impossible to add a non-nullable field 'brand' to cruddemomodel without specifying a default. This is because the database needs something to populate existing rows.
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit and manually define a default value in models.py.
Select an option:

原因分析

因为在models.py中增加也字段brand,但没有指定默认值或允许为空。在数据库增加字段时,数据库会检查每一条数据的完整性。要么指定该数据库字段的默认值,要么设置允许为空。

解决方法

选择1表示为现有数据指定一个默认值,选择2表示退出本次makemigrations并自行去修改models.py。建议选择2。

提示请求地址出错: /api/traceability/a/0/

错误描述:登录系统--商品管理--商品信息,提示【请求地址出错: /api/traceability/a/0/】

解决方法:

访问对应后端地址:http://127.0.0.1:8000/api/traceability/a/0/,提示【Page not found (404)】

image-20240927142145726

检查并更正前端请求地址

提示接口地址不正确: /api/traceability/0/

错误描述:登录系统--商品管理--商品信息,提示【接口地址不正确: /api/traceability/0/】

image-20240927142325492

解决方法:

访问对应后端地址:http://127.0.0.1:8000/api/traceability/0/,返回JSON数据为:

json
{
    "code": 400,
    "data": null,
    "msg": "接口地址不正确"
}

image-20240927142416816

检查并更正前端请求地址

参考资料


有任何疑问,欢迎在技术交流区留下您的见解,一起交流成长!