• bitcoinBitcoin(BTC)$60,821.00-4.37%
  • ethereumEthereum(ETH)$1,572.88-10.86%
  • tetherTether(USDT)$1.000.07%
  • binancecoinBNB(BNB)$569.56-5.45%
  • usd-coinUSDC(USDC)$1.00-0.01%
  • rippleXRP(XRP)$1.09-6.03%
  • solanaSolana(SOL)$63.25-7.63%
  • tronTRON(TRX)$0.320029-3.60%
  • Figure HelocFigure Heloc(FIGR_HELOC)$1.030.96%
  • HyperliquidHyperliquid(HYPE)$59.22-7.40%
  • dogecoinDogecoin(DOGE)$0.081026-8.08%
  • USDSUSDS(USDS)$1.000.03%
  • leo-tokenLEO Token(LEO)$9.59-3.46%
  • RainRain(RAIN)$0.012962-8.10%
  • stellarStellar(XLM)$0.200370-0.03%
  • zcashZcash(ZEC)$380.58-15.19%
  • cardanoCardano(ADA)$0.156024-13.57%
  • moneroMonero(XMR)$306.82-17.18%
  • CantonCanton(CC)$0.148607-0.48%
  • chainlinkChainlink(LINK)$7.33-7.77%
  • whitebitWhiteBIT Coin(WBT)$43.50-5.03%
  • USD1USD1(USD1)$1.000.02%
  • Ethena USDeEthena USDe(USDE)$1.000.08%
  • daiDai(DAI)$1.000.01%
  • bitcoin-cashBitcoin Cash(BCH)$209.24-14.31%
  • the-open-networkToncoin(TON)$1.50-9.30%
  • MemeCoreMemeCore(M)$2.87-12.98%
  • hedera-hashgraphHedera(HBAR)$0.079884-4.21%
  • litecoinLitecoin(LTC)$42.95-5.71%
  • LABLAB(LAB)$9.68-19.92%
  • avalanche-2Avalanche(AVAX)$6.68-12.73%
  • paypal-usdPayPal USD(PYUSD)$1.000.03%
  • Circle USYCCircle USYC(USYC)$1.130.00%
  • suiSui(SUI)$0.69-8.85%
  • shiba-inuShiba Inu(SHIB)$0.000005-8.24%
  • tether-goldTether Gold(XAUT)$4,300.82-3.08%
  • crypto-com-chainCronos(CRO)$0.057260-5.58%
  • Global DollarGlobal Dollar(USDG)$1.000.01%
  • nearNEAR Protocol(NEAR)$1.95-11.12%
  • BlackRock USD Institutional Digital Liquidity FundBlackRock USD Institutional Digital Liquidity Fund(BUIDL)$1.000.00%
  • Ondo US Dollar YieldOndo US Dollar Yield(USDY)$1.13-1.14%
  • pax-goldPAX Gold(PAXG)$4,319.20-3.07%
  • BittensorBittensor(TAO)$193.59-7.46%
  • World Liberty FinancialWorld Liberty Financial(WLFI)$0.056212-5.45%
  • worldcoin-wldWorldcoin(WLD)$0.53-2.69%
  • mantleMantle(MNT)$0.51-7.32%
  • Ripple USDRipple USD(RLUSD)$1.00-0.02%
  • OndoOndo(ONDO)$0.340570-8.32%
  • polkadotPolkadot(DOT)$0.94-8.94%
  • AsterAster(ASTER)$0.61-7.13%
TradePoint.io
  • Main
  • AI & Technology
  • Stock Charts
  • Market & News
  • Business
  • Finance Tips
  • Trade Tube
  • Blog
  • Shop
No Result
View All Result
TradePoint.io
No Result
View All Result

How to Build a Django-Unfold Admin Dashboard with Custom Models, Filters, Actions, and KPIs

May 15, 2026
in AI & Technology
Reading Time: 4 mins read
A A
How to Build a Django-Unfold Admin Dashboard with Custom Models, Filters, Actions, and KPIs
ShareShareShareShareShare
(ROOT / "shop" / "admin.py").write_text('''
from django.contrib import admin, messages
from django.contrib.auth.admin import (UserAdmin as DjangoUserAdmin,
                                      GroupAdmin as DjangoGroupAdmin)
from django.contrib.auth.models import User, Group
from django.shortcuts import redirect
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
from unfold.admin import ModelAdmin, TabularInline
from unfold.contrib.filters.admin import (
   ChoicesDropdownFilter, RangeNumericFilter, RangeDateFilter,
   MultipleChoicesDropdownFilter,
)
from unfold.decorators import display, action
from .models import Category, Customer, Product, Order, OrderItem
admin.site.unregister(User); admin.site.unregister(Group)
@admin.register(User)
class UserAdmin(DjangoUserAdmin, ModelAdmin):
   pass
@admin.register(Group)
class GroupAdmin(DjangoGroupAdmin, ModelAdmin):
   pass
@admin.register(Category)
class CategoryAdmin(ModelAdmin):
   list_display = ("name", "parent", "show_active", "created_at")
   list_filter  = (("is_active", ChoicesDropdownFilter),)
   search_fields = ("name", "slug")
   prepopulated_fields = {"slug": ("name",)}
   list_filter_submit = True
   compressed_fields = True
   @display(description=_("Active"), boolean=True)
   def show_active(self, obj): return obj.is_active
@admin.register(Customer)
class CustomerAdmin(ModelAdmin):
   list_display = ("name","email","show_tier","lifetime_value","joined")
   list_filter  = (
       ("tier",            MultipleChoicesDropdownFilter),
       ("lifetime_value",  RangeNumericFilter),
       ("joined",          RangeDateFilter),
   )
   search_fields = ("name","email")
   list_filter_submit = True
   warn_unsaved_form  = True
   list_per_page = 25
   @display(description=_("Tier"), label={
       "bronze":"warning","silver":"info","gold":"success","platinum":"primary"})
   def show_tier(self, obj):
       return obj.get_tier_display(), obj.tier
class OrderItemInline(TabularInline):
   model = OrderItem
   extra = 0
   fields = ("product", "quantity", "unit_price", "position")
   ordering_field = "position"
   tab = True
@admin.register(Order)
class OrderAdmin(ModelAdmin):
   list_display = ("number","customer_link","show_status","total","created_at")
   list_filter  = (
       ("status",     ChoicesDropdownFilter),
       ("total",      RangeNumericFilter),
       ("created_at", RangeDateFilter),
   )
   search_fields = ("number","customer__name","customer__email")
   readonly_fields = ("created_at",)
   autocomplete_fields = ("customer",)
   inlines = [OrderItemInline]
   list_filter_submit = True
   fieldsets = (
       (_("Order"), {"classes":["tab"], "fields":("number","customer","status","total")}),
       (_("Notes"), {"classes":["tab"], "fields":("notes","created_at")}),
   )
   actions_list        = ["mark_paid_bulk"]
   actions_row         = ["mark_paid_row"]
   actions_detail      = ["duplicate_order"]
   actions_submit_line = ["save_and_ship"]
   @display(description=_("Status"), label={
       "pending":"warning","paid":"info","shipped":"primary",
       "delivered":"success","cancelled":"danger"})
   def show_status(self, obj):
       return obj.get_status_display(), obj.status
   @display(description=_("Customer"))
   def customer_link(self, obj):
       return format_html(\'{}\',
                          obj.customer_id, obj.customer.name)
   @action(description=_("Mark pending → PAID (all)"), icon="payments")
   def mark_paid_bulk(self, request, queryset=None):
       n = Order.objects.filter(status="pending").update(status="paid")
       self.message_user(request, f"Marked {n} orders as paid.", level=messages.SUCCESS)
   @action(description=_("Mark paid"), icon="payments", url_path="mark-paid-row")
   def mark_paid_row(self, request, object_id):
       Order.objects.filter(pk=object_id).update(status="paid")
       self.message_user(request, "Order marked as paid.", level=messages.SUCCESS)
       return redirect(request.META.get("HTTP_REFERER","/admin/"))
   @action(description=_("Duplicate"), icon="content_copy", url_path="duplicate")
   def duplicate_order(self, request, object_id):
       o = Order.objects.get(pk=object_id)
       o.pk = None; o.number = o.number + "-COPY"; o.status = "pending"; o.save()
       self.message_user(request, "Order duplicated.", level=messages.SUCCESS)
       return redirect(f"/admin/shop/order/{o.pk}/change/")
   @action(description=_("Save & ship"))
   def save_and_ship(self, request, obj):
       obj.status = "shipped"; obj.save()
       self.message_user(request, f"Order {obj.number} shipped.", level=messages.SUCCESS)
@admin.register(Product)
class ProductAdmin(ModelAdmin):
   list_display = ("name","sku","category","show_status",
                   "price_display","stock_badge","featured")
   list_editable = ("featured",)
   list_filter   = (
       ("status",   ChoicesDropdownFilter),
       ("category", admin.RelatedFieldListFilter),
       ("price",    RangeNumericFilter),
       ("featured", ChoicesDropdownFilter),
   )
   search_fields = ("name","sku")
   autocomplete_fields = ("category",)
   list_filter_submit = True
   list_per_page = 20
   save_on_top = True
   fieldsets = (
       (_("Basics"),  {"classes":["tab"],
                       "fields":("name","sku","category","status","featured")}),
       (_("Pricing"), {"classes":["tab"],
                       "fields":("price","has_discount","discount_percent","stock")}),
       (_("Content"), {"classes":["tab"], "fields":("description",)}),
   )
   conditional_fields = {"discount_percent": "has_discount == true"}
   @display(description=_("Status"), label={
       "draft":"info","active":"success","archived":"warning"})
   def show_status(self, obj):
       return obj.get_status_display(), obj.status
   @display(description=_("Price"))
   def price_display(self, obj):
       if obj.has_discount and obj.discount_percent:
           return format_html(
               \'${} \'
               \'${}\', obj.price, obj.final_price)
       return f"${obj.price}"
   @display(description=_("Stock"), ordering="stock",
            label={"out":"danger","low":"warning","ok":"success"})
   def stock_badge(self, obj):
       if obj.stock == 0:                  return "Out of stock", "out"
       if obj.stock < 10:                  return f"Low ({obj.stock})", "low"
       return f"{obj.stock} in stock", "ok"
''')
(ROOT / "templates" / "admin" / "index.html").write_text('''{% extends "admin/index.html" %}
{% load i18n %}
{% block content %}
{% for k in kpis %}

{{ k.title }}

{{ k.value }}

YOU MAY ALSO LIKE

Alien Isolation 2’s First Trailer Takes The Horror To A Colony Planet

Google DeepMind Releases Gemma 4 QAT Checkpoints: Q4_0 and a New Mobile Format Cut On-Device Memory

{{ k.footer }}

{% endfor %}

{% trans "Top categories" %}

    {% for c in top_cats %}
  • {{ c.name }}{{ c.n }}
  • {% endfor %}

{% trans "Orders by status" %}

    {% for s in by_status %}
  • {{ s.status }}{{ s.c }}
  • {% endfor %}
{{ block.super }} {% endblock %} ''')

Credit: Source link

ShareTweetSendSharePin

Related Posts

Alien Isolation 2’s First Trailer Takes The Horror To A Colony Planet
AI & Technology

Alien Isolation 2’s First Trailer Takes The Horror To A Colony Planet

June 5, 2026
Google DeepMind Releases Gemma 4 QAT Checkpoints: Q4_0 and a New Mobile Format Cut On-Device Memory
AI & Technology

Google DeepMind Releases Gemma 4 QAT Checkpoints: Q4_0 and a New Mobile Format Cut On-Device Memory

June 5, 2026
AI agents are learning on the job — just not for your whole team
AI & Technology

AI agents are learning on the job — just not for your whole team

June 5, 2026
Google Shuts Down The AI Image App Pixel Studio
AI & Technology

Google Shuts Down The AI Image App Pixel Studio

June 5, 2026
Next Post
White House Correspondents’ Dinner wrapped early, but Trump hoped to ‘let the show go on’

White House Correspondents' Dinner wrapped early, but Trump hoped to 'let the show go on'

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Search

No Result
View All Result
BITCOIN & MSTR ARE CRASHING AND IT’S ABOUT TO GET WORSE

BITCOIN & MSTR ARE CRASHING AND IT’S ABOUT TO GET WORSE

June 5, 2026
Video shows a car being swept away by floodwaters in southern China

Video shows a car being swept away by floodwaters in southern China

June 3, 2026
Snowflake CEO Weighs In on Sales Outlook, Signing  Billion Deal With Amazon

Snowflake CEO Weighs In on Sales Outlook, Signing $6 Billion Deal With Amazon

May 30, 2026

About

Learn more

Our Services

Legal

Privacy Policy

Terms of Use

Bloggers

Learn more

Article Links

Contact

Advertise

Ask us anything

©2020- TradePoint.io - All rights reserved!

Tradepoint.io, being just a publishing and technology platform, is not a registered broker-dealer or investment adviser. So we do not provide investment advice. Rather, brokerage services are provided to clients of Tradepoint.io by independent SEC-registered broker-dealers and members of FINRA/SIPC. Every form of investing carries some risk and past performance is not a guarantee of future results. “Tradepoint.io“, “Instant Investing” and “My Trading Tools” are registered trademarks of Apperbuild, LLC.

This website is operated by Apperbuild, LLC. We have no link to any brokerage firm and we do not provide investment advice. Every information and resource we provide is solely for the education of our readers. © 2020 Apperbuild, LLC. All rights reserved.

No Result
View All Result
  • Main
  • AI & Technology
  • Stock Charts
  • Market & News
  • Business
  • Finance Tips
  • Trade Tube
  • Blog
  • Shop

© 2023 - TradePoint.io - All Rights Reserved!