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

Django进阶关联表/模型:多场景文件关联的复用实现问询

可行实现思路

一、自定义关联字段方案

这是最直观的复用方式,针对每个文件类型定义绑定category的字段,自动处理文件创建和关联逻辑:

  • 定义继承自ForeignKey的自定义字段FileCategoryForeignKey,初始化时接收category参数
  • 重写字段的pre_save方法,当关联的文件字段有上传时,自动在all_files表创建对应category的记录,并把ID赋值给当前字段
  • 示例代码:
from django.db import models
from .models import AllFiles

class FileCategoryForeignKey(models.ForeignKey):
    def __init__(self, category, **kwargs):
        self.category = category
        super().__init__(AllFiles, **kwargs)

    def pre_save(self, model_instance, add):
        # 匹配对应的文件字段(比如contract_file_id对应contract_file)
        file_field_name = self.name.replace('_id', '')
        file_obj = getattr(model_instance, file_field_name)
        if file_obj and add:
            all_file = AllFiles.objects.create(
                category=self.category,
                file=file_obj
            )
            setattr(model_instance, self.name, all_file.id)
        return super().pre_save(model_instance, add)
  • 使用示例:
class WorkOrder(models.Model):
    owner_id = models.UUIDField()
    contract_file = models.FileField(upload_to='contracts/', blank=True)
    contract_file_id = FileCategoryForeignKey(
        category='contract',
        on_delete=models.SET_NULL,
        null=True,
        blank=True
    )

二、Mixin方案

针对模型层面复用,封装文件上传和关联的通用逻辑:

  • 定义FileAssociatedMixin类,子类需声明file_category_map属性,指定{文件字段名: 对应category}的映射
  • 在Mixin的save方法中统一处理文件上传,自动创建all_files记录并关联
  • 示例代码:
class FileAssociatedMixin:
    file_category_map = {}

    def save(self, *args, **kwargs):
        add = self._state.adding
        if add:
            for file_field, category in self.file_category_map.items():
                file_obj = getattr(self, file_field)
                if file_obj:
                    all_file = AllFiles.objects.create(
                        category=category,
                        file=file_obj
                    )
                    setattr(self, f"{file_field}_id", all_file.id)
        super().save(*args, **kwargs)
  • 使用示例:
class WorkOrder(FileAssociatedMixin, models.Model):
    owner_id = models.UUIDField()
    contract_file = models.FileField(upload_to='contracts/', blank=True)
    contract_file_id = models.ForeignKey(AllFiles, on_delete=models.SET_NULL, null=True, blank=True)

    file_category_map = {
        'contract_file': 'contract'
    }

三、元类方案

适合批量处理多模型场景,自动为模型添加关联字段和处理逻辑:

  • 定义FileAssociatedMeta元类,扫描模型Meta类的file_fields配置,自动生成关联ID字段并绑定save逻辑
  • 示例代码:
class FileAssociatedMeta(models.base.ModelBase):
    def __new__(cls, name, bases, attrs):
        file_config = attrs.get('Meta', {}).get('file_fields', {})
        # 自动生成关联ID字段
        for file_field, category in file_config.items():
            attrs[f"{file_field}_id"] = models.ForeignKey(
                AllFiles,
                on_delete=models.SET_NULL,
                null=True,
                blank=True
            )
        # 创建模型类并覆盖save方法
        model_class = super().__new__(cls, name, bases, attrs)
        original_save = model_class.save
        
        def new_save(self, *args, **kwargs):
            add = self._state.adding
            if add:
                for file_field, category in self._meta.file_fields.items():
                    file_obj = getattr(self, file_field)
                    if file_obj:
                        all_file = AllFiles.objects.create(
                            category=category,
                            file=file_obj
                        )
                        setattr(self, f"{file_field}_id", all_file.id)
            original_save(self, *args, **kwargs)
        
        model_class.save = new_save
        return model_class
  • 使用示例:
class WorkOrder(models.Model, metaclass=FileAssociatedMeta):
    owner_id = models.UUIDField()
    contract_file = models.FileField(upload_to='contracts/', blank=True)

    class Meta:
        file_fields = {
            'contract_file': 'contract'
        }

四、文件访问时的owner_id查询处理

在AllFiles模型中添加自定义方法,根据category反向查询对应模型的owner_id:

import uuid
from django.db import models
from .models import WorkOrder, LabourLicense

class AllFiles(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    category = models.CharField(max_length=50)
    file = models.FileField(upload_to='files/')

    def get_owner_id(self):
        # 映射category到对应模型和关联字段
        model_map = {
            'contract': (WorkOrder, 'contract_file_id'),
            'labour_lic': (LabourLicense, 'license_file_id')
        }
        model_class, field_name = model_map.get(self.category, (None, None))
        if model_class:
            obj = model_class.objects.filter(**{field_name: self.id}).first()
            return obj.owner_id if obj else None

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 04:12:03