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 = ` + + + Previous + + `; + 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 = ` + ${page} + `; + 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 = ` + + Next + + + `; + 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 = ` - - - Previous - - `; - 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 = ` - ${i} - `; - 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 = ` - - Next - - - `; - 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 = ` - - - Previous - - `; - 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 = ` - ${i} - `; - 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 = ` - - Next - - - `; - 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 @@