Features: 1) Add I18NFieldsetMixin to organize translation fields into a dedicated fieldset; 2) Implement I18NTabTranslationAdmin for enhanced multi-language admin support; 3) Apply new translation admin to AttributeGroupAdmin, AttributeAdmin, AttributeValueAdmin, CategoryAdmin, BrandAdmin, ProductAdmin, ProductTagAdmin, CategoryTagAdmin, and PromotionAdmin.
Fixes: 1) Replace `TranslationGenericTabularInline` with `I18NTabTranslationAdmin` to avoid potential translation field duplication. Extra: 1) Update fieldsets in various model admins for improved structure and translation field grouping; 2) Add constants for fieldset section names to enhance readability and consistency.
This commit is contained in:
parent
02102fa470
commit
48ef110ac4
1 changed files with 155 additions and 32 deletions
187
core/admin.py
187
core/admin.py
|
|
@ -5,9 +5,14 @@ from django.apps import apps
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.admin import ModelAdmin, TabularInline
|
from django.contrib.admin import ModelAdmin, TabularInline
|
||||||
from django.contrib.gis.admin import GISModelAdmin
|
from django.contrib.gis.admin import GISModelAdmin
|
||||||
|
from django.db.models import Model
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from modeltranslation.admin import TranslationGenericTabularInline
|
from modeltranslation.admin import (
|
||||||
|
TabbedExternalJqueryTranslationAdmin,
|
||||||
|
)
|
||||||
|
from modeltranslation.translator import translator
|
||||||
|
from modeltranslation.utils import get_translation_fields
|
||||||
from mptt.admin import DraggableMPTTAdmin
|
from mptt.admin import DraggableMPTTAdmin
|
||||||
|
|
||||||
from evibes.settings import CONSTANCE_CONFIG
|
from evibes.settings import CONSTANCE_CONFIG
|
||||||
|
|
@ -34,6 +39,51 @@ from .models import (
|
||||||
Wishlist,
|
Wishlist,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SECTION_GENERAL = _("general")
|
||||||
|
SECTION_I18N = _("I18N")
|
||||||
|
SECTION_META = _("metadata")
|
||||||
|
SECTION_DATES = _("timestamps")
|
||||||
|
SECTION_RELATIONS = _("relations")
|
||||||
|
|
||||||
|
|
||||||
|
class I18NFieldsetMixin:
|
||||||
|
model: type[Model]
|
||||||
|
"""
|
||||||
|
Pulls all <field>_<lang> translation columns out of the regular
|
||||||
|
fieldsets and tucks them into one extra fieldset/tab called "I18N".
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_fieldsets(self, request, obj=None):
|
||||||
|
base_fieldsets = super().get_fieldsets(request, obj)
|
||||||
|
|
||||||
|
trans_opts = translator.get_options_for_model(self.model)
|
||||||
|
translation_fields = []
|
||||||
|
for orig in trans_opts.fields.keys(): # noqa: SIM118
|
||||||
|
translation_fields.extend(get_translation_fields(orig))
|
||||||
|
|
||||||
|
cleaned = []
|
||||||
|
for title, opts in base_fieldsets:
|
||||||
|
original = list(opts.get("fields", []))
|
||||||
|
filtered = [f for f in original if f not in translation_fields]
|
||||||
|
opts = opts.copy()
|
||||||
|
opts["fields"] = filtered
|
||||||
|
cleaned.append((title, opts))
|
||||||
|
|
||||||
|
cleaned.append(
|
||||||
|
(
|
||||||
|
_("I18N"),
|
||||||
|
{
|
||||||
|
"classes": ("suit-tab-i18n",),
|
||||||
|
"fields": tuple(translation_fields),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return cleaned
|
||||||
|
|
||||||
|
|
||||||
|
class I18NTabTranslationAdmin(I18NFieldsetMixin, TabbedExternalJqueryTranslationAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BasicModelAdmin(ModelAdmin):
|
class BasicModelAdmin(ModelAdmin):
|
||||||
@admin.action(description=str(_("activate selected %(verbose_name_plural)s")))
|
@admin.action(description=str(_("activate selected %(verbose_name_plural)s")))
|
||||||
|
|
@ -71,16 +121,41 @@ class AttributeValueInline(TabularInline):
|
||||||
|
|
||||||
|
|
||||||
@admin.register(AttributeGroup)
|
@admin.register(AttributeGroup)
|
||||||
class AttributeGroupAdmin(BasicModelAdmin, TranslationGenericTabularInline):
|
class AttributeGroupAdmin(BasicModelAdmin, I18NTabTranslationAdmin):
|
||||||
list_display = ("name", "modified")
|
list_display = ("name", "modified")
|
||||||
search_fields = (
|
search_fields = (
|
||||||
"uuid",
|
"uuid",
|
||||||
"name",
|
"name",
|
||||||
)
|
)
|
||||||
|
fieldsets = (
|
||||||
|
(
|
||||||
|
SECTION_GENERAL,
|
||||||
|
{
|
||||||
|
"fields": ("name", "parent", "is_active"),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SECTION_META,
|
||||||
|
{
|
||||||
|
"fields": ("uuid",),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SECTION_I18N,
|
||||||
|
{
|
||||||
|
"classes": ("suit-tab-i18n",),
|
||||||
|
"fields": tuple( # pulled in by I18NFieldsetMixin
|
||||||
|
translator.get_options_for_model(
|
||||||
|
AttributeGroup
|
||||||
|
).get_all_translation_fields()
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Attribute)
|
@admin.register(Attribute)
|
||||||
class AttributeAdmin(BasicModelAdmin, TranslationGenericTabularInline):
|
class AttributeAdmin(BasicModelAdmin, I18NTabTranslationAdmin):
|
||||||
list_display = ("name", "group", "value_type", "modified")
|
list_display = ("name", "group", "value_type", "modified")
|
||||||
list_filter = ("value_type", "group", "is_active")
|
list_filter = ("value_type", "group", "is_active")
|
||||||
search_fields = ("uuid", "name", "group__name")
|
search_fields = ("uuid", "name", "group__name")
|
||||||
|
|
@ -88,7 +163,7 @@ class AttributeAdmin(BasicModelAdmin, TranslationGenericTabularInline):
|
||||||
|
|
||||||
|
|
||||||
@admin.register(AttributeValue)
|
@admin.register(AttributeValue)
|
||||||
class AttributeValueAdmin(BasicModelAdmin, TranslationGenericTabularInline):
|
class AttributeValueAdmin(BasicModelAdmin, I18NTabTranslationAdmin):
|
||||||
list_display = ("attribute", "value", "modified")
|
list_display = ("attribute", "value", "modified")
|
||||||
list_filter = ("attribute__group", "is_active")
|
list_filter = ("attribute__group", "is_active")
|
||||||
search_fields = ("uuid", "value", "attribute__name")
|
search_fields = ("uuid", "value", "attribute__name")
|
||||||
|
|
@ -104,7 +179,7 @@ class CategoryChildrenInline(admin.TabularInline):
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Category)
|
@admin.register(Category)
|
||||||
class CategoryAdmin(DraggableMPTTAdmin, BasicModelAdmin, TranslationGenericTabularInline):
|
class CategoryAdmin(DraggableMPTTAdmin, BasicModelAdmin, I18NTabTranslationAdmin):
|
||||||
mptt_indent_field = "name"
|
mptt_indent_field = "name"
|
||||||
list_display = ("indented_title", "parent", "is_active", "modified")
|
list_display = ("indented_title", "parent", "is_active", "modified")
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
|
|
@ -115,25 +190,48 @@ class CategoryAdmin(DraggableMPTTAdmin, BasicModelAdmin, TranslationGenericTabul
|
||||||
"name",
|
"name",
|
||||||
)
|
)
|
||||||
inlines = [CategoryChildrenInline]
|
inlines = [CategoryChildrenInline]
|
||||||
fieldsets = [
|
fieldsets = (
|
||||||
(
|
(
|
||||||
None,
|
SECTION_GENERAL,
|
||||||
{
|
{
|
||||||
"fields": (
|
"fields": (
|
||||||
"uuid",
|
|
||||||
"slug",
|
|
||||||
"name",
|
"name",
|
||||||
"description",
|
"slug",
|
||||||
"parent",
|
"parent",
|
||||||
"is_active",
|
"is_active",
|
||||||
"priority",
|
),
|
||||||
"image",
|
|
||||||
"markup_percent",
|
|
||||||
"tags",
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
)
|
),
|
||||||
]
|
(
|
||||||
|
SECTION_RELATIONS,
|
||||||
|
{
|
||||||
|
"fields": ("tags",),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SECTION_META,
|
||||||
|
{
|
||||||
|
"fields": ("uuid",),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SECTION_DATES,
|
||||||
|
{
|
||||||
|
"fields": ("created", "modified"),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SECTION_I18N,
|
||||||
|
{
|
||||||
|
"classes": ("suit-tab-i18n",),
|
||||||
|
"fields": tuple(
|
||||||
|
translator.get_options_for_model(
|
||||||
|
Category
|
||||||
|
).get_all_translation_fields()
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
autocomplete_fields = ["parent", "tags"]
|
autocomplete_fields = ["parent", "tags"]
|
||||||
readonly_fields = (
|
readonly_fields = (
|
||||||
"uuid",
|
"uuid",
|
||||||
|
|
@ -148,7 +246,7 @@ class CategoryAdmin(DraggableMPTTAdmin, BasicModelAdmin, TranslationGenericTabul
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Brand)
|
@admin.register(Brand)
|
||||||
class BrandAdmin(BasicModelAdmin, TranslationGenericTabularInline):
|
class BrandAdmin(BasicModelAdmin, I18NTabTranslationAdmin):
|
||||||
list_display = ("name",)
|
list_display = ("name",)
|
||||||
list_filter = ("categories", "is_active")
|
list_filter = ("categories", "is_active")
|
||||||
search_fields = (
|
search_fields = (
|
||||||
|
|
@ -179,7 +277,7 @@ class StockInline(TabularInline):
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Product)
|
@admin.register(Product)
|
||||||
class ProductAdmin(BasicModelAdmin, TranslationGenericTabularInline):
|
class ProductAdmin(BasicModelAdmin, I18NTabTranslationAdmin):
|
||||||
list_display = (
|
list_display = (
|
||||||
"name",
|
"name",
|
||||||
"partnumber",
|
"partnumber",
|
||||||
|
|
@ -224,23 +322,46 @@ class ProductAdmin(BasicModelAdmin, TranslationGenericTabularInline):
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
_("basic info"),
|
SECTION_GENERAL,
|
||||||
{
|
{
|
||||||
"fields": (
|
"fields": (
|
||||||
"uuid",
|
"name",
|
||||||
"slug",
|
"slug",
|
||||||
"partnumber",
|
"partnumber",
|
||||||
"is_active",
|
"is_active",
|
||||||
"is_digital",
|
"is_digital",
|
||||||
"name",
|
),
|
||||||
"category",
|
},
|
||||||
"brand",
|
),
|
||||||
"description",
|
(
|
||||||
"tags",
|
SECTION_RELATIONS,
|
||||||
)
|
{
|
||||||
|
"fields": ("category", "brand", "tags"),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SECTION_META,
|
||||||
|
{
|
||||||
|
"fields": ("uuid",),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SECTION_DATES,
|
||||||
|
{
|
||||||
|
"fields": ("created", "modified"),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SECTION_I18N,
|
||||||
|
{
|
||||||
|
"classes": ("suit-tab-i18n",),
|
||||||
|
"fields": tuple(
|
||||||
|
translator.get_options_for_model(
|
||||||
|
Product
|
||||||
|
).get_all_translation_fields()
|
||||||
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(_("important dates"), {"fields": ("created", "modified")}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
inlines = [AttributeValueInline, ProductImageInline, StockInline]
|
inlines = [AttributeValueInline, ProductImageInline, StockInline]
|
||||||
|
|
@ -252,13 +373,13 @@ class ProductAdmin(BasicModelAdmin, TranslationGenericTabularInline):
|
||||||
|
|
||||||
|
|
||||||
@admin.register(ProductTag)
|
@admin.register(ProductTag)
|
||||||
class ProductTagAdmin(BasicModelAdmin, TranslationGenericTabularInline):
|
class ProductTagAdmin(BasicModelAdmin, I18NTabTranslationAdmin):
|
||||||
list_display = ("name",)
|
list_display = ("name",)
|
||||||
search_fields = ("name",)
|
search_fields = ("name",)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(CategoryTag)
|
@admin.register(CategoryTag)
|
||||||
class CategoryTagAdmin(BasicModelAdmin, TranslationGenericTabularInline):
|
class CategoryTagAdmin(BasicModelAdmin, I18NTabTranslationAdmin):
|
||||||
list_display = ("name",)
|
list_display = ("name",)
|
||||||
search_fields = ("name",)
|
search_fields = ("name",)
|
||||||
|
|
||||||
|
|
@ -370,7 +491,7 @@ class PromoCodeAdmin(BasicModelAdmin):
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Promotion)
|
@admin.register(Promotion)
|
||||||
class PromotionAdmin(BasicModelAdmin, TranslationGenericTabularInline):
|
class PromotionAdmin(BasicModelAdmin, I18NTabTranslationAdmin):
|
||||||
list_display = ("name", "discount_percent", "modified")
|
list_display = ("name", "discount_percent", "modified")
|
||||||
search_fields = ("name",)
|
search_fields = ("name",)
|
||||||
autocomplete_fields = ("products",)
|
autocomplete_fields = ("products",)
|
||||||
|
|
@ -433,7 +554,9 @@ class ConstanceAdmin(BaseConstanceAdmin):
|
||||||
self.admin_site.admin_view(self.changelist_view),
|
self.admin_site.admin_view(self.changelist_view),
|
||||||
name=f"{info}_changelist",
|
name=f"{info}_changelist",
|
||||||
),
|
),
|
||||||
path("", self.admin_site.admin_view(self.changelist_view), name=f"{info}_add"),
|
path(
|
||||||
|
"", self.admin_site.admin_view(self.changelist_view), name=f"{info}_add"
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue