refactored admin.py and renamed helper file

This commit is contained in:
David Kennedy 2025-01-09 07:24:30 -05:00
parent 8d436c7bb0
commit 9a8b236d5d
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
4 changed files with 78 additions and 137 deletions

View file

@ -29,7 +29,7 @@ from django_fsm import get_available_FIELD_transitions, FSMField
from registrar.models import DomainInformation, Portfolio, UserPortfolioPermission, DomainInvitation from registrar.models import DomainInformation, Portfolio, UserPortfolioPermission, DomainInvitation
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
from registrar.utility.email_invitations import send_domain_invitation_email, send_portfolio_invitation_email from registrar.utility.email_invitations import send_domain_invitation_email, send_portfolio_invitation_email
from registrar.views.utility.portfolio_helper import ( from registrar.views.utility.invitation_helper import (
get_org_membership, get_org_membership,
get_requested_user, get_requested_user,
handle_invitation_exceptions, handle_invitation_exceptions,
@ -1393,8 +1393,78 @@ class UserDomainRoleAdmin(ListHeaderAdmin, ImportExportModelAdmin):
return super().changeform_view(request, object_id, form_url, extra_context=extra_context) return super().changeform_view(request, object_id, form_url, extra_context=extra_context)
class BaseInvitationAdmin(ListHeaderAdmin):
"""Base class for admin classes which will customize save_model and send email invitations
on model adds, and require custom handling of forms and form errors."""
class DomainInvitationAdmin(ListHeaderAdmin): def response_add(self, request, obj, post_url_continue=None):
"""
Override response_add to handle rendering when exceptions are raised during add model.
Normal flow on successful save_model on add is to redirect to changelist_view.
If there are errors, flow is modified to instead render change form.
"""
# store current messages from request so that they are preserved throughout the method
storage = get_messages(request)
# Check if there are any error or warning messages in the `messages` framework
has_errors = any(message.level_tag in ["error", "warning"] for message in storage)
if has_errors:
# Re-render the change form if there are errors or warnings
# Prepare context for rendering the change form
# Get the model form
ModelForm = self.get_form(request, obj=obj)
form = ModelForm(instance=obj)
# Create an AdminForm instance
admin_form = AdminForm(
form,
list(self.get_fieldsets(request, obj)),
self.get_prepopulated_fields(request, obj),
self.get_readonly_fields(request, obj),
model_admin=self,
)
media = self.media + form.media
opts = obj._meta
change_form_context = {
**self.admin_site.each_context(request), # Add admin context
"title": f"Add {opts.verbose_name}",
"opts": opts,
"original": obj,
"save_as": self.save_as,
"has_change_permission": self.has_change_permission(request, obj),
"add": True, # Indicate this is an "Add" form
"change": False, # Indicate this is not a "Change" form
"is_popup": False,
"inline_admin_formsets": [],
"save_on_top": self.save_on_top,
"show_delete": self.has_delete_permission(request, obj),
"obj": obj,
"adminform": admin_form, # Pass the AdminForm instance
"media": media,
"errors": None,
}
return self.render_change_form(
request,
context=change_form_context,
add=True,
change=False,
obj=obj,
)
response = super().response_add(request, obj, post_url_continue)
# Re-add all messages from storage after `super().response_add`
# as super().response_add resets the success messages in request
for message in storage:
messages.add_message(request, message.level, message.message)
return response
class DomainInvitationAdmin(BaseInvitationAdmin):
"""Custom domain invitation admin class.""" """Custom domain invitation admin class."""
class Meta: class Meta:
@ -1497,74 +1567,8 @@ class DomainInvitationAdmin(ListHeaderAdmin):
# Call the parent save method to save the object # Call the parent save method to save the object
super().save_model(request, obj, form, change) super().save_model(request, obj, form, change)
def response_add(self, request, obj, post_url_continue=None):
"""
Override response_add to handle rendering when exceptions are raised during add model.
Normal flow on successful save_model on add is to redirect to changelist_view. class PortfolioInvitationAdmin(BaseInvitationAdmin):
If there are errors, flow is modified to instead render change form.
"""
# Check if there are any error or warning messages in the `messages` framework
storage = get_messages(request)
has_errors = any(message.level_tag in ["error", "warning"] for message in storage)
if has_errors:
# Re-render the change form if there are errors or warnings
# Prepare context for rendering the change form
# Get the model form
ModelForm = self.get_form(request, obj=obj)
form = ModelForm(instance=obj)
# Create an AdminForm instance
admin_form = AdminForm(
form,
list(self.get_fieldsets(request, obj)),
self.get_prepopulated_fields(request, obj),
self.get_readonly_fields(request, obj),
model_admin=self,
)
media = self.media + form.media
opts = obj._meta
change_form_context = {
**self.admin_site.each_context(request), # Add admin context
"title": f"Add {opts.verbose_name}",
"opts": opts,
"original": obj,
"save_as": self.save_as,
"has_change_permission": self.has_change_permission(request, obj),
"add": True, # Indicate this is an "Add" form
"change": False, # Indicate this is not a "Change" form
"is_popup": False,
"inline_admin_formsets": [],
"save_on_top": self.save_on_top,
"show_delete": self.has_delete_permission(request, obj),
"obj": obj,
"adminform": admin_form, # Pass the AdminForm instance
"media": media,
"errors": None,
}
return self.render_change_form(
request,
context=change_form_context,
add=True,
change=False,
obj=obj,
)
# Preserve all success messages
all_messages = [message for message in get_messages(request)]
response = super().response_add(request, obj, post_url_continue)
# Re-add all messages to the storage after `super().response_add` to preserve them
for message in all_messages:
messages.add_message(request, message.level, message.message)
return response
class PortfolioInvitationAdmin(ListHeaderAdmin):
"""Custom portfolio invitation admin class.""" """Custom portfolio invitation admin class."""
form = PortfolioInvitationAdminForm form = PortfolioInvitationAdminForm
@ -1647,72 +1651,6 @@ class PortfolioInvitationAdmin(ListHeaderAdmin):
# Call the parent save method to save the object # Call the parent save method to save the object
super().save_model(request, obj, form, change) super().save_model(request, obj, form, change)
def response_add(self, request, obj, post_url_continue=None):
"""
Override response_add to handle rendering when exceptions are raised during add model.
Normal flow on successful save_model on add is to redirect to changelist_view.
If there are errors, flow is modified to instead render change form.
"""
# Check if there are any error or warning messages in the `messages` framework
storage = get_messages(request)
has_errors = any(message.level_tag in ["error", "warning"] for message in storage)
if has_errors:
# Re-render the change form if there are errors or warnings
# Prepare context for rendering the change form
# Get the model form
ModelForm = self.get_form(request, obj=obj)
form = ModelForm(instance=obj)
# Create an AdminForm instance
admin_form = AdminForm(
form,
list(self.get_fieldsets(request, obj)),
self.get_prepopulated_fields(request, obj),
self.get_readonly_fields(request, obj),
model_admin=self,
)
media = self.media + form.media
opts = obj._meta
change_form_context = {
**self.admin_site.each_context(request), # Add admin context
"title": f"Add {opts.verbose_name}",
"opts": opts,
"original": obj,
"save_as": self.save_as,
"has_change_permission": self.has_change_permission(request, obj),
"add": True, # Indicate this is an "Add" form
"change": False, # Indicate this is not a "Change" form
"is_popup": False,
"inline_admin_formsets": [],
"save_on_top": self.save_on_top,
"show_delete": self.has_delete_permission(request, obj),
"obj": obj,
"adminform": admin_form, # Pass the AdminForm instance
"media": media,
"errors": None,
}
return self.render_change_form(
request,
context=change_form_context,
add=True,
change=False,
obj=obj,
)
# Preserve all success messages
all_messages = [message for message in get_messages(request)]
response = super().response_add(request, obj, post_url_continue)
# Re-add all messages to the storage after `super().response_add` to preserve them
for message in all_messages:
messages.add_message(request, message.level, message.message)
return response
class DomainInformationResource(resources.ModelResource): class DomainInformationResource(resources.ModelResource):
"""defines how each field in the referenced model should be mapped to the corresponding fields in the """defines how each field in the referenced model should be mapped to the corresponding fields in the

View file

@ -42,7 +42,7 @@ from registrar.utility.errors import (
from registrar.models.utility.contact_error import ContactError from registrar.models.utility.contact_error import ContactError
from registrar.views.utility.permission_views import UserDomainRolePermissionDeleteView from registrar.views.utility.permission_views import UserDomainRolePermissionDeleteView
from registrar.utility.waffle import flag_is_active_for_user from registrar.utility.waffle import flag_is_active_for_user
from registrar.views.utility.portfolio_helper import ( from registrar.views.utility.invitation_helper import (
get_org_membership, get_org_membership,
get_requested_user, get_requested_user,
handle_invitation_exceptions, handle_invitation_exceptions,

View file

@ -34,7 +34,7 @@ from django.views.generic import View
from django.views.generic.edit import FormMixin from django.views.generic.edit import FormMixin
from django.db import IntegrityError from django.db import IntegrityError
from registrar.views.utility.portfolio_helper import get_org_membership from registrar.views.utility.invitation_helper import get_org_membership
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View file

@ -15,6 +15,9 @@ from registrar.utility.errors import (
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# These methods are used by multiple views which share similar logic and function
# when creating invitations and sending associated emails. These can be reused in
# any view, and were initially developed for domain.py, portfolios.py and admin.py
def get_org_membership(requestor_org, requested_email, requested_user): def get_org_membership(requestor_org, requested_email, requested_user):
""" """