Respond to PR feedback

This commit is contained in:
Seamus Johnston 2023-01-06 11:45:29 -06:00
parent 1c45ac55b3
commit 607f181819
No known key found for this signature in database
GPG key ID: 2F21225985069105
4 changed files with 103 additions and 105 deletions

103
src/Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "c1f5d0bb53a9268568ecaad6de6bbc8106cc2bf3a62537611ada4c69222fb9de"
"sha256": "1668475ce39851bd84ff7be330afe9766f6823cf9095980ba3b220ced3a284f4"
},
"pipfile-spec": 6,
"requires": {},
@ -125,35 +125,32 @@
},
"cryptography": {
"hashes": [
"sha256:0e70da4bdff7601b0ef48e6348339e490ebfb0cbe638e083c9c41fb49f00c8bd",
"sha256:10652dd7282de17990b88679cb82f832752c4e8237f0c714be518044269415db",
"sha256:175c1a818b87c9ac80bb7377f5520b7f31b3ef2a0004e2420319beadedb67290",
"sha256:1d7e632804a248103b60b16fb145e8df0bc60eed790ece0d12efe8cd3f3e7744",
"sha256:1f13ddda26a04c06eb57119caf27a524ccae20533729f4b1e4a69b54e07035eb",
"sha256:2ec2a8714dd005949d4019195d72abed84198d877112abb5a27740e217e0ea8d",
"sha256:2fa36a7b2cc0998a3a4d5af26ccb6273f3df133d61da2ba13b3286261e7efb70",
"sha256:2fb481682873035600b5502f0015b664abc26466153fab5c6bc92c1ea69d478b",
"sha256:3178d46f363d4549b9a76264f41c6948752183b3f587666aff0555ac50fd7876",
"sha256:4367da5705922cf7070462e964f66e4ac24162e22ab0a2e9d31f1b270dd78083",
"sha256:4eb85075437f0b1fd8cd66c688469a0c4119e0ba855e3fef86691971b887caf6",
"sha256:50a1494ed0c3f5b4d07650a68cd6ca62efe8b596ce743a5c94403e6f11bf06c1",
"sha256:53049f3379ef05182864d13bb9686657659407148f901f3f1eee57a733fb4b00",
"sha256:6391e59ebe7c62d9902c24a4d8bcbc79a68e7c4ab65863536127c8a9cd94043b",
"sha256:67461b5ebca2e4c2ab991733f8ab637a7265bb582f07c7c88914b5afb88cb95b",
"sha256:78e47e28ddc4ace41dd38c42e6feecfdadf9c3be2af389abbfeef1ff06822285",
"sha256:80ca53981ceeb3241998443c4964a387771588c4e4a5d92735a493af868294f9",
"sha256:8a4b2bdb68a447fadebfd7d24855758fe2d6fecc7fed0b78d190b1af39a8e3b0",
"sha256:8e45653fb97eb2f20b8c96f9cd2b3a0654d742b47d638cf2897afbd97f80fa6d",
"sha256:998cd19189d8a747b226d24c0207fdaa1e6658a1d3f2494541cb9dfbf7dcb6d2",
"sha256:a10498349d4c8eab7357a8f9aa3463791292845b79597ad1b98a543686fb1ec8",
"sha256:b4cad0cea995af760f82820ab4ca54e5471fc782f70a007f31531957f43e9dee",
"sha256:bfe6472507986613dc6cc00b3d492b2f7564b02b3b3682d25ca7f40fa3fd321b",
"sha256:c9e0d79ee4c56d841bd4ac6e7697c8ff3c8d6da67379057f29e66acffcd1e9a7",
"sha256:ca57eb3ddaccd1112c18fc80abe41db443cc2e9dcb1917078e02dfa010a4f353",
"sha256:ce127dd0a6a0811c251a6cddd014d292728484e530d80e872ad9806cfb1c5b3c"
"sha256:1a6915075c6d3a5e1215eab5d99bcec0da26036ff2102a1038401d6ef5bef25b",
"sha256:1ee1fd0de9851ff32dbbb9362a4d833b579b4a6cc96883e8e6d2ff2a6bc7104f",
"sha256:407cec680e811b4fc829de966f88a7c62a596faa250fc1a4b520a0355b9bc190",
"sha256:50386acb40fbabbceeb2986332f0287f50f29ccf1497bae31cf5c3e7b4f4b34f",
"sha256:6f97109336df5c178ee7c9c711b264c502b905c2d2a29ace99ed761533a3460f",
"sha256:754978da4d0457e7ca176f58c57b1f9de6556591c19b25b8bcce3c77d314f5eb",
"sha256:76c24dd4fd196a80f9f2f5405a778a8ca132f16b10af113474005635fe7e066c",
"sha256:7dacfdeee048814563eaaec7c4743c8aea529fe3dd53127313a792f0dadc1773",
"sha256:80ee674c08aaef194bc4627b7f2956e5ba7ef29c3cc3ca488cf15854838a8f72",
"sha256:844ad4d7c3850081dffba91cdd91950038ee4ac525c575509a42d3fc806b83c8",
"sha256:875aea1039d78557c7c6b4db2fe0e9d2413439f4676310a5f269dd342ca7a717",
"sha256:887cbc1ea60786e534b00ba8b04d1095f4272d380ebd5f7a7eb4cc274710fad9",
"sha256:ad04f413436b0781f20c52a661660f1e23bcd89a0e9bb1d6d20822d048cf2856",
"sha256:bae6c7f4a36a25291b619ad064a30a07110a805d08dc89984f4f441f6c1f3f96",
"sha256:c52a1a6f81e738d07f43dab57831c29e57d21c81a942f4602fac7ee21b27f288",
"sha256:e0a05aee6a82d944f9b4edd6a001178787d1546ec7c6223ee9a848a7ade92e39",
"sha256:e324de6972b151f99dc078defe8fb1b0a82c6498e37bff335f5bc6b1e3ab5a1e",
"sha256:e5d71c5d5bd5b5c3eebcf7c5c2bb332d62ec68921a8c593bea8c394911a005ce",
"sha256:f3ed2d864a2fa1666e749fe52fb8e23d8e06b8012e8bd8147c73797c506e86f1",
"sha256:f671c1bb0d6088e94d61d80c606d65baacc0d374e67bf895148883461cd848de",
"sha256:f6c0db08d81ead9576c4d94bbb27aed8d7a430fa27890f39084c2d0e2ec6b0df",
"sha256:f964c7dcf7802d133e8dbd1565914fa0194f9d683d82411989889ecd701e8adf",
"sha256:fec8b932f51ae245121c4671b4bbc030880f363354b2f0e0bd1366017d891458"
],
"markers": "python_version >= '3.6'",
"version": "==38.0.4"
"version": "==39.0.0"
},
"defusedxml": {
"hashes": [
@ -179,19 +176,19 @@
},
"django": {
"hashes": [
"sha256:0b223bfa55511f950ff741983d408d78d772351284c75e9f77d2b830b6b4d148",
"sha256:d38a4e108d2386cb9637da66a82dc8d0733caede4c83c4afdbda78af4214211b"
"sha256:4b214a05fe4c99476e99e2445c8b978c8369c18d4dea8e22ec412862715ad763",
"sha256:ff56ebd7ead0fd5dbe06fe157b0024a7aaea2e0593bb3785fb594cf94dad58ef"
],
"index": "pypi",
"version": "==4.1.4"
"version": "==4.1.5"
},
"django-allow-cidr": {
"hashes": [
"sha256:2fd88ffe697caf0c1d0fd147b88cf44d81282c069bbc475166a2ff1637ad9155",
"sha256:d17347e75d6c02864022f52ed608775a5e9ab144d1a82bb40853714f125f5d87"
"sha256:24b71f70257e97bab9fdb5ad8342c96eeea1d45bc06a36332978574252219401",
"sha256:6709f4581dfd2a00476a134741a738a7f67714ec4f8596c55b22cf3b2ac5a12e"
],
"index": "pypi",
"version": "==0.5.0"
"version": "==0.6.0"
},
"django-auditlog": {
"hashes": [
@ -216,14 +213,6 @@
"index": "pypi",
"version": "==3.7"
},
"django-formtools": {
"hashes": [
"sha256:deb932be55b1d9419e37dc4d65dfbfeb8d307b71c8c11fd52f159aba5fc0deed",
"sha256:f5f32f62ec8192cd1bc55bd929ca7dff5a5f2addf9027db95a5906ecfaa64836"
],
"index": "pypi",
"version": "==2.4"
},
"django-fsm": {
"hashes": [
"sha256:e2c02cbf273fb9691aa9a907c29990afdd21a4adea09c5640344c93fbe03f8d9",
@ -563,11 +552,11 @@
},
"whitenoise": {
"hashes": [
"sha256:8e9c600a5c18bd17655ef668ad55b5edf6c24ce9bdca5bf607649ca4b1e8e2c2",
"sha256:8fa943c6d4cd9e27673b70c21a07b0aa120873901e099cd46cab40f7cc96d567"
"sha256:cf8ecf56d86ba1c734fdb5ef6127312e39e92ad5947fef9033dc9e43ba2777d9",
"sha256:fe0af31504ab08faa1ec7fc02845432096e40cc1b27e6a7747263d7b30fb51fa"
],
"index": "pypi",
"version": "==6.2.0"
"version": "==6.3.0"
}
},
"develop": {
@ -631,11 +620,11 @@
},
"django": {
"hashes": [
"sha256:0b223bfa55511f950ff741983d408d78d772351284c75e9f77d2b830b6b4d148",
"sha256:d38a4e108d2386cb9637da66a82dc8d0733caede4c83c4afdbda78af4214211b"
"sha256:4b214a05fe4c99476e99e2445c8b978c8369c18d4dea8e22ec412862715ad763",
"sha256:ff56ebd7ead0fd5dbe06fe157b0024a7aaea2e0593bb3785fb594cf94dad58ef"
],
"index": "pypi",
"version": "==4.1.4"
"version": "==4.1.5"
},
"django-debug-toolbar": {
"hashes": [
@ -687,11 +676,11 @@
},
"gitpython": {
"hashes": [
"sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f",
"sha256:cc36bfc4a3f913e66805a28e84703e419d9c264c1077e537b54f0e1af85dbefd"
"sha256:769c2d83e13f5d938b7688479da374c4e3d49f71549aaf462b646db9602ea6f8",
"sha256:cd455b0000615c60e286208ba540271af9fe531fa6a87cc590a7298785ab2882"
],
"markers": "python_version >= '3.7'",
"version": "==3.1.29"
"version": "==3.1.30"
},
"mccabe": {
"hashes": [
@ -770,11 +759,11 @@
},
"platformdirs": {
"hashes": [
"sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca",
"sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e"
"sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490",
"sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"
],
"markers": "python_version >= '3.7'",
"version": "==2.6.0"
"version": "==2.6.2"
},
"pycodestyle": {
"hashes": [
@ -910,11 +899,11 @@
},
"types-requests": {
"hashes": [
"sha256:48b7c06e3dffc1b6359e1888084a2b97f41b6b63f208c571ddb02ddbc6a892e4",
"sha256:8c1b1e6a0b19522b4738063e772dcee82cee1c3646536ccc4eb96f655af2b6c6"
"sha256:0ae38633734990d019b80f5463dfa164ebd3581998ac8435f526da6fe4d598c3",
"sha256:b6a2fca8109f4fdba33052f11ed86102bddb2338519e1827387137fefc66a98b"
],
"index": "pypi",
"version": "==2.28.11.6"
"version": "==2.28.11.7"
},
"types-urllib3": {
"hashes": [

View file

@ -14,13 +14,14 @@ from registrar.views.application import Step
from registrar.views.utility import always_404
from api.views import available
application_urls = (
[
APPLICATION_NAMESPACE = views.ApplicationWizard.URL_NAMESPACE
application_urls = [
path("", views.ApplicationWizard.as_view(), name=""),
# dynamically generate the other paths
*[
path(f"{step}/", view.as_view(), name=step)
for step, view in [
path("finished/", views.Finished.as_view(), name="finished"),
]
# dynamically generate the other application_urls
for step, view in [
# add/remove steps here
(Step.ORGANIZATION_TYPE, views.OrganizationType),
(Step.ORGANIZATION_FEDERAL, views.OrganizationFederal),
@ -36,12 +37,9 @@ application_urls = (
(Step.ANYTHING_ELSE, views.AnythingElse),
(Step.REQUIREMENTS, views.Requirements),
(Step.REVIEW, views.Review),
]
],
path("finished/", views.Finished.as_view(), name="finished"),
],
views.ApplicationWizard.URL_NAMESPACE,
)
]:
application_urls.append(path(f"{step}/", view.as_view(), name=step))
urlpatterns = [
path("", views.index, name="home"),
@ -55,7 +53,7 @@ urlpatterns = [
path("health/", views.health),
path("edit_profile/", views.edit_profile, name="edit-profile"),
path("openid/", include("djangooidc.urls")),
path("register/", include(application_urls)),
path("register/", include((application_urls, APPLICATION_NAMESPACE))),
path("api/v1/available/<domain>", available, name="available"),
path(
"todo",

View file

@ -36,7 +36,7 @@ class RegistrarForm(forms.Form):
@classmethod
def from_database(cls, obj: DomainApplication | Contact | None):
"""Initializes this form's fields with values gotten from `obj`."""
"""Returns a dict of form field values gotten from `obj`."""
if obj is None:
return {}
return {
@ -99,7 +99,6 @@ class OrganizationContactForm(RegistrarForm):
class AuthorizingOfficialForm(RegistrarForm):
def to_database(self, obj):
"""Adds this form's cleaned data to `obj` and saves `obj`."""
if not self.is_valid():
return
contact = getattr(obj, "authorizing_official", None)
@ -113,7 +112,6 @@ class AuthorizingOfficialForm(RegistrarForm):
@classmethod
def from_database(cls, obj):
"""Initializes this form's fields with values gotten from `obj`."""
contact = getattr(obj, "authorizing_official", None)
return super().from_database(contact)
@ -130,7 +128,6 @@ class AuthorizingOfficialForm(RegistrarForm):
class CurrentSitesForm(RegistrarForm):
def to_database(self, obj):
"""Adds this form's cleaned data to `obj` and saves `obj`."""
if not self.is_valid():
return
obj.save()
@ -141,7 +138,6 @@ class CurrentSitesForm(RegistrarForm):
@classmethod
def from_database(cls, obj):
"""Initializes this form's fields with values gotten from `obj`."""
current_website = obj.current_websites.first()
if current_website is not None:
return {"current_site": current_website.website}
@ -157,7 +153,6 @@ class CurrentSitesForm(RegistrarForm):
class DotGovDomainForm(RegistrarForm):
def to_database(self, obj):
"""Adds this form's cleaned data to `obj` and saves `obj`."""
if not self.is_valid():
return
normalized = Domain.normalize(
@ -183,7 +178,6 @@ class DotGovDomainForm(RegistrarForm):
@classmethod
def from_database(cls, obj):
"""Initializes this form's fields with values gotten from `obj`."""
values = {}
requested_domain = getattr(obj, "requested_domain", None)
if requested_domain is not None:
@ -209,7 +203,6 @@ class PurposeForm(RegistrarForm):
class YourContactForm(RegistrarForm):
def to_database(self, obj):
"""Adds this form's cleaned data to `obj` and saves `obj`."""
if not self.is_valid():
return
contact = getattr(obj, "submitter", None)
@ -223,7 +216,6 @@ class YourContactForm(RegistrarForm):
@classmethod
def from_database(cls, obj):
"""Initializes this form's fields with values gotten from `obj`."""
contact = getattr(obj, "submitter", None)
return super().from_database(contact)
@ -240,7 +232,6 @@ class YourContactForm(RegistrarForm):
class OtherContactsForm(RegistrarForm):
def to_database(self, obj):
"""Adds this form's cleaned data to `obj` and saves `obj`."""
if not self.is_valid():
return
obj.save()
@ -256,7 +247,6 @@ class OtherContactsForm(RegistrarForm):
@classmethod
def from_database(cls, obj):
"""Initializes this form's fields with values gotten from `obj`."""
other_contacts = obj.other_contacts.first()
return super().from_database(other_contacts)

View file

@ -16,7 +16,12 @@ logger = logging.getLogger(__name__)
class Step(StrEnum):
"""Names for each page of the application wizard."""
"""
Names for each page of the application wizard.
As with Django's own `TextChoices` class, steps will
appear in the order they are defined. (Order matters.)
"""
ORGANIZATION_TYPE = "organization_type"
ORGANIZATION_FEDERAL = "organization_federal"
@ -95,6 +100,10 @@ class ApplicationWizard(LoginRequiredMixin, TemplateView):
self.steps = StepsHelper(self)
self._application = None # for caching
def has_pk(self):
"""Does this wizard know about a DomainApplication database record?"""
return "application_id" in self.storage
@property
def prefix(self):
"""Namespace the wizard to avoid clashes in session variable names."""
@ -134,9 +143,7 @@ class ApplicationWizard(LoginRequiredMixin, TemplateView):
# marking session as modified on every access
# so that updates to nested keys are always saved
self.request.session.modified = True
if self.prefix not in self.request.session:
self.request.session[self.prefix] = {}
return self.request.session[self.prefix]
return self.request.session.setdefault(self.prefix, {})
@storage.setter
def storage(self, value):
@ -187,6 +194,12 @@ class ApplicationWizard(LoginRequiredMixin, TemplateView):
self.storage["application_id"] = kwargs["id"]
# if accessing this class directly, redirect to the first step
# in other words, if `ApplicationWizard` is called as view
# directly by some redirect or url handler, we'll send users
# to the first step in the processes; subclasses will NOT
# be redirected. The purpose of this is to allow code to
# send users "to the application wizard" without needing to
# know which view is first in the list of steps.
if self.__class__ == ApplicationWizard:
return self.goto(self.steps.first)
@ -273,10 +286,6 @@ class ApplicationWizard(LoginRequiredMixin, TemplateView):
else:
raise Http404()
def has_pk(self):
"""Does this wizard know about a DomainApplication database record?"""
return "application_id" in self.storage
def is_valid(self, forms: list = None) -> bool:
"""Returns True if all forms in the wizard are valid."""
forms = forms if forms is not None else self.get_all_forms()
@ -401,6 +410,18 @@ class Review(ApplicationWizard):
def goto_next_step(self):
return self.done()
# TODO: validate before saving, show errors
# Extra info:
#
# Formtools used saved POST data to revalidate each form as
# the user had entered it. This implementation (in this file) discards
# that data and tries to instantiate the forms from the database
# in order to perform validation.
#
# This must be possible in Django (after all, that is how ModelForms work),
# but is presently not working: the form claims it is invalid,
# even when careful checking via breakpoint() shows that the form
# object contains valid data.
#
# forms = self.get_all_forms()
# if self.is_valid(forms):
# return self.done()