mass edit for compute plans
This commit is contained in:
parent
b32a19ffa2
commit
2da6285800
2 changed files with 243 additions and 1 deletions
|
@ -3,8 +3,12 @@ Admin classes for pricing models including compute plans, storage plans, and VSH
|
|||
"""
|
||||
|
||||
import re
|
||||
from django.contrib import admin
|
||||
from django.contrib import admin, messages
|
||||
from django.contrib.admin import helpers
|
||||
from django.utils.html import format_html
|
||||
from django import forms
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponseRedirect
|
||||
from adminsortable2.admin import SortableAdminMixin
|
||||
from import_export.admin import ImportExportModelAdmin
|
||||
from import_export import resources
|
||||
|
@ -27,6 +31,8 @@ from ..models import (
|
|||
Service,
|
||||
)
|
||||
|
||||
from ..models.base import Term
|
||||
|
||||
|
||||
def natural_sort_key(obj):
|
||||
"""Extract numeric parts for natural sorting"""
|
||||
|
@ -123,6 +129,41 @@ class ComputePlanResource(resources.ModelResource):
|
|||
)
|
||||
|
||||
|
||||
class MassUpdateComputePlanForm(forms.Form):
|
||||
"""Form for mass updating ComputePlan fields"""
|
||||
|
||||
active = forms.ChoiceField(
|
||||
choices=[("", "-- No change --"), ("True", "Active"), ("False", "Inactive")],
|
||||
required=False,
|
||||
help_text="Set active status for selected compute plans",
|
||||
)
|
||||
|
||||
term = forms.ChoiceField(
|
||||
choices=[("", "-- No change --")] + Term.choices,
|
||||
required=False,
|
||||
help_text="Set billing term for selected compute plans",
|
||||
)
|
||||
|
||||
group = forms.ModelChoiceField(
|
||||
queryset=ComputePlanGroup.objects.all(),
|
||||
required=False,
|
||||
empty_label="-- No change --",
|
||||
help_text="Set group for selected compute plans",
|
||||
)
|
||||
|
||||
valid_from = forms.DateField(
|
||||
widget=forms.DateInput(attrs={"type": "date"}),
|
||||
required=False,
|
||||
help_text="Set valid from date for selected compute plans",
|
||||
)
|
||||
|
||||
valid_to = forms.DateField(
|
||||
widget=forms.DateInput(attrs={"type": "date"}),
|
||||
required=False,
|
||||
help_text="Set valid to date for selected compute plans",
|
||||
)
|
||||
|
||||
|
||||
@admin.register(ComputePlan)
|
||||
class ComputePlanAdmin(ImportExportModelAdmin):
|
||||
"""Admin configuration for ComputePlan model with import/export functionality"""
|
||||
|
@ -141,6 +182,7 @@ class ComputePlanAdmin(ImportExportModelAdmin):
|
|||
search_fields = ("name", "cloud_provider__name", "group__name")
|
||||
list_filter = ("active", "cloud_provider", "group")
|
||||
inlines = [ComputePlanPriceInline]
|
||||
actions = ["mass_update_compute_plans"]
|
||||
|
||||
def changelist_view(self, request, extra_context=None):
|
||||
"""Override changelist view to apply natural sorting"""
|
||||
|
@ -164,6 +206,80 @@ class ComputePlanAdmin(ImportExportModelAdmin):
|
|||
|
||||
display_prices.short_description = "Prices (Amount Currency)"
|
||||
|
||||
def mass_update_compute_plans(self, request, queryset):
|
||||
"""Admin action to mass update compute plan fields"""
|
||||
if request.POST.get("post"):
|
||||
# Process the form submission
|
||||
form = MassUpdateComputePlanForm(request.POST)
|
||||
if form.is_valid():
|
||||
updated_count = 0
|
||||
updated_fields = []
|
||||
|
||||
# Prepare update data
|
||||
update_data = {}
|
||||
|
||||
# Handle active field
|
||||
if form.cleaned_data["active"]:
|
||||
update_data["active"] = form.cleaned_data["active"] == "True"
|
||||
updated_fields.append("active")
|
||||
|
||||
# Handle term field
|
||||
if form.cleaned_data["term"]:
|
||||
update_data["term"] = form.cleaned_data["term"]
|
||||
updated_fields.append("term")
|
||||
|
||||
# Handle group field
|
||||
if form.cleaned_data["group"]:
|
||||
update_data["group"] = form.cleaned_data["group"]
|
||||
updated_fields.append("group")
|
||||
|
||||
# Handle valid_from field
|
||||
if form.cleaned_data["valid_from"]:
|
||||
update_data["valid_from"] = form.cleaned_data["valid_from"]
|
||||
updated_fields.append("valid_from")
|
||||
|
||||
# Handle valid_to field
|
||||
if form.cleaned_data["valid_to"]:
|
||||
update_data["valid_to"] = form.cleaned_data["valid_to"]
|
||||
updated_fields.append("valid_to")
|
||||
|
||||
# Perform the bulk update
|
||||
if update_data:
|
||||
updated_count = queryset.update(**update_data)
|
||||
|
||||
# Create success message
|
||||
field_list = ", ".join(updated_fields)
|
||||
self.message_user(
|
||||
request,
|
||||
f"Successfully updated {updated_count} compute plan(s). "
|
||||
f"Updated fields: {field_list}",
|
||||
messages.SUCCESS,
|
||||
)
|
||||
else:
|
||||
self.message_user(
|
||||
request, "No fields were selected for update.", messages.WARNING
|
||||
)
|
||||
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
else:
|
||||
# Show the form
|
||||
form = MassUpdateComputePlanForm()
|
||||
|
||||
# Render the mass update template
|
||||
return render(
|
||||
request,
|
||||
"admin/mass_update_compute_plans.html",
|
||||
{
|
||||
"form": form,
|
||||
"queryset": queryset,
|
||||
"action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
|
||||
"opts": self.model._meta,
|
||||
"title": f"Mass Update {queryset.count()} Compute Plans",
|
||||
},
|
||||
)
|
||||
|
||||
mass_update_compute_plans.short_description = "Mass update selected compute plans"
|
||||
|
||||
|
||||
class VSHNAppCatBaseFeeInline(admin.TabularInline):
|
||||
"""Inline admin for VSHNAppCatBaseFee model"""
|
||||
|
|
126
hub/services/templates/admin/mass_update_compute_plans.html
Normal file
126
hub/services/templates/admin/mass_update_compute_plans.html
Normal file
|
@ -0,0 +1,126 @@
|
|||
{% extends "admin/base_site.html" %}
|
||||
{% load i18n admin_urls static admin_modify %}
|
||||
|
||||
{% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
|
||||
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
|
||||
› {{ title }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ title }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="mb-3">You are about to update <strong>{{ queryset.count }}</strong> compute plan(s). Please select the fields you want to update:</p>
|
||||
|
||||
<!-- Selected items display -->
|
||||
<div class="alert alert-info" role="alert">
|
||||
<h6 class="alert-heading">Selected Compute Plans:</h6>
|
||||
<div class="row">
|
||||
{% for obj in queryset %}
|
||||
<div class="col-md-6 mb-1">
|
||||
<small><i class="fas fa-server me-1"></i>{{ obj.name }} ({{ obj.cloud_provider }})</small>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Update form -->
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<!-- Pass selected items -->
|
||||
{% for obj in queryset %}
|
||||
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk }}">
|
||||
{% endfor %}
|
||||
<input type="hidden" name="action" value="mass_update_compute_plans">
|
||||
<input type="hidden" name="post" value="yes">
|
||||
|
||||
<!-- Form fields -->
|
||||
<div class="row mb-3">
|
||||
<label for="{{ form.active.id_for_label }}" class="col-sm-3 col-form-label">Active Status:</label>
|
||||
<div class="col-sm-9">
|
||||
{{ form.active }}
|
||||
{% if form.active.help_text %}
|
||||
<div class="form-text">{{ form.active.help_text }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<label for="{{ form.term.id_for_label }}" class="col-sm-3 col-form-label">Term:</label>
|
||||
<div class="col-sm-9">
|
||||
{{ form.term }}
|
||||
{% if form.term.help_text %}
|
||||
<div class="form-text">{{ form.term.help_text }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<label for="{{ form.group.id_for_label }}" class="col-sm-3 col-form-label">Group:</label>
|
||||
<div class="col-sm-9">
|
||||
{{ form.group }}
|
||||
{% if form.group.help_text %}
|
||||
<div class="form-text">{{ form.group.help_text }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<label for="{{ form.valid_from.id_for_label }}" class="col-sm-3 col-form-label">Valid From:</label>
|
||||
<div class="col-sm-9">
|
||||
{{ form.valid_from }}
|
||||
{% if form.valid_from.help_text %}
|
||||
<div class="form-text">{{ form.valid_from.help_text }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<label for="{{ form.valid_to.id_for_label }}" class="col-sm-3 col-form-label">Valid To:</label>
|
||||
<div class="col-sm-9">
|
||||
{{ form.valid_to }}
|
||||
{% if form.valid_to.help_text %}
|
||||
<div class="form-text">{{ form.valid_to.help_text }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Submit buttons -->
|
||||
<div class="row">
|
||||
<div class="col-sm-9 offset-sm-3">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save me-1"></i>Update Compute Plans
|
||||
</button>
|
||||
<a href="{% url opts|admin_urlname:'changelist' %}" class="btn btn-secondary ms-2">
|
||||
<i class="fas fa-times me-1"></i>Cancel
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const selects = document.querySelectorAll('select');
|
||||
const inputs = document.querySelectorAll('input[type="date"]');
|
||||
|
||||
selects.forEach(select => {
|
||||
select.classList.add('form-select');
|
||||
});
|
||||
|
||||
inputs.forEach(input => {
|
||||
input.classList.add('form-control');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue