schon/core/admin.py
Egor fureunoir Gorbunov 5926ceb8bf Features: 1) Add missing call to super().__init__() in admin field constructor;
Fixes: 1) Correct use of `remote` to `remote_opts` for consistent model metadata reference;

Extra: 1) Improve formatting for better code readability.
2025-07-01 18:16:02 +03:00

550 lines
17 KiB
Python

from contextlib import suppress
from constance.admin import Config
from constance.admin import ConstanceAdmin as BaseConstanceAdmin
from django.apps import apps
from django.contrib.admin import FieldListFilter, ModelAdmin, TabularInline, action, register, site
from django.contrib.gis.admin import GISModelAdmin
from django.db.models import Model
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from modeltranslation.translator import NotRegistered, translator
from modeltranslation.utils import get_translation_fields
from mptt.admin import DraggableMPTTAdmin
from evibes.settings import CONSTANCE_CONFIG
from .forms import OrderForm, OrderProductForm, VendorForm
from .models import (
Address,
Attribute,
AttributeGroup,
AttributeValue,
Brand,
Category,
CategoryTag,
Feedback,
Order,
OrderProduct,
Product,
ProductImage,
ProductTag,
PromoCode,
Promotion,
Stock,
Vendor,
Wishlist,
)
class RelatedAutocompleteFilter(FieldListFilter):
template = "admin/autocomplete_list_filter.html"
limit = 10
def __init__(self, field, request, params, model, model_admin, field_path):
super().__init__(field, request, params, model, model_admin, field_path)
self.lookup_kwarg = f"{field_path}__{field.target_field.name}__exact"
self.lookup_val = params.get(self.lookup_kwarg, "")
remote_opts = field.remote_field.model._meta
base_url = reverse("admin:autocomplete")
self.autocomplete_url = (
f"{base_url}"
f"?app_label={remote_opts.app_label}"
f"&model_name={remote_opts.model_name}"
f"&field_name={field.name}"
f"&limit={self.limit}"
)
def expected_parameters(self):
return [self.lookup_kwarg]
def has_output(self):
return True
def choices(self, changelist):
yield {
"selected": bool(self.lookup_val),
"query_string": changelist.get_query_string(
({self.lookup_kwarg: self.lookup_val}
if self.lookup_val else {}),
[]
),
"display": _(""),
"autocomplete_url": self.autocomplete_url,
"lookup_kwarg": self.lookup_kwarg,
"lookup_val": self.lookup_val,
}
class FieldsetsMixin:
general_fields: list = []
relation_fields: list = []
model: Model
def get_fieldsets(self, request, obj=None):
if request:
pass
if obj:
pass
fieldsets = []
def add_translations_fieldset(fss):
with suppress(NotRegistered):
transoptions = translator.get_options_for_model(self.model)
translation_fields = []
for orig in transoptions.local_fields:
translation_fields += get_translation_fields(orig)
if translation_fields:
fss = list(fss) + [(_("translations"), {"fields": translation_fields})]
return fss
if self.general_fields:
fieldsets.append((_("general"), {"fields": self.general_fields}))
if self.relation_fields:
fieldsets.append((_("relations"), {"fields": self.relation_fields}))
opts = self.model._meta
if any(f.name == "uuid" for f in opts.fields):
if any(f.name == "slug" for f in opts.fields):
fieldsets.append((_("metadata"), {"fields": ["uuid", "slug"]}))
else:
fieldsets.append((_("metadata"), {"fields": ["uuid"]}))
ts = []
for name in ("created", "modified"):
if any(f.name == name for f in opts.fields):
ts.append(name)
if ts:
fieldsets.append((_("timestamps"), {"fields": ts, "classes": ["collapse"]}))
fieldsets = add_translations_fieldset(fieldsets)
return fieldsets
class ActivationActionsMixin:
@action(description=str(_("activate selected %(verbose_name_plural)s")))
def activate_selected(self, request, queryset) -> str:
if request:
pass
queryset.update(is_active=True)
return str(_("%(verbose_name_plural)s activated successfully!"))
@action(description=str(_("deactivate selected %(verbose_name_plural)s")))
def deactivate_selected(self, request, queryset) -> str:
if request:
pass
queryset.update(is_active=False)
return str(_("%(verbose_name_plural)s deactivated successfully."))
def get_actions(self, request):
actions = super().get_actions(request)
actions["activate_selected"] = (
self.activate_selected,
"activate_selected",
str(_("activate selected %(verbose_name_plural)s")),
)
actions["deactivate_selected"] = (
self.deactivate_selected,
"deactivate_selected",
str(_("deactivate selected %(verbose_name_plural)s")),
)
return actions
class AttributeValueInline(TabularInline):
model = AttributeValue
extra = 0
autocomplete_fields = ["attribute"]
is_navtab = True
verbose_name = _("attribute value")
verbose_name_plural = _("attribute values")
icon = "fa-solid fa-list-ul"
class ProductImageInline(TabularInline):
model = ProductImage
extra = 0
is_navtab = True
verbose_name = _("image")
verbose_name_plural = _("images")
icon = "fa-regular fa-images"
class StockInline(TabularInline):
model = Stock
extra = 0
is_navtab = True
verbose_name = _("stock")
verbose_name_plural = _("stocks")
icon = "fa-solid fa-boxes-stacked"
class OrderProductInline(TabularInline):
model = OrderProduct
extra = 0
readonly_fields = ("product", "quantity", "buy_price")
form = OrderProductForm
is_navtab = True
verbose_name = _("order product")
verbose_name_plural = _("order products")
icon = "fa-solid fa-boxes-packing"
def get_queryset(self, request):
return super().get_queryset(request).select_related("product").only("product__name")
class CategoryChildrenInline(TabularInline):
model = Category
fk_name = "parent"
extra = 0
fields = ("name", "description", "is_active", "image", "markup_percent")
is_navtab = True
verbose_name = _("children")
verbose_name_plural = _("children")
icon = "fa-solid fa-leaf"
@register(AttributeGroup)
class AttributeGroupAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = AttributeGroup # type: ignore
list_display = ("name", "modified")
search_fields = ("uuid", "name")
readonly_fields = ("uuid", "modified", "created")
general_fields = ["is_active", "name", "parent"]
relation_fields = []
@register(Attribute)
class AttributeAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Attribute # type: ignore
list_display = ("name", "group", "value_type", "modified")
list_filter = ("value_type", "group", "is_active")
search_fields = ("uuid", "name", "group__name")
readonly_fields = ("uuid", "modified", "created")
autocomplete_fields = ["categories", "group"]
general_fields = ["is_active", "name", "value_type"]
relation_fields = ["group", "categories"]
@register(AttributeValue)
class AttributeValueAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = AttributeValue # type: ignore
list_display = ("attribute", "value", "modified")
list_filter = ("attribute__group", "is_active")
search_fields = ("uuid", "value", "attribute__name")
readonly_fields = ("uuid", "modified", "created")
autocomplete_fields = ["attribute"]
general_fields = ["is_active", "value"]
relation_fields = ["attribute", "product"]
@register(Category)
class CategoryAdmin(FieldsetsMixin, ActivationActionsMixin, DraggableMPTTAdmin):
model = Category # type: ignore
list_display = ("indented_title", "parent", "is_active", "modified")
# noinspection PyUnresolvedReferences
list_filter = ("is_active", "level", "created", "modified")
search_fields = ("uuid", "name")
inlines = [CategoryChildrenInline]
autocomplete_fields = ["parent", "tags"]
readonly_fields = ("slug", "uuid", "modified", "created")
general_fields = ["is_active", "name", "description", "image", "markup_percent", "priority"]
relation_fields = ["parent", "tags"]
@register(Brand)
class BrandAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Brand # type: ignore
list_display = ("name",)
list_filter = ("categories", "is_active")
search_fields = ("uuid", "name", "categories__name")
readonly_fields = ("uuid", "slug", "modified", "created")
general_fields = ["is_active", "name", "description", "priority"]
relation_fields = ["small_logo", "big_logo", "categories"]
@register(Product)
class ProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Product # type: ignore
list_display = (
"name",
"partnumber",
"is_active",
"category",
"brand",
"price",
"rating",
"modified",
)
list_filter = (
"is_active",
"is_digital",
"stocks__vendor",
"created",
"modified",
("brand", RelatedAutocompleteFilter),
("category", RelatedAutocompleteFilter),
)
search_fields = (
"name",
"partnumber",
"brand__name",
"category__name",
"uuid",
"slug",
)
readonly_fields = ("slug", "uuid", "modified", "created")
autocomplete_fields = ("category", "brand", "tags")
inlines = [AttributeValueInline, ProductImageInline, StockInline]
general_fields = ["is_active", "name", "partnumber", "is_digital"]
relation_fields = ["category", "brand", "tags"]
@register(ProductTag)
class ProductTagAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = ProductTag # type: ignore
list_display = ("tag_name",)
search_fields = ("tag_name",)
readonly_fields = ("uuid", "modified", "created")
general_fields = ["is_active", "tag_name", "name"]
relation_fields = []
@register(CategoryTag)
class CategoryTagAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = CategoryTag # type: ignore
list_display = ("tag_name",)
search_fields = ("tag_name",)
readonly_fields = ("uuid", "modified", "created")
general_fields = ["is_active", "tag_name", "name"]
relation_fields = []
@register(Vendor)
class VendorAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Vendor # type: ignore
list_display = ("name", "markup_percent", "modified")
list_filter = ("markup_percent", "is_active")
search_fields = ("name",)
readonly_fields = ("uuid", "modified", "created")
form = VendorForm
general_fields = ["is_active", "name", "markup_percent", "authentication"]
relation_fields = []
@register(Feedback)
class FeedbackAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Feedback # type: ignore
list_display = ("order_product", "rating", "comment", "modified")
list_filter = ("rating", "is_active")
search_fields = ("order_product__product__name", "comment")
readonly_fields = ("uuid", "modified", "created")
general_fields = ["is_active", "rating", "comment"]
relation_fields = ["order_product"]
@register(Order)
class OrderAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Order # type: ignore
list_display = (
"human_readable_id",
"user",
"status",
"total_price",
"buy_time",
"modified",
)
list_filter = ("status", "buy_time", "modified", "created")
search_fields = ("user__email", "status", "uuid", "human_readable_id")
readonly_fields = (
"total_price",
"total_quantity",
"human_readable_id",
"uuid",
"modified",
"created",
)
inlines = [OrderProductInline]
form = OrderForm
general_fields = ["is_active", "user", "status"]
relation_fields = ["promo_code", "billing_address", "shipping_address"]
@register(OrderProduct)
class OrderProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = OrderProduct # type: ignore
list_display = ("order", "product", "quantity", "buy_price", "status", "modified")
list_filter = ("status",)
search_fields = ("order__user__email", "product__name")
readonly_fields = ("uuid", "modified", "created")
form = OrderProductForm
general_fields = ["is_active", "quantity", "buy_price", "status"]
relation_fields = ["order", "product"]
@register(PromoCode)
class PromoCodeAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = PromoCode # type: ignore
list_display = (
"code",
"discount_percent",
"discount_amount",
"start_time",
"end_time",
"used_on",
)
list_filter = ("discount_percent", "discount_amount", "start_time", "end_time")
search_fields = ("code",)
readonly_fields = ("used_on", "uuid", "modified", "created")
autocomplete_fields = ("user",)
general_fields = [
"is_active",
"code",
"discount_amount",
"discount_percent",
"start_time",
"end_time",
"used_on",
]
relation_fields = ["user"]
@register(Promotion)
class PromotionAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Promotion # type: ignore
list_display = ("name", "discount_percent", "modified")
search_fields = ("name",)
readonly_fields = ("uuid", "modified", "created")
autocomplete_fields = ("products",)
general_fields = ["is_active", "name", "discount_percent", "description"]
relation_fields = ["products"]
@register(Stock)
class StockAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Stock # type: ignore
list_display = ("product", "vendor", "sku", "quantity", "price", "modified")
list_filter = ("vendor", "quantity")
search_fields = ("product__name", "vendor__name", "sku")
readonly_fields = ("uuid", "modified", "created")
autocomplete_fields = ("product", "vendor")
general_fields = [
"is_active",
"sku",
"quantity",
"price",
"purchase_price",
"digital_asset",
]
relation_fields = ["product", "vendor"]
@register(Wishlist)
class WishlistAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Wishlist # type: ignore
list_display = ("user", "modified")
search_fields = ("user__email",)
readonly_fields = ("uuid", "modified", "created")
general_fields = ["is_active", "user"]
relation_fields = ["products"]
@register(ProductImage)
class ProductImageAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = ProductImage # type: ignore
list_display = ("alt", "product", "priority", "modified")
list_filter = ("priority",)
search_fields = ("alt", "product__name")
readonly_fields = ("uuid", "modified", "created")
autocomplete_fields = ("product",)
general_fields = ["is_active", "alt", "priority", "image"]
relation_fields = ["product"]
@register(Address)
class AddressAdmin(FieldsetsMixin, GISModelAdmin):
model = Address # type: ignore
list_display = ("street", "city", "region", "country", "user")
list_filter = ("country", "region")
search_fields = ("street", "city", "postal_code", "user__email")
readonly_fields = ("uuid", "modified", "created")
gis_widget_kwargs = {
"attrs": {
"default_lon": 37.61556,
"default_lat": 55.75222,
"default_zoom": 6,
}
}
general_fields = [
"is_active",
"address_line",
"street",
"district",
"city",
"region",
"postal_code",
"country",
"raw_data",
]
relation_fields = ["user", "api_response"]
# Constance configuration
class ConstanceConfig:
class Meta:
app_label = "core"
object_name = "Config"
concrete_model = None
model_name = module_name = "config"
verbose_name_plural = _("Config")
abstract = False
swapped = False
is_composite_pk = False
def get_change_permission(self):
return f"change_{self.model_name}"
@property
def app_config(self):
return apps.get_app_config(self.app_label)
@property
def label(self):
return f"{self.app_label}.{self.object_name}"
@property
def label_lower(self):
return f"{self.app_label}.{self.model_name}"
def get_ordered_objects(self):
return False
_meta = Meta()
site.unregister([Config]) # type: ignore
site.register([ConstanceConfig], BaseConstanceAdmin) # type: ignore
site.site_title = CONSTANCE_CONFIG["PROJECT_NAME"][0] # type: ignore
site.site_header = "eVibes"
site.index_title = CONSTANCE_CONFIG["PROJECT_NAME"][0] # type: ignore