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

如何从子模型视图及后台访问基模型定义的翻译字段?

问题描述

接手一个Django项目,包含基于PolymorphicModel的多态模型,原结构如下:

from django.db import models
from polymorphic.models import PolymorphicManager, PolymorphicModel

class Product(PolymorphicModel):
    name=models.CharField(max_length=127)
    objects=PolymorphicManager()

class Book(Product):
    pass

class Drink(Product):
    pass

无法修改多态方案,需要用Parler给基模型Product的name字段添加翻译功能。修改后的模型代码存在笔误,调整前的版本如下:

from django.db import models
from parler.models import TranslatableModel, TranslatedFields
from parler.managers import TranslatableManager, TranslatableQuerySet
from polymorphic.models import PolymorphicManager, PolymorphicModel
from polymorphic.query import PolymorphicQuerySet

class ProductQuerySet(PolymorphicQuerySet, TranslatableQuerySet):
    pass

class ProductManager(PolymorphicManager, TranslatableManager):
    queryset_class = MarkerQuerySet  # 笔误:应为ProductQuerySet

class Product(PolymorphicModel):
    translations = TranslatedFields(
        name=models.CharField(max_length=127)
    # 缺失闭合括号
    objects=ProductManager()

class Book(Product):
    pass

class Drink(Product):
    pass

在Django Shell中测试正常,但出现两个问题:

  1. 接口测试报错:
.venv/lib/python3.12/site-packages/django/db/models/sql/query.py:1577: in _add_q
    child_clause, needed_inner = self.build_filter

...

>       arg, value = filter_expr
E       ValueError: too many values to unpack (expected 2)
  1. 访问子模型后台管理页面报错:
Cannot resolve keyword 'name' into field.
解决方案

1. 修正模型核心问题

首先修复笔误,同时让Product继承TranslatableModel——这是Parler翻译功能生效的必要条件,也是后台找不到字段的核心原因。调整后的模型代码:

from django.db import models
from parler.models import TranslatableModel, TranslatedFields
from parler.managers import TranslatableManager, TranslatableQuerySet
from polymorphic.models import PolymorphicManager, PolymorphicModel
from polymorphic.query import PolymorphicQuerySet

class ProductQuerySet(PolymorphicQuerySet, TranslatableQuerySet):
    pass

class ProductManager(PolymorphicManager, TranslatableManager):
    queryset_class = ProductQuerySet  # 修正笔误

class Product(TranslatableModel, PolymorphicModel):  # 同时继承两个基类
    translations = TranslatedFields(
        name=models.CharField(max_length=127)
    )  # 补全闭合括号
    objects = ProductManager()

class Book(Product):
    pass

class Drink(Product):
    pass

2. 修复查询集兼容性问题

Polymorphic和Parler的查询集混合时,方法调用顺序可能冲突,导致过滤报错。重载查询集的filter方法,优先让Parler处理翻译字段逻辑:

class ProductQuerySet(PolymorphicQuerySet, TranslatableQuerySet):
    def filter(self, *args, **kwargs):
        # 先处理翻译字段过滤,再交给多态逻辑处理
        return super(TranslatableQuerySet, self).filter(*args, **kwargs)

3. 适配Django后台管理

子模型的Admin类必须继承ParlerAdmin,才能正确识别翻译字段:

from django.contrib import admin
from parler.admin import TranslatableAdmin
from .models import Book, Drink

class BookAdmin(TranslatableAdmin):
    list_display = ('name',)  # 直接使用翻译字段,Parler自动处理多语言

class DrinkAdmin(TranslatableAdmin):
    list_display = ('name',)

admin.site.register(Book, BookAdmin)
admin.site.register(Drink, DrinkAdmin)

4. 数据迁移注意事项

执行迁移前,需要把原Product表的name字段数据迁移到Parler的翻译表中,示例迁移脚本:

from django.db import migrations

def migrate_name_to_translations(apps, schema_editor):
    Product = apps.get_model('product', 'Product')
    ProductTranslation = apps.get_model('product', 'ProductTranslation')
    # 把原有name值导入默认语言的翻译记录
    for product in Product.objects.all():
        ProductTranslation.objects.get_or_create(
            master=product,
            language_code='en',  # 替换为项目默认语言
            defaults={'name': product.name}
        )

class Migration(migrations.Migration):
    dependencies = [
        ('product', '0002_auto_xxxxxx_xxxx'),  # 对应添加TranslatedFields后的迁移文件
    ]

    operations = [
        migrations.RunPython(migrate_name_to_translations),
        migrations.RemoveField(
            model_name='product',
            name='name',
        ),
    ]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 06:28:12