schon/core/admin.py
Egor fureunoir Gorbunov 77d978fecb Features: 1) Integrated django-summernote for rich text editing; 2) Added Summernote fields in PostAdmin with configuration; 3) Configured summernote settings in a separated module;
Fixes: 1) Adjusted modeltranslation admin import to use external jQuery variant; 2) Corrected optional flags for package dependencies in poetry.lock;

Extra: 1) Removed custom PostAdminForm in favor of SummernoteModelAdmin; 2) Enhanced formatting consistency in api_urls and admin files.
2025-06-19 13:15:04 +03:00

470 lines
13 KiB
Python

# noinspection PyUnresolvedReferences
from constance.admin import Config
from constance.admin import ConstanceAdmin as BaseConstanceAdmin
from django.apps import apps
from django.contrib import admin
from django.contrib.admin import ModelAdmin, TabularInline
from django.contrib.gis.admin import GISModelAdmin
from django.urls import path
from django.utils.translation import gettext_lazy as _
from modeltranslation.admin import (
TabbedExternalJqueryTranslationAdmin as TabbedTranslationAdmin,
)
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 BasicModelAdmin(ModelAdmin):
@admin.action(description=str(_("activate selected %(verbose_name_plural)s")))
def activate_selected(self, _request, queryset) -> str:
queryset.update(is_active=True)
return ""
@admin.action(description=str(_("deactivate selected %(verbose_name_plural)s")))
def deactivate_selected(self, _request, queryset) -> str:
queryset.update(is_active=False)
return ""
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
is_navtab = True
verbose_name = _("attribute value")
verbose_name_plural = _("attribute values")
autocomplete_fields = ["attribute"]
@admin.register(AttributeGroup)
class AttributeGroupAdmin(BasicModelAdmin, TabbedTranslationAdmin):
list_display = ("name", "modified")
search_fields = (
"uuid",
"name",
)
@admin.register(Attribute)
class AttributeAdmin(BasicModelAdmin, TabbedTranslationAdmin):
list_display = ("name", "group", "value_type", "modified")
list_filter = ("value_type", "group", "is_active")
search_fields = ("uuid", "name", "group__name")
autocomplete_fields = ["categories", "group"]
@admin.register(AttributeValue)
class AttributeValueAdmin(BasicModelAdmin, TabbedTranslationAdmin):
list_display = ("attribute", "value", "modified")
list_filter = ("attribute__group", "is_active")
search_fields = ("uuid", "value", "attribute__name")
autocomplete_fields = ["attribute"]
class CategoryChildrenInline(admin.TabularInline):
model = Category
fk_name = "parent"
extra = 0
fields = ("name", "description", "is_active", "image", "markup_percent")
@admin.register(Category)
class CategoryAdmin(DraggableMPTTAdmin, BasicModelAdmin, TabbedTranslationAdmin):
mptt_indent_field = "name"
list_display = ("indented_title", "parent", "is_active", "modified")
# noinspection PyUnresolvedReferences
list_filter = ("is_active", "level", "created", "modified")
list_display_links = ("indented_title",)
search_fields = (
"uuid",
"name",
)
inlines = [CategoryChildrenInline]
fieldsets = [
(
None,
{
"fields": (
"name",
"description",
"parent",
"is_active",
"image",
"markup_percent",
"tags",
)
},
)
]
autocomplete_fields = ["parent", "tags"]
def indented_title(self, instance):
return instance.name
indented_title.short_description = _("name") # type: ignore
indented_title.admin_order_field = "name" # type: ignore
@admin.register(Brand)
class BrandAdmin(BasicModelAdmin, TabbedTranslationAdmin):
list_display = ("name",)
list_filter = ("categories", "is_active")
search_fields = (
"uuid",
"name",
"categories__name",
)
class ProductImageInline(TabularInline):
model = ProductImage
extra = 0
is_navtab = True
verbose_name = _("image")
verbose_name_plural = _("images")
class StockInline(TabularInline):
model = Stock
extra = 0
is_navtab = True
verbose_name = _("stock")
verbose_name_plural = _("stocks")
@admin.register(Product)
class ProductAdmin(BasicModelAdmin, TabbedTranslationAdmin):
list_display = (
"name",
"partnumber",
"is_active",
"category",
"brand",
"price",
"rating",
"modified",
)
list_filter = (
"is_active",
"is_digital",
("tags", admin.RelatedOnlyFieldListFilter),
("stocks__vendor", admin.RelatedOnlyFieldListFilter),
"created",
"modified",
)
search_fields = (
"name",
"partnumber",
"brand__name",
"category__name",
"uuid",
"slug",
)
readonly_fields = ("created", "modified", "uuid", "rating", "price", "slug")
autocomplete_fields = ("category", "brand", "tags")
def price(self, obj):
return obj.price
price.short_description = _("price") # type: ignore
def rating(self, obj):
return obj.rating
rating.short_description = _("rating") # type: ignore
fieldsets = (
(
_("basic info"),
{
"fields": (
"uuid",
"slug",
"partnumber",
"is_active",
"is_digital",
"name",
"category",
"brand",
"description",
"tags",
)
},
),
(_("important dates"), {"fields": ("created", "modified")}),
)
inlines = [AttributeValueInline, ProductImageInline, StockInline]
def get_changelist(self, request, **kwargs):
changelist = super().get_changelist(request, **kwargs)
changelist.filter_input_length = 64
return changelist
@admin.register(ProductTag)
class ProductTagAdmin(BasicModelAdmin, TabbedTranslationAdmin):
list_display = ("name",)
search_fields = ("name",)
@admin.register(CategoryTag)
class CategoryTagAdmin(BasicModelAdmin, TabbedTranslationAdmin):
list_display = ("name",)
search_fields = ("name",)
@admin.register(Vendor)
class VendorAdmin(BasicModelAdmin):
list_display = ("name", "markup_percent", "modified")
list_filter = ("markup_percent", "is_active")
search_fields = ("name",)
form = VendorForm
@admin.register(Feedback)
class FeedbackAdmin(BasicModelAdmin):
list_display = ("order_product", "rating", "comment", "modified")
list_filter = ("rating", "is_active")
search_fields = ("order_product__product__name", "comment")
class OrderProductInline(admin.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")
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.select_related("product").only("product__name")
@admin.register(Order)
class OrderAdmin(BasicModelAdmin):
list_display = (
"human_readable_id",
"user",
"is_business",
"status",
"total_price",
"buy_time",
"modified",
)
list_filter = ("status", "buy_time", "modified", "created")
search_fields = ("user__email", "status", "uuid", "human_readable_id")
inlines = [OrderProductInline]
form = OrderForm
readonly_fields = ("total_price", "total_quantity", "buy_time", "human_readable_id")
def is_business(self, obj):
return obj.is_business
is_business.short_description = _("is business") # type: ignore
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.prefetch_related(
"user",
"shipping_address",
"billing_address",
"order_products",
"promo_code",
)
def save_model(self, request, obj, form, change):
if form.cleaned_data.get("attributes") is None:
obj.attributes = None
if form.cleaned_data.get("notifications") is None:
obj.attributes = None
super().save_model(request, obj, form, change)
@admin.register(OrderProduct)
class OrderProductAdmin(BasicModelAdmin):
list_display = ("order", "product", "quantity", "buy_price", "status", "modified")
list_filter = ("status",)
search_fields = ("order__user__email", "product__name")
form = OrderProductForm
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.prefetch_related("order", "product")
def save_model(self, request, obj, form, change):
if form.cleaned_data.get("attributes") is None:
obj.attributes = None
if form.cleaned_data.get("notifications") is None:
obj.attributes = None
super().save_model(request, obj, form, change)
@admin.register(PromoCode)
class PromoCodeAdmin(BasicModelAdmin):
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",)
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.prefetch_related("user")
@admin.register(Promotion)
class PromotionAdmin(BasicModelAdmin, TabbedTranslationAdmin):
list_display = ("name", "discount_percent", "modified")
search_fields = ("name",)
autocomplete_fields = ("products",)
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.prefetch_related("products")
@admin.register(Stock)
class StockAdmin(BasicModelAdmin):
list_display = ("product", "vendor", "sku", "quantity", "price", "modified")
list_filter = ("vendor", "quantity")
search_fields = ("product__name", "vendor__name", "sku")
autocomplete_fields = ("product", "vendor")
@admin.register(Wishlist)
class WishlistAdmin(BasicModelAdmin):
list_display = ("user", "modified")
search_fields = ("user__email",)
@admin.register(ProductImage)
class ProductImageAdmin(BasicModelAdmin):
list_display = ("alt", "product", "priority", "modified")
list_filter = ("priority",)
search_fields = ("alt", "product__name")
autocomplete_fields = ("product",)
@admin.register(Address)
class AddressAdmin(GISModelAdmin):
list_display = ("street", "city", "region", "country", "user")
list_filter = ("country", "region")
search_fields = (
"raw_data",
"street",
"city",
"postal_code",
"user__email",
"address_line",
)
gis_widget_kwargs = {
"attrs": {
"default_lon": 37.61556,
"default_lat": 55.75222,
"default_zoom": 6,
}
}
class ConstanceAdmin(BaseConstanceAdmin):
def get_urls(self):
info = f"{self.model._meta.app_label}_{self.model._meta.model_name}"
return [
path(
"",
self.admin_site.admin_view(self.changelist_view),
name=f"{info}_changelist",
),
path(
"", self.admin_site.admin_view(self.changelist_view), name=f"{info}_add"
),
]
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_ordered_objects(self):
return 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}"
_meta = Meta()
admin.site.unregister([Config]) # type: ignore
admin.site.register([ConstanceConfig], ConstanceAdmin) # type: ignore
admin.site.site_title = f"{CONSTANCE_CONFIG.get('PROJECT_NAME')[0]}" # type: ignore
admin.site.site_header = "eVibes"
admin.site.index_title = f"{CONSTANCE_CONFIG.get('PROJECT_NAME')[0]}" # type: ignore