diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js
index 4a6d98522..0d594b315 100644
--- a/src/registrar/assets/js/get-gov.js
+++ b/src/registrar/assets/js/get-gov.js
@@ -879,6 +879,146 @@ function unloadModals() {
window.modal.off();
}
+/**
+ * Helper function that scrolls to an element
+ * @param {string} attributeName - The string "class" or "id"
+ * @param {string} attributeValue - The class or id name
+ */
+function ScrollToElement(attributeName, attributeValue) {
+ let targetEl = null;
+
+ if (attributeName === 'class') {
+ targetEl = document.getElementsByClassName(attributeValue)[0];
+ } else if (attributeName === 'id') {
+ targetEl = document.getElementById(attributeValue);
+ } else {
+ console.log('Error: unknown attribute name provided.');
+ return; // Exit the function if an invalid attributeName is provided
+ }
+
+ if (targetEl) {
+ const rect = targetEl.getBoundingClientRect();
+ const scrollTop = window.scrollY || document.documentElement.scrollTop;
+ window.scrollTo({
+ top: rect.top + scrollTop,
+ behavior: 'smooth' // Optional: for smooth scrolling
+ });
+ }
+}
+
+/**
+ * Generalized function to update pagination for a list.
+ * @param {string} itemName - The name displayed in the counter
+ * @param {string} paginationSelector - CSS selector for the pagination container.
+ * @param {string} counterSelector - CSS selector for the pagination counter.
+ * @param {string} headerAnchor - CSS selector for the header element to anchor the links to.
+ * @param {Function} loadPageFunction - Function to call when a page link is clicked.
+ * @param {number} currentPage - The current page number (starting with 1).
+ * @param {number} numPages - The total number of pages.
+ * @param {boolean} hasPrevious - Whether there is a page before the current page.
+ * @param {boolean} hasNext - Whether there is a page after the current page.
+ * @param {number} totalItems - The total number of items.
+ */
+function updatePagination(itemName, paginationSelector, counterSelector, headerAnchor, loadPageFunction, currentPage, numPages, hasPrevious, hasNext, totalItems) {
+ const paginationContainer = document.querySelector(paginationSelector);
+ const paginationCounter = document.querySelector(counterSelector);
+ const paginationButtons = document.querySelector(`${paginationSelector} .usa-pagination__list`);
+ paginationCounter.innerHTML = '';
+ paginationButtons.innerHTML = '';
+
+ // Buttons should only be displayed if there are more than one pages of results
+ paginationButtons.classList.toggle('display-none', numPages <= 1);
+
+ // Counter should only be displayed if there is more than 1 item
+ paginationContainer.classList.toggle('display-none', totalItems < 1);
+
+ paginationCounter.innerHTML = `${totalItems} ${itemName}${totalItems > 1 ? 's' : ''}`;
+
+ if (hasPrevious) {
+ const prevPageItem = document.createElement('li');
+ prevPageItem.className = 'usa-pagination__item usa-pagination__arrow';
+ prevPageItem.innerHTML = `
+
+ `;
+ prevPageItem.querySelector('a').addEventListener('click', (event) => {
+ event.preventDefault();
+ loadPageFunction(currentPage - 1);
+ });
+ paginationButtons.appendChild(prevPageItem);
+ }
+
+ // Helper function to create a page item
+ function createPageItem(page) {
+ const pageItem = document.createElement('li');
+ pageItem.className = 'usa-pagination__item usa-pagination__page-no';
+ pageItem.innerHTML = `
+
+ `;
+ if (page === currentPage) {
+ pageItem.querySelector('a').classList.add('usa-current');
+ pageItem.querySelector('a').setAttribute('aria-current', 'page');
+ }
+ pageItem.querySelector('a').addEventListener('click', (event) => {
+ event.preventDefault();
+ loadPageFunction(page);
+ });
+ return pageItem;
+ }
+
+ // Add first page and ellipsis if necessary
+ if (currentPage > 2) {
+ paginationButtons.appendChild(createPageItem(1));
+ if (currentPage > 3) {
+ const ellipsis = document.createElement('li');
+ ellipsis.className = 'usa-pagination__item usa-pagination__overflow';
+ ellipsis.setAttribute('aria-label', 'ellipsis indicating non-visible pages');
+ ellipsis.innerHTML = '…';
+ paginationButtons.appendChild(ellipsis);
+ }
+ }
+
+ // Add pages around the current page
+ for (let i = Math.max(1, currentPage - 1); i <= Math.min(numPages, currentPage + 1); i++) {
+ paginationButtons.appendChild(createPageItem(i));
+ }
+
+ // Add last page and ellipsis if necessary
+ if (currentPage < numPages - 1) {
+ if (currentPage < numPages - 2) {
+ const ellipsis = document.createElement('li');
+ ellipsis.className = 'usa-pagination__item usa-pagination__overflow';
+ ellipsis.setAttribute('aria-label', 'ellipsis indicating non-visible pages');
+ ellipsis.innerHTML = '…';
+ paginationButtons.appendChild(ellipsis);
+ }
+ paginationButtons.appendChild(createPageItem(numPages));
+ }
+
+ if (hasNext) {
+ const nextPageItem = document.createElement('li');
+ nextPageItem.className = 'usa-pagination__item usa-pagination__arrow';
+ nextPageItem.innerHTML = `
+
+ `;
+ nextPageItem.querySelector('a').addEventListener('click', (event) => {
+ event.preventDefault();
+ loadPageFunction(currentPage + 1);
+ });
+ paginationButtons.appendChild(nextPageItem);
+ }
+}
+
+
/**
* An IIFE that listens for DOM Content to be loaded, then executes. This function
* initializes the domains list and associated functionality on the home page of the app.
@@ -891,6 +1031,7 @@ document.addEventListener('DOMContentLoaded', function() {
let currentSortBy = 'id';
let currentOrder = 'asc';
let noDomainsWrapper = document.querySelector('.no-domains-wrapper');
+ let hasLoaded = false;
/**
* Loads rows in the domains list, as well as updates pagination around the domains list
@@ -898,8 +1039,9 @@ document.addEventListener('DOMContentLoaded', function() {
* @param {*} page - the page number of the results (starts with 1)
* @param {*} sortBy - the sort column option
* @param {*} order - the sort order {asc, desc}
+ * @param {*} loaded - control for the scrollToElement functionality
*/
- function loadDomains(page, sortBy = currentSortBy, order = currentOrder) {
+ function loadDomains(page, sortBy = currentSortBy, order = currentOrder, loaded = hasLoaded) {
//fetch json of page of domains, given page # and sort
fetch(`/get-domains-json/?page=${page}&sort_by=${sortBy}&order=${order}`)
.then(response => response.json())
@@ -923,9 +1065,12 @@ document.addEventListener('DOMContentLoaded', function() {
domainList.innerHTML = '';
data.domains.forEach(domain => {
+ const options = { year: 'numeric', month: 'short', day: 'numeric' };
const expirationDate = domain.expiration_date ? new Date(domain.expiration_date) : null;
+ const expirationDateFormatted = expirationDate ? expirationDate.toLocaleDateString('en-US', options) : null;
const expirationDateSortValue = expirationDate ? expirationDate.getTime() : '';
const actionUrl = domain.action_url;
+
const row = document.createElement('tr');
row.innerHTML = `
@@ -933,7 +1078,7 @@ document.addEventListener('DOMContentLoaded', function() {
${domain.name}
- ${expirationDate ? expirationDate.toLocaleDateString() : ''}
+ ${expirationDateFormatted}
|
${domain.state_display}
@@ -961,84 +1106,31 @@ document.addEventListener('DOMContentLoaded', function() {
});
// initialize tool tips immediately after the associated DOM elements are added
initializeTooltips();
+ if (loaded)
+ ScrollToElement('id', 'domains-header');
hasLoaded = true;
// update pagination
- updateDomainsPagination(data.page, data.num_pages, data.has_previous, data.has_next, data.total);
+ updatePagination(
+ 'domain',
+ '#domains-pagination',
+ '#domains-pagination .usa-pagination__counter',
+ '#domains-header',
+ loadDomains,
+ data.page,
+ data.num_pages,
+ data.has_previous,
+ data.has_next,
+ data.total
+ );
currentSortBy = sortBy;
currentOrder = order;
})
.catch(error => console.error('Error fetching domains:', error));
}
- /**
- * Update the pagination below the domains list.
- * @param {*} currentPage - the current page number (starting with 1)
- * @param {*} numPages - the number of pages indicated by the domains list response
- * @param {*} hasPrevious - if there is a page of results prior to the current page
- * @param {*} hasNext - if there is a page of results after the current page
- */
- function updateDomainsPagination(currentPage, numPages, hasPrevious, hasNext, totalItems) {
- // identify the DOM element where the pagination will be inserted
- const paginationContainer = document.querySelector('#domains-pagination');
- const paginationCounter = document.querySelector('#domains-pagination .usa-pagination__counter');
- const paginationButtons = document.querySelector('#domains-pagination .usa-pagination__list');
- paginationCounter.innerHTML = '';
- paginationButtons.innerHTML = '';
-
- // Buttons should only be displayed if there are more than one pages of results
- paginationButtons.classList.toggle('display-none', numPages <= 1);
-
- // Counter should only be displayed if there is more than 1 item
- paginationContainer.classList.toggle('display-none', totalItems < 1);
-
- paginationCounter.innerHTML = `${totalItems} domain${totalItems > 1 ? 's' : ''}`;
- if (hasPrevious) {
- const prevPageItem = document.createElement('li');
- prevPageItem.className = 'usa-pagination__item usa-pagination__arrow';
- prevPageItem.innerHTML = `
-
- `;
- prevPageItem.querySelector('a').addEventListener('click', () => loadDomains(currentPage - 1));
- paginationButtons.appendChild(prevPageItem);
- }
-
- for (let i = 1; i <= numPages; i++) {
- const pageItem = document.createElement('li');
- pageItem.className = 'usa-pagination__item usa-pagination__page-no';
- pageItem.innerHTML = `
-
- `;
- if (i === currentPage) {
- pageItem.querySelector('a').classList.add('usa-current');
- pageItem.querySelector('a').setAttribute('aria-current', 'page');
- }
- pageItem.querySelector('a').addEventListener('click', () => loadDomains(i));
- paginationButtons.appendChild(pageItem);
- }
-
- if (hasNext) {
- const nextPageItem = document.createElement('li');
- nextPageItem.className = 'usa-pagination__item usa-pagination__arrow';
- nextPageItem.innerHTML = `
-
- `;
- nextPageItem.querySelector('a').addEventListener('click', () => loadDomains(currentPage + 1));
- paginationButtons.appendChild(nextPageItem);
- }
- }
// Add event listeners to table headers for sorting
document.querySelectorAll('.dotgov-table__registered-domains th[data-sortable]').forEach(header => {
@@ -1060,6 +1152,17 @@ document.addEventListener('DOMContentLoaded', function() {
}
});
+const utcDateString = (dateString) => {
+ const date = new Date(dateString);
+ const utcYear = date.getUTCFullYear();
+ const utcMonth = date.toLocaleString('en-US', { month: 'short', timeZone: 'UTC' });
+ const utcDay = date.getUTCDate().toString().padStart(2, '0');
+ const utcHours = date.getUTCHours().toString().padStart(2, '0');
+ const utcMinutes = date.getUTCMinutes().toString().padStart(2, '0');
+
+ return `${utcMonth} ${utcDay}, ${utcYear}, ${utcHours}:${utcMinutes} UTC`;
+};
+
/**
* An IIFE that listens for DOM Content to be loaded, then executes. This function
* initializes the domain requests list and associated functionality on the home page of the app.
@@ -1072,6 +1175,7 @@ document.addEventListener('DOMContentLoaded', function() {
let currentSortBy = 'id';
let currentOrder = 'asc';
let noDomainRequestsWrapper = document.querySelector('.no-domain-requests-wrapper');
+ let hasLoaded = false;
/**
* Loads rows in the domain requests list, as well as updates pagination around the domain requests list
@@ -1079,8 +1183,9 @@ document.addEventListener('DOMContentLoaded', function() {
* @param {*} page - the page number of the results (starts with 1)
* @param {*} sortBy - the sort column option
* @param {*} order - the sort order {asc, desc}
+ * @param {*} loaded - control for the scrollToElement functionality
*/
- function loadDomainRequests(page, sortBy = currentSortBy, order = currentOrder) {
+ function loadDomainRequests(page, sortBy = currentSortBy, order = currentOrder, loaded = hasLoaded) {
//fetch json of page of domain requests, given page # and sort
fetch(`/get-domain-requests-json/?page=${page}&sort_by=${sortBy}&order=${order}`)
.then(response => response.json())
@@ -1107,10 +1212,10 @@ document.addEventListener('DOMContentLoaded', function() {
// after the DOM content changes and there are new delete modal buttons added
unloadModals();
data.domain_requests.forEach(request => {
- const domainName = request.requested_domain ? request.requested_domain : `New domain request (${new Date(request.created_at).toLocaleString()} UTC)`;
+ const options = { year: 'numeric', month: 'short', day: 'numeric' };
+ const domainName = request.requested_domain ? request.requested_domain : `New domain request (${utcDateString(request.created_at)})`;
const actionUrl = request.action_url;
const actionLabel = request.action_label;
- const options = { year: 'numeric', month: 'short', day: 'numeric' };
const submissionDate = request.submission_date ? new Date(request.submission_date).toLocaleDateString('en-US', options) : `Not submitted`;
const deleteButton = request.is_deletable ? `
console.error('Error fetching domain requests:', error));
}
- /**
- * Update the pagination below the domain requests list.
- * @param {*} currentPage - the current page number (starting with 1)
- * @param {*} numPages - the number of pages indicated by the domain request list response
- * @param {*} hasPrevious - if there is a page of results prior to the current page
- * @param {*} hasNext - if there is a page of results after the current page
- */
- function updateDomainRequestsPagination(currentPage, numPages, hasPrevious, hasNext, totalItems) {
- // identify the DOM element where pagination is contained
- const paginationContainer = document.querySelector('#domain-requests-pagination');
- const paginationCounter = document.querySelector('#domain-requests-pagination .usa-pagination__counter');
- const paginationButtons = document.querySelector('#domain-requests-pagination .usa-pagination__list');
- paginationCounter.innerHTML = '';
- paginationButtons.innerHTML = '';
-
- // Buttons should only be displayed if there are more than one pages of results
- paginationButtons.classList.toggle('display-none', numPages <= 1);
-
- // Counter should only be displayed if there is more than 1 item
- paginationContainer.classList.toggle('display-none', totalItems < 1);
-
- paginationCounter.innerHTML = `${totalItems} domain request${totalItems > 1 ? 's' : ''}`;
-
- if (hasPrevious) {
- const prevPageItem = document.createElement('li');
- prevPageItem.className = 'usa-pagination__item usa-pagination__arrow';
- prevPageItem.innerHTML = `
-
- `;
- prevPageItem.querySelector('a').addEventListener('click', () => loadDomainRequests(currentPage - 1));
- paginationButtons.appendChild(prevPageItem);
- }
-
- for (let i = 1; i <= numPages; i++) {
- const pageItem = document.createElement('li');
- pageItem.className = 'usa-pagination__item usa-pagination__page-no';
- pageItem.innerHTML = `
-
- `;
- if (i === currentPage) {
- pageItem.querySelector('a').classList.add('usa-current');
- pageItem.querySelector('a').setAttribute('aria-current', 'page');
- }
- pageItem.querySelector('a').addEventListener('click', () => loadDomainRequests(i));
- paginationButtons.appendChild(pageItem);
- }
-
- if (hasNext) {
- const nextPageItem = document.createElement('li');
- nextPageItem.className = 'usa-pagination__item usa-pagination__arrow';
- nextPageItem.innerHTML = `
-
- `;
- nextPageItem.querySelector('a').addEventListener('click', () => loadDomainRequests(currentPage + 1));
- paginationButtons.appendChild(nextPageItem);
- }
- }
-
// Add event listeners to table headers for sorting
document.querySelectorAll('.dotgov-table__domain-requests th[data-sortable]').forEach(header => {
header.addEventListener('click', function() {
diff --git a/src/registrar/assets/sass/_theme/_pagination.scss b/src/registrar/assets/sass/_theme/_pagination.scss
new file mode 100644
index 000000000..53fa3fff9
--- /dev/null
+++ b/src/registrar/assets/sass/_theme/_pagination.scss
@@ -0,0 +1,15 @@
+@use "uswds-core" as *;
+
+.usa-pagination {
+ flex-wrap: wrap;
+ background-color: transparent;
+ .usa-current {
+ background-color: color('base-dark');
+ }
+}
+
+@include at-media(desktop) {
+ .usa-pagination {
+ flex-wrap: nowrap;
+ }
+}
diff --git a/src/registrar/assets/sass/_theme/styles.scss b/src/registrar/assets/sass/_theme/styles.scss
index 64b113a29..921976b44 100644
--- a/src/registrar/assets/sass/_theme/styles.scss
+++ b/src/registrar/assets/sass/_theme/styles.scss
@@ -13,6 +13,7 @@
@forward "links";
@forward "lists";
@forward "buttons";
+@forward "pagination";
@forward "forms";
@forward "tooltips";
@forward "fieldsets";
diff --git a/src/registrar/migrations/0096_alter_contact_email_alter_contact_first_name_and_more.py b/src/registrar/migrations/0096_alter_contact_email_alter_contact_first_name_and_more.py
new file mode 100644
index 000000000..68cbc625b
--- /dev/null
+++ b/src/registrar/migrations/0096_alter_contact_email_alter_contact_first_name_and_more.py
@@ -0,0 +1,131 @@
+# Generated by Django 4.2.10 on 2024-05-28 14:40
+
+from django.db import migrations, models
+import phonenumber_field.modelfields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("registrar", "0095_user_middle_name_user_title"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="contact",
+ name="email",
+ field=models.EmailField(blank=True, max_length=320, null=True),
+ ),
+ migrations.AlterField(
+ model_name="contact",
+ name="first_name",
+ field=models.CharField(blank=True, null=True, verbose_name="first name"),
+ ),
+ migrations.AlterField(
+ model_name="contact",
+ name="last_name",
+ field=models.CharField(blank=True, null=True, verbose_name="last name"),
+ ),
+ migrations.AlterField(
+ model_name="contact",
+ name="phone",
+ field=phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True, region=None),
+ ),
+ migrations.AlterField(
+ model_name="domaininformation",
+ name="organization_name",
+ field=models.CharField(blank=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name="domaininformation",
+ name="zipcode",
+ field=models.CharField(blank=True, max_length=10, null=True, verbose_name="zip code"),
+ ),
+ migrations.AlterField(
+ model_name="domainrequest",
+ name="organization_name",
+ field=models.CharField(blank=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name="domainrequest",
+ name="zipcode",
+ field=models.CharField(blank=True, max_length=10, null=True, verbose_name="zip code"),
+ ),
+ migrations.AlterField(
+ model_name="transitiondomain",
+ name="first_name",
+ field=models.CharField(
+ blank=True, help_text="First name / given name", null=True, verbose_name="first name"
+ ),
+ ),
+ migrations.AlterField(
+ model_name="transitiondomain",
+ name="organization_name",
+ field=models.CharField(blank=True, help_text="Organization name", null=True),
+ ),
+ migrations.AlterField(
+ model_name="transitiondomain",
+ name="zipcode",
+ field=models.CharField(blank=True, help_text="Zip code", max_length=10, null=True, verbose_name="zip code"),
+ ),
+ migrations.AlterField(
+ model_name="user",
+ name="phone",
+ field=phonenumber_field.modelfields.PhoneNumberField(
+ blank=True, help_text="Phone", max_length=128, null=True, region=None
+ ),
+ ),
+ migrations.AlterField(
+ model_name="verifiedbystaff",
+ name="email",
+ field=models.EmailField(max_length=254),
+ ),
+ migrations.AddIndex(
+ model_name="contact",
+ index=models.Index(fields=["user"], name="registrar_c_user_id_4059c4_idx"),
+ ),
+ migrations.AddIndex(
+ model_name="contact",
+ index=models.Index(fields=["email"], name="registrar_c_email_bde2de_idx"),
+ ),
+ migrations.AddIndex(
+ model_name="domain",
+ index=models.Index(fields=["name"], name="registrar_d_name_5b1956_idx"),
+ ),
+ migrations.AddIndex(
+ model_name="domain",
+ index=models.Index(fields=["state"], name="registrar_d_state_84c134_idx"),
+ ),
+ migrations.AddIndex(
+ model_name="domaininformation",
+ index=models.Index(fields=["domain"], name="registrar_d_domain__88838a_idx"),
+ ),
+ migrations.AddIndex(
+ model_name="domaininformation",
+ index=models.Index(fields=["domain_request"], name="registrar_d_domain__d1fba8_idx"),
+ ),
+ migrations.AddIndex(
+ model_name="domaininvitation",
+ index=models.Index(fields=["status"], name="registrar_d_status_e84571_idx"),
+ ),
+ migrations.AddIndex(
+ model_name="domainrequest",
+ index=models.Index(fields=["requested_domain"], name="registrar_d_request_6894eb_idx"),
+ ),
+ migrations.AddIndex(
+ model_name="domainrequest",
+ index=models.Index(fields=["approved_domain"], name="registrar_d_approve_ac4c46_idx"),
+ ),
+ migrations.AddIndex(
+ model_name="domainrequest",
+ index=models.Index(fields=["status"], name="registrar_d_status_a32b59_idx"),
+ ),
+ migrations.AddIndex(
+ model_name="user",
+ index=models.Index(fields=["username"], name="registrar_u_usernam_964b1b_idx"),
+ ),
+ migrations.AddIndex(
+ model_name="user",
+ index=models.Index(fields=["email"], name="registrar_u_email_c8f2c4_idx"),
+ ),
+ ]
diff --git a/src/registrar/models/contact.py b/src/registrar/models/contact.py
index a5a6ff16c..91a7515c7 100644
--- a/src/registrar/models/contact.py
+++ b/src/registrar/models/contact.py
@@ -17,6 +17,14 @@ class Contact(TimeStampedModel):
will be updated if any updates are made to it through Login.gov.
"""
+ class Meta:
+ """Contains meta information about this class"""
+
+ indexes = [
+ models.Index(fields=["user"]),
+ models.Index(fields=["email"]),
+ ]
+
user = models.OneToOneField(
"registrar.User",
null=True,
@@ -28,7 +36,6 @@ class Contact(TimeStampedModel):
null=True,
blank=True,
verbose_name="first name",
- db_index=True,
)
middle_name = models.CharField(
null=True,
@@ -38,7 +45,6 @@ class Contact(TimeStampedModel):
null=True,
blank=True,
verbose_name="last name",
- db_index=True,
)
title = models.CharField(
null=True,
@@ -48,13 +54,11 @@ class Contact(TimeStampedModel):
email = models.EmailField(
null=True,
blank=True,
- db_index=True,
max_length=320,
)
phone = PhoneNumberField(
null=True,
blank=True,
- db_index=True,
)
def _get_all_relations(self):
diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py
index bc9508f30..26dcb89a7 100644
--- a/src/registrar/models/domain.py
+++ b/src/registrar/models/domain.py
@@ -65,6 +65,14 @@ class Domain(TimeStampedModel, DomainHelper):
domain meets the required checks.
"""
+ class Meta:
+ """Contains meta information about this class"""
+
+ indexes = [
+ models.Index(fields=["name"]),
+ models.Index(fields=["state"]),
+ ]
+
def __init__(self, *args, **kwargs):
self._cache = {}
super(Domain, self).__init__(*args, **kwargs)
diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py
index 95af94010..3cd74b0d8 100644
--- a/src/registrar/models/domain_information.py
+++ b/src/registrar/models/domain_information.py
@@ -22,6 +22,16 @@ class DomainInformation(TimeStampedModel):
the domain request once approved, so copying them that way we can make changes
after its approved. Most fields here are copied from DomainRequest."""
+ class Meta:
+ """Contains meta information about this class"""
+
+ indexes = [
+ models.Index(fields=["domain"]),
+ models.Index(fields=["domain_request"]),
+ ]
+
+ verbose_name_plural = "Domain information"
+
StateTerritoryChoices = DomainRequest.StateTerritoryChoices
# use the short names in Django admin
@@ -111,7 +121,6 @@ class DomainInformation(TimeStampedModel):
organization_name = models.CharField(
null=True,
blank=True,
- db_index=True,
)
address_line1 = models.CharField(
null=True,
@@ -138,7 +147,6 @@ class DomainInformation(TimeStampedModel):
max_length=10,
null=True,
blank=True,
- db_index=True,
verbose_name="zip code",
)
urbanization = models.CharField(
@@ -350,6 +358,3 @@ class DomainInformation(TimeStampedModel):
def _get_many_to_many_fields():
"""Returns a set of each field.name that has the many to many relation"""
return {field.name for field in DomainInformation._meta.many_to_many} # type: ignore
-
- class Meta:
- verbose_name_plural = "Domain information"
diff --git a/src/registrar/models/domain_invitation.py b/src/registrar/models/domain_invitation.py
index 12082142d..c9cbc8b39 100644
--- a/src/registrar/models/domain_invitation.py
+++ b/src/registrar/models/domain_invitation.py
@@ -15,6 +15,13 @@ logger = logging.getLogger(__name__)
class DomainInvitation(TimeStampedModel):
+ class Meta:
+ """Contains meta information about this class"""
+
+ indexes = [
+ models.Index(fields=["status"]),
+ ]
+
# Constants for status field
class DomainInvitationStatus(models.TextChoices):
INVITED = "invited", "Invited"
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index d7f1b6eba..5ae230496 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -25,6 +25,15 @@ logger = logging.getLogger(__name__)
class DomainRequest(TimeStampedModel):
"""A registrant's domain request for a new domain."""
+ class Meta:
+ """Contains meta information about this class"""
+
+ indexes = [
+ models.Index(fields=["requested_domain"]),
+ models.Index(fields=["approved_domain"]),
+ models.Index(fields=["status"]),
+ ]
+
# https://django-auditlog.readthedocs.io/en/latest/usage.html#object-history
# If we note any performace degradation due to this addition,
# we can query the auditlogs table in admin.py and add the results to
@@ -331,7 +340,6 @@ class DomainRequest(TimeStampedModel):
organization_name = models.CharField(
null=True,
blank=True,
- db_index=True,
)
address_line1 = models.CharField(
@@ -360,7 +368,6 @@ class DomainRequest(TimeStampedModel):
null=True,
blank=True,
verbose_name="zip code",
- db_index=True,
)
urbanization = models.CharField(
null=True,
diff --git a/src/registrar/models/transition_domain.py b/src/registrar/models/transition_domain.py
index 2dafd6da4..0b0cffcec 100644
--- a/src/registrar/models/transition_domain.py
+++ b/src/registrar/models/transition_domain.py
@@ -59,7 +59,6 @@ class TransitionDomain(TimeStampedModel):
null=True,
blank=True,
help_text="Organization name",
- db_index=True,
)
federal_type = models.CharField(
max_length=50,
@@ -85,7 +84,6 @@ class TransitionDomain(TimeStampedModel):
blank=True,
help_text="First name / given name",
verbose_name="first name",
- db_index=True,
)
middle_name = models.CharField(
null=True,
@@ -136,7 +134,6 @@ class TransitionDomain(TimeStampedModel):
blank=True,
verbose_name="zip code",
help_text="Zip code",
- db_index=True,
)
def __str__(self):
diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py
index 005037925..705d2011c 100644
--- a/src/registrar/models/user.py
+++ b/src/registrar/models/user.py
@@ -31,6 +31,17 @@ class User(AbstractUser):
will be updated if any updates are made to it through Login.gov.
"""
+ class Meta:
+ indexes = [
+ models.Index(fields=["username"]),
+ models.Index(fields=["email"]),
+ ]
+
+ permissions = [
+ ("analyst_access_permission", "Analyst Access Permission"),
+ ("full_access_permission", "Full Access Permission"),
+ ]
+
class VerificationTypeChoices(models.TextChoices):
"""
Users achieve access to our system in a few different ways.
@@ -77,7 +88,6 @@ class User(AbstractUser):
null=True,
blank=True,
help_text="Phone",
- db_index=True,
)
middle_name = models.CharField(
@@ -281,9 +291,3 @@ class User(AbstractUser):
"""
self.check_domain_invitations_on_login()
-
- class Meta:
- permissions = [
- ("analyst_access_permission", "Analyst Access Permission"),
- ("full_access_permission", "Full Access Permission"),
- ]
diff --git a/src/registrar/models/verified_by_staff.py b/src/registrar/models/verified_by_staff.py
index c09dce822..1e3e21057 100644
--- a/src/registrar/models/verified_by_staff.py
+++ b/src/registrar/models/verified_by_staff.py
@@ -9,7 +9,6 @@ class VerifiedByStaff(TimeStampedModel):
email = models.EmailField(
null=False,
blank=False,
- db_index=True,
)
requestor = models.ForeignKey(
diff --git a/src/registrar/templates/home.html b/src/registrar/templates/home.html
index 9a5082104..fd54769a8 100644
--- a/src/registrar/templates/home.html
+++ b/src/registrar/templates/home.html
@@ -63,7 +63,7 @@
|