mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-22 04:19:26 +02:00
Refactor model layout
This commit is contained in:
parent
349659be90
commit
8b8ade428b
11 changed files with 522 additions and 462 deletions
66
src/Pipfile.lock
generated
66
src/Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "f3c73d2389ee9b1648528a855174d19d20b67f64a2337a660ebeaf613db31488"
|
||||
"sha256": "4e755e3f5778ff572fba5755b966cde05d30a84c4eddb1d63ca5fe1034565283"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
|
@ -126,7 +126,7 @@
|
|||
"sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845",
|
||||
"sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"markers": "python_full_version >= '3.6.0'",
|
||||
"version": "==2.1.1"
|
||||
},
|
||||
"cryptography": {
|
||||
|
@ -199,6 +199,14 @@
|
|||
"index": "pypi",
|
||||
"version": "==0.5.0"
|
||||
},
|
||||
"django-auditlog": {
|
||||
"hashes": [
|
||||
"sha256:0ab57a536e02341e27c3d0431ad0e124e674507bd965a0756e29b01cb67c38ce",
|
||||
"sha256:2f83389f98db4b1a9c2961f17cd9ac4a3ea94304655071f30da45d8debf59688"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.2.0"
|
||||
},
|
||||
"django-cache-url": {
|
||||
"hashes": [
|
||||
"sha256:6cc9901a99a99751f5458aa7de08ce06e48c1441b1a94c9457d78af74fab9a26",
|
||||
|
@ -378,6 +386,7 @@
|
|||
"sha256:212757ffcecb3e1a5338d4e6761bf9c04f750e7d027117e74aa3cd8a75bb6fbd",
|
||||
"sha256:215d6bf7e66732a514f47614f828d8c0aaac9a648c46a831955cb103473c7147",
|
||||
"sha256:25382c7d174c679ce6927c16b6fbb68b10e56ee44b1acb40671e02d29f2fce7c",
|
||||
"sha256:2abccab84d057723d2ca8f99ff7b619285d40da6814d50366f61f0fc385c3903",
|
||||
"sha256:2d964eb24c8b021623df1c93c626671420c6efadbdb8655cb2bd5e0c6fa422ba",
|
||||
"sha256:2ec46ed947801652c9643e0b1dc334cfb2781232e375ba97312c2fc256597632",
|
||||
"sha256:2ef892cabdccefe577088a79580301f09f2a713eb239f4f9f62b2b29cafb0577",
|
||||
|
@ -423,6 +432,7 @@
|
|||
"sha256:b9c33d4aef08dfecbd1736ceab8b7b3c4358bf10a0121483e5cd60d3d308cc64",
|
||||
"sha256:b9d38a4656e4e715d637abdf7296e98d6267df0cc0a8e9a016f8ba07e4aa3eeb",
|
||||
"sha256:bcda1c84a1c533c528356da5490d464a139b6e84eb77cc0b432e38c5c6dd7882",
|
||||
"sha256:bef7e3f9dc6f0c13afdd671008534be5744e0e682fb851584c8c3a025ec09720",
|
||||
"sha256:c15ba5982c177bc4b23a7940c7e4394197e2d6a424a2d282e7c236b66da6d896",
|
||||
"sha256:c5254cbd4f4855e11cebf678c1a848a3042d455a22a4ce61349c36aafd4c2267",
|
||||
"sha256:c5682a45df7d9642eff590abc73157c887a68f016df0a8ad722dcc0f888f56d7",
|
||||
|
@ -497,6 +507,14 @@
|
|||
"markers": "python_full_version >= '3.6.8'",
|
||||
"version": "==3.0.9"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
|
||||
"sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.8.2"
|
||||
},
|
||||
"python-dotenv": {
|
||||
"hashes": [
|
||||
"sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5",
|
||||
|
@ -683,7 +701,7 @@
|
|||
"sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd",
|
||||
"sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.0'",
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==4.0.9"
|
||||
},
|
||||
"gitpython": {
|
||||
|
@ -699,7 +717,7 @@
|
|||
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
|
||||
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.0'",
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.7.0"
|
||||
},
|
||||
"mypy": {
|
||||
|
@ -782,7 +800,7 @@
|
|||
"sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785",
|
||||
"sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.0'",
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.9.1"
|
||||
},
|
||||
"pyflakes": {
|
||||
|
@ -790,7 +808,7 @@
|
|||
"sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2",
|
||||
"sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.0'",
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.5.0"
|
||||
},
|
||||
"pyyaml": {
|
||||
|
@ -836,7 +854,7 @@
|
|||
"sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
|
||||
"sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.0'",
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==6.0"
|
||||
},
|
||||
"six": {
|
||||
|
@ -852,7 +870,7 @@
|
|||
"sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94",
|
||||
"sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.0'",
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==5.0.0"
|
||||
},
|
||||
"soupsieve": {
|
||||
|
@ -860,7 +878,7 @@
|
|||
"sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759",
|
||||
"sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.0'",
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.3.2.post1"
|
||||
},
|
||||
"sqlparse": {
|
||||
|
@ -873,18 +891,18 @@
|
|||
},
|
||||
"stevedore": {
|
||||
"hashes": [
|
||||
"sha256:02518a8f0d6d29be8a445b7f2ac63753ff29e8f2a2faa01777568d5500d777a6",
|
||||
"sha256:3b1cbd592a87315f000d05164941ee5e164899f8fc0ce9a00bb0f321f40ef93e"
|
||||
"sha256:7f8aeb6e3f90f96832c301bff21a7eb5eefbe894c88c506483d355565d88cc1a",
|
||||
"sha256:aa6436565c069b2946fe4ebff07f5041e0c8bf18c7376dd29edf80cf7d524e4e"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==4.1.0"
|
||||
"version": "==4.1.1"
|
||||
},
|
||||
"tomli": {
|
||||
"hashes": [
|
||||
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
|
||||
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
|
||||
],
|
||||
"markers": "python_version < '3.11'",
|
||||
"markers": "python_full_version < '3.11.0a7'",
|
||||
"version": "==2.0.1"
|
||||
},
|
||||
"types-cachetools": {
|
||||
|
@ -904,25 +922,25 @@
|
|||
},
|
||||
"types-pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:70ccaafcf3fb404d57bffc1529fdd86a13e8b4f2cf9fc3ee81a6408ce0ad59d2",
|
||||
"sha256:aaf5e51444c13bd34104695a89ad9c48412599a4f615d65a60e649109714f608"
|
||||
"sha256:1e94e80aafee07a7e798addb2a320e32956a373f376655128ae20637adb2655b",
|
||||
"sha256:6840819871c92deebe6a2067fb800c11b8a063632eb4e3e755914e7ab3604e83"
|
||||
],
|
||||
"version": "==6.0.12.1"
|
||||
"version": "==6.0.12.2"
|
||||
},
|
||||
"types-requests": {
|
||||
"hashes": [
|
||||
"sha256:14941f8023a80b16441b3b46caffcbfce5265fd14555844d6029697824b5a2ef",
|
||||
"sha256:fdcd7bd148139fb8eef72cf4a41ac7273872cad9e6ada14b11ff5dfdeee60ed3"
|
||||
"sha256:bdb1f9811e53d0642c8347b09137363eb25e1a516819e190da187c29595a1df3",
|
||||
"sha256:d4f342b0df432262e9e326d17638eeae96a5881e78e7a6aae46d33870d73952e"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.28.11.2"
|
||||
"version": "==2.28.11.4"
|
||||
},
|
||||
"types-urllib3": {
|
||||
"hashes": [
|
||||
"sha256:a948584944b2412c9a74b9cf64f6c48caf8652cb88b38361316f6d15d8a184cd",
|
||||
"sha256:f6422596cc9ee5fdf68f9d547f541096a20c2dcfd587e37c804c9ea720bf5cb2"
|
||||
"sha256:1807b87b8ee1ae0226813ba2c52330eff20fb2bf6359b1de24df08eb3090e442",
|
||||
"sha256:a188c24fc61a99658c8c324c8dd7419f5b91a0d89df004e5f576869122c1db55"
|
||||
],
|
||||
"version": "==1.26.25.1"
|
||||
"version": "==1.26.25.3"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
|
@ -937,7 +955,7 @@
|
|||
"sha256:7500c9625927c8ec60f54377d590f67b30c8e70ef4b8894214ac6e4cad233d2a",
|
||||
"sha256:780a4082c5fbc0fde6a2fcfe5e26e6efc1e8f425730863c04085769781f51eba"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"markers": "python_full_version >= '3.7.0'",
|
||||
"version": "==2.1.2"
|
||||
},
|
||||
"webob": {
|
||||
|
@ -953,7 +971,7 @@
|
|||
"sha256:2a001a9efa40d2a7e5d9cd8d1527c75f41814eb6afce2c3d207402547b1e5ead",
|
||||
"sha256:54bd969725838d9861a9fa27f8d971f79d275d94ae255f5c501f53bb6d9929eb"
|
||||
],
|
||||
"markers": "python_version < '4' and python_full_version >= '3.6.0'",
|
||||
"markers": "python_version >= '3.6' and python_version < '4'",
|
||||
"version": "==3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 4.1.1 on 2022-09-26 15:26
|
||||
# Generated by Django 4.1.3 on 2022-11-10 14:23
|
||||
|
||||
from django.conf import settings
|
||||
import django.contrib.auth.models
|
||||
|
@ -6,6 +6,7 @@ import django.contrib.auth.validators
|
|||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import django_fsm # type: ignore
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -132,7 +133,65 @@ class Migration(migrations.Migration):
|
|||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="UserProfile",
|
||||
name="Contact",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"first_name",
|
||||
models.TextField(
|
||||
blank=True, db_index=True, help_text="First name", null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"middle_name",
|
||||
models.TextField(blank=True, help_text="Middle name", null=True),
|
||||
),
|
||||
(
|
||||
"last_name",
|
||||
models.TextField(
|
||||
blank=True, db_index=True, help_text="Last name", null=True
|
||||
),
|
||||
),
|
||||
("title", models.TextField(blank=True, help_text="Title", null=True)),
|
||||
(
|
||||
"email",
|
||||
models.TextField(
|
||||
blank=True, db_index=True, help_text="Email", null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"phone",
|
||||
models.TextField(
|
||||
blank=True, db_index=True, help_text="Phone", null=True
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Website",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("website", models.CharField(max_length=255)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="DomainApplication",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
|
@ -145,6 +204,228 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"status",
|
||||
django_fsm.FSMField(
|
||||
choices=[
|
||||
("started", "started"),
|
||||
("submitted", "submitted"),
|
||||
("investigating", "investigating"),
|
||||
("approved", "approved"),
|
||||
],
|
||||
default="started",
|
||||
max_length=50,
|
||||
),
|
||||
),
|
||||
(
|
||||
"organization_type",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("federal", "a federal agency"),
|
||||
("interstate", "an organization of two or more states"),
|
||||
(
|
||||
"state_or_territory",
|
||||
"one of the 50 U.S. states, the District of Columbia, American Samoa, Guam, Northern Mariana Islands, Puerto Rico, or the U.S. Virgin Islands",
|
||||
),
|
||||
(
|
||||
"tribal",
|
||||
"a tribal government recognized by the federal or state government",
|
||||
),
|
||||
("county", "a county, parish, or borough"),
|
||||
("city", "a city, town, township, village, etc."),
|
||||
(
|
||||
"special_district",
|
||||
"an independent organization within a single state",
|
||||
),
|
||||
],
|
||||
help_text="Type of Organization",
|
||||
max_length=255,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"federal_branch",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("Executive", "Executive"),
|
||||
("Judicial", "Judicial"),
|
||||
("Legislative", "Legislative"),
|
||||
],
|
||||
help_text="Branch of federal government",
|
||||
max_length=50,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_election_office",
|
||||
models.BooleanField(
|
||||
blank=True,
|
||||
help_text="Is your ogranization an election office?",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"organization_name",
|
||||
models.TextField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="Organization name",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"street_address",
|
||||
models.TextField(blank=True, help_text="Street Address", null=True),
|
||||
),
|
||||
(
|
||||
"unit_type",
|
||||
models.CharField(
|
||||
blank=True, help_text="Unit type", max_length=15, null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"unit_number",
|
||||
models.CharField(
|
||||
blank=True, help_text="Unit number", max_length=255, null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"state_territory",
|
||||
models.CharField(
|
||||
blank=True, help_text="State/Territory", max_length=2, null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"zip_code",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="ZIP code",
|
||||
max_length=10,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"purpose",
|
||||
models.TextField(
|
||||
blank=True, help_text="Purpose of the domain", null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"security_email",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
help_text="Security email for public use",
|
||||
max_length=320,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"anything_else",
|
||||
models.TextField(
|
||||
blank=True, help_text="Anything else we should know?", null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"acknowledged_policy",
|
||||
models.BooleanField(
|
||||
blank=True,
|
||||
help_text="Acknowledged .gov acceptable use policy",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"alternative_domains",
|
||||
models.ManyToManyField(
|
||||
blank=True, related_name="alternatives+", to="registrar.website"
|
||||
),
|
||||
),
|
||||
(
|
||||
"authorizing_official",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="authorizing_official",
|
||||
to="registrar.contact",
|
||||
),
|
||||
),
|
||||
(
|
||||
"creator",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="applications_created",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"current_websites",
|
||||
models.ManyToManyField(
|
||||
blank=True, related_name="current+", to="registrar.website"
|
||||
),
|
||||
),
|
||||
(
|
||||
"investigator",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="applications_investigating",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"other_contacts",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name="contact_applications",
|
||||
to="registrar.contact",
|
||||
),
|
||||
),
|
||||
(
|
||||
"requested_domain",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
help_text="The requested domain",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="requested+",
|
||||
to="registrar.website",
|
||||
),
|
||||
),
|
||||
(
|
||||
"submitter",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="submitted_applications",
|
||||
to="registrar.contact",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="UserProfile",
|
||||
fields=[
|
||||
(
|
||||
"contact_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="registrar.contact",
|
||||
),
|
||||
),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("street1", models.TextField(blank=True)),
|
||||
("street2", models.TextField(blank=True)),
|
||||
("street3", models.TextField(blank=True)),
|
||||
|
@ -152,13 +433,11 @@ class Migration(migrations.Migration):
|
|||
("sp", models.TextField(blank=True)),
|
||||
("pc", models.TextField(blank=True)),
|
||||
("cc", models.TextField(blank=True)),
|
||||
("voice", models.TextField(blank=True)),
|
||||
("fax", models.TextField(blank=True)),
|
||||
("email", models.TextField(blank=True)),
|
||||
("display_name", models.TextField()),
|
||||
(
|
||||
"user",
|
||||
models.OneToOneField(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
|
@ -168,5 +447,6 @@ class Migration(migrations.Migration):
|
|||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
bases=("registrar.contact", models.Model),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,258 +0,0 @@
|
|||
# Generated by Django 4.1.3 on 2022-11-08 20:17
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django_fsm # type: ignore
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("registrar", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Contact",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"first_name",
|
||||
models.TextField(db_index=True, help_text="First name", null=True),
|
||||
),
|
||||
("middle_name", models.TextField(help_text="Middle name", null=True)),
|
||||
(
|
||||
"last_name",
|
||||
models.TextField(db_index=True, help_text="Last name", null=True),
|
||||
),
|
||||
("title", models.TextField(help_text="Title", null=True)),
|
||||
(
|
||||
"email",
|
||||
models.TextField(db_index=True, help_text="Email", null=True),
|
||||
),
|
||||
(
|
||||
"phone",
|
||||
models.TextField(db_index=True, help_text="Phone", null=True),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Website",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("website", models.CharField(max_length=255)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="DomainApplication",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"status",
|
||||
django_fsm.FSMField(
|
||||
choices=[
|
||||
("started", "started"),
|
||||
("submitted", "submitted"),
|
||||
("investigating", "investigating"),
|
||||
("approved", "approved"),
|
||||
],
|
||||
default="started",
|
||||
max_length=50,
|
||||
),
|
||||
),
|
||||
(
|
||||
"organization_type",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("federal", "a federal agency"),
|
||||
("interstate", "an organization of two or more states"),
|
||||
(
|
||||
"state_or_territory",
|
||||
"one of the 50 U.S. states, the District of Columbia, American Samoa, Guam, Northern Mariana Islands, Puerto Rico, or the U.S. Virgin Islands",
|
||||
),
|
||||
(
|
||||
"tribal",
|
||||
"a tribal government recognized by the federal or state government",
|
||||
),
|
||||
("county", "a county, parish, or borough"),
|
||||
("city", "a city, town, township, village, etc."),
|
||||
(
|
||||
"special_district",
|
||||
"an independent organization within a single state",
|
||||
),
|
||||
],
|
||||
help_text="Type of Organization",
|
||||
max_length=255,
|
||||
),
|
||||
),
|
||||
(
|
||||
"federal_branch",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("Executive", "Executive"),
|
||||
("Judicial", "Judicial"),
|
||||
("Legislative", "Legislative"),
|
||||
],
|
||||
help_text="Branch of federal government",
|
||||
max_length=50,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_election_office",
|
||||
models.BooleanField(
|
||||
help_text="Is your ogranization an election office?", null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"organization_name",
|
||||
models.TextField(
|
||||
db_index=True, help_text="Organization name", null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"street_address",
|
||||
models.TextField(help_text="Street Address", null=True),
|
||||
),
|
||||
(
|
||||
"unit_type",
|
||||
models.CharField(help_text="Unit type", max_length=15, null=True),
|
||||
),
|
||||
(
|
||||
"unit_number",
|
||||
models.CharField(
|
||||
help_text="Unit number", max_length=255, null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"state_territory",
|
||||
models.CharField(
|
||||
help_text="State/Territory", max_length=2, null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"zip_code",
|
||||
models.CharField(
|
||||
db_index=True, help_text="ZIP code", max_length=10, null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"purpose",
|
||||
models.TextField(help_text="Purpose of the domain", null=True),
|
||||
),
|
||||
(
|
||||
"security_email",
|
||||
models.CharField(
|
||||
help_text="Security email for public use",
|
||||
max_length=320,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"anything_else",
|
||||
models.TextField(
|
||||
help_text="Anything else we should know?", null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"acknowledged_policy",
|
||||
models.BooleanField(
|
||||
help_text="Acknowledged .gov acceptable use policy", null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"alternative_domains",
|
||||
models.ManyToManyField(
|
||||
related_name="alternatives+", to="registrar.website"
|
||||
),
|
||||
),
|
||||
(
|
||||
"authorizing_official",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="authorizing_official",
|
||||
to="registrar.contact",
|
||||
),
|
||||
),
|
||||
(
|
||||
"creator",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="applications_created",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"current_websites",
|
||||
models.ManyToManyField(
|
||||
related_name="current+", to="registrar.website"
|
||||
),
|
||||
),
|
||||
(
|
||||
"investigator",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="applications_investigating",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"other_contacts",
|
||||
models.ManyToManyField(
|
||||
related_name="contact_applications", to="registrar.contact"
|
||||
),
|
||||
),
|
||||
(
|
||||
"requested_domain",
|
||||
models.ForeignKey(
|
||||
help_text="The requested domain",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="requested+",
|
||||
to="registrar.website",
|
||||
),
|
||||
),
|
||||
(
|
||||
"submitter",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="submitted_applications",
|
||||
to="registrar.contact",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -1,6 +1,10 @@
|
|||
from auditlog.registry import auditlog # type: ignore
|
||||
|
||||
from .models import User, UserProfile, Contact, Website, DomainApplication
|
||||
from .contact import Contact
|
||||
from .domain_application import DomainApplication
|
||||
from .user_profile import UserProfile
|
||||
from .user import User
|
||||
from .website import Website
|
||||
|
||||
__all__ = [
|
||||
"Contact",
|
||||
|
|
51
src/registrar/models/contact.py
Normal file
51
src/registrar/models/contact.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
from django.db import models
|
||||
|
||||
|
||||
class Contact(models.Model):
|
||||
|
||||
"""Contact information follows a similar pattern for each contact."""
|
||||
|
||||
first_name = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="First name",
|
||||
db_index=True,
|
||||
)
|
||||
middle_name = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Middle name",
|
||||
)
|
||||
last_name = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Last name",
|
||||
db_index=True,
|
||||
)
|
||||
title = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Title",
|
||||
)
|
||||
email = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Email",
|
||||
db_index=True,
|
||||
)
|
||||
phone = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Phone",
|
||||
db_index=True,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
if self.first_name or self.last_name:
|
||||
return f"{self.title or ''} {self.first_name or ''} {self.last_name or ''}"
|
||||
elif self.email:
|
||||
return self.email
|
||||
elif self.pk:
|
||||
return str(self.pk)
|
||||
else:
|
||||
return ""
|
|
@ -1,174 +1,10 @@
|
|||
import re
|
||||
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models
|
||||
|
||||
from django_fsm import FSMField, transition # type: ignore
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
"""
|
||||
A custom user model that performs identically to the default user model
|
||||
but can be customized later.
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
# this info is pulled from Login.gov
|
||||
if self.first_name or self.last_name:
|
||||
return f"{self.first_name or ''} {self.last_name or ''}"
|
||||
elif self.email:
|
||||
return self.email
|
||||
else:
|
||||
return self.username
|
||||
|
||||
|
||||
class TimeStampedModel(models.Model):
|
||||
"""
|
||||
An abstract base model that provides self-updating
|
||||
`created_at` and `updated_at` fields.
|
||||
"""
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
# don't put anything else here, it will be ignored
|
||||
|
||||
|
||||
class AddressModel(models.Model):
|
||||
"""
|
||||
An abstract base model that provides common fields
|
||||
for postal addresses.
|
||||
"""
|
||||
|
||||
# contact's street (null ok)
|
||||
street1 = models.TextField(blank=True)
|
||||
# contact's street (null ok)
|
||||
street2 = models.TextField(blank=True)
|
||||
# contact's street (null ok)
|
||||
street3 = models.TextField(blank=True)
|
||||
# contact's city
|
||||
city = models.TextField(blank=True)
|
||||
# contact's state or province (null ok)
|
||||
sp = models.TextField(blank=True)
|
||||
# contact's postal code (null ok)
|
||||
pc = models.TextField(blank=True)
|
||||
# contact's country code
|
||||
cc = models.TextField(blank=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
# don't put anything else here, it will be ignored
|
||||
|
||||
|
||||
class Website(models.Model):
|
||||
|
||||
"""Keep domain names in their own table so that applications can refer to
|
||||
many of them."""
|
||||
|
||||
# domain names have strictly limited lengths, 255 characters is more than
|
||||
# enough.
|
||||
website = models.CharField(
|
||||
max_length=255,
|
||||
null=False,
|
||||
help_text="",
|
||||
)
|
||||
|
||||
# a domain name is alphanumeric or hyphen, up to 63 characters, doesn't
|
||||
# begin or end with a hyphen, followed by a TLD of 2-6 alphabetic characters
|
||||
DOMAIN_REGEX = re.compile(r"^(?!-)[A-Za-z0-9-]{1,63}(?<!-)\.[A-Za-z]{2,6}")
|
||||
|
||||
@classmethod
|
||||
def string_could_be_domain(cls, domain: str) -> bool:
|
||||
"""Return True if the string could be a domain name, otherwise False.
|
||||
|
||||
TODO: when we have a Domain class, this could be a classmethod there.
|
||||
"""
|
||||
if cls.DOMAIN_REGEX.match(domain):
|
||||
return True
|
||||
return False
|
||||
|
||||
def could_be_domain(self) -> bool:
|
||||
"""Could this instance be a domain?"""
|
||||
# short-circuit if self.website is null/None
|
||||
if not self.website:
|
||||
return False
|
||||
return self.string_could_be_domain(str(self.website))
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.website)
|
||||
|
||||
|
||||
class Contact(models.Model):
|
||||
|
||||
"""Contact information follows a similar pattern for each contact."""
|
||||
|
||||
first_name = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="First name",
|
||||
db_index=True,
|
||||
)
|
||||
middle_name = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Middle name",
|
||||
)
|
||||
last_name = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Last name",
|
||||
db_index=True,
|
||||
)
|
||||
title = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Title",
|
||||
)
|
||||
email = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Email",
|
||||
db_index=True,
|
||||
)
|
||||
phone = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Phone",
|
||||
db_index=True,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
if self.first_name or self.last_name:
|
||||
return f"{self.title or ''} {self.first_name or ''} {self.last_name or ''}"
|
||||
elif self.email:
|
||||
return self.email
|
||||
elif self.pk:
|
||||
return str(self.pk)
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
class UserProfile(TimeStampedModel, Contact, AddressModel):
|
||||
"""User information, unrelated to their login/auth details."""
|
||||
|
||||
user = models.OneToOneField(
|
||||
User,
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
display_name = models.TextField()
|
||||
|
||||
def __str__(self):
|
||||
# use info stored in User rather than Contact,
|
||||
# because Contact is user-editable while User
|
||||
# pulls from identity-verified Login.gov
|
||||
if self.user:
|
||||
return str(self.user)
|
||||
else:
|
||||
return "Orphaned account"
|
||||
from .utility.time_stamped_model import TimeStampedModel
|
||||
from .contact import Contact
|
||||
from .user import User
|
||||
from .website import Website
|
||||
|
||||
|
||||
class DomainApplication(TimeStampedModel):
|
||||
|
@ -367,13 +203,13 @@ class DomainApplication(TimeStampedModel):
|
|||
)
|
||||
|
||||
def __str__(self):
|
||||
if self.requested_domain and self.requested_domain.website:
|
||||
return self.requested_domain.website
|
||||
else:
|
||||
try:
|
||||
try:
|
||||
if self.requested_domain and self.requested_domain.website:
|
||||
return self.requested_domain.website
|
||||
else:
|
||||
return f"{self.status} application created by {self.creator}"
|
||||
except Exception:
|
||||
return ""
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
@transition(field="status", source=STARTED, target=SUBMITTED)
|
||||
def submit(self):
|
17
src/registrar/models/user.py
Normal file
17
src/registrar/models/user.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from django.contrib.auth.models import AbstractUser
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
"""
|
||||
A custom user model that performs identically to the default user model
|
||||
but can be customized later.
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
# this info is pulled from Login.gov
|
||||
if self.first_name or self.last_name:
|
||||
return f"{self.first_name or ''} {self.last_name or ''}"
|
||||
elif self.email:
|
||||
return self.email
|
||||
else:
|
||||
return self.username
|
29
src/registrar/models/user_profile.py
Normal file
29
src/registrar/models/user_profile.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
from django.db import models
|
||||
|
||||
from .utility.time_stamped_model import TimeStampedModel
|
||||
from .utility.address_model import AddressModel
|
||||
|
||||
from .contact import Contact
|
||||
from .user import User
|
||||
|
||||
|
||||
class UserProfile(TimeStampedModel, Contact, AddressModel):
|
||||
|
||||
"""User information, unrelated to their login/auth details."""
|
||||
|
||||
user = models.OneToOneField(
|
||||
User,
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
display_name = models.TextField()
|
||||
|
||||
def __str__(self):
|
||||
# use info stored in User rather than Contact,
|
||||
# because Contact is user-editable while User
|
||||
# pulls from identity-verified Login.gov
|
||||
try:
|
||||
return str(self.user)
|
||||
except Exception:
|
||||
return "Orphaned account"
|
27
src/registrar/models/utility/address_model.py
Normal file
27
src/registrar/models/utility/address_model.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
from django.db import models
|
||||
|
||||
|
||||
class AddressModel(models.Model):
|
||||
"""
|
||||
An abstract base model that provides common fields
|
||||
for postal addresses.
|
||||
"""
|
||||
|
||||
# contact's street (null ok)
|
||||
street1 = models.TextField(blank=True)
|
||||
# contact's street (null ok)
|
||||
street2 = models.TextField(blank=True)
|
||||
# contact's street (null ok)
|
||||
street3 = models.TextField(blank=True)
|
||||
# contact's city
|
||||
city = models.TextField(blank=True)
|
||||
# contact's state or province (null ok)
|
||||
sp = models.TextField(blank=True)
|
||||
# contact's postal code (null ok)
|
||||
pc = models.TextField(blank=True)
|
||||
# contact's country code
|
||||
cc = models.TextField(blank=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
# don't put anything else here, it will be ignored
|
15
src/registrar/models/utility/time_stamped_model.py
Normal file
15
src/registrar/models/utility/time_stamped_model.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
from django.db import models
|
||||
|
||||
|
||||
class TimeStampedModel(models.Model):
|
||||
"""
|
||||
An abstract base model that provides self-updating
|
||||
`created_at` and `updated_at` fields.
|
||||
"""
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
# don't put anything else here, it will be ignored
|
41
src/registrar/models/website.py
Normal file
41
src/registrar/models/website.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
import re
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Website(models.Model):
|
||||
|
||||
"""Keep domain names in their own table so that applications can refer to
|
||||
many of them."""
|
||||
|
||||
# domain names have strictly limited lengths, 255 characters is more than
|
||||
# enough.
|
||||
website = models.CharField(
|
||||
max_length=255,
|
||||
null=False,
|
||||
help_text="",
|
||||
)
|
||||
|
||||
# a domain name is alphanumeric or hyphen, up to 63 characters, doesn't
|
||||
# begin or end with a hyphen, followed by a TLD of 2-6 alphabetic characters
|
||||
DOMAIN_REGEX = re.compile(r"^(?!-)[A-Za-z0-9-]{1,63}(?<!-)\.[A-Za-z]{2,6}")
|
||||
|
||||
@classmethod
|
||||
def string_could_be_domain(cls, domain: str) -> bool:
|
||||
"""Return True if the string could be a domain name, otherwise False.
|
||||
|
||||
TODO: when we have a Domain class, this could be a classmethod there.
|
||||
"""
|
||||
if cls.DOMAIN_REGEX.match(domain):
|
||||
return True
|
||||
return False
|
||||
|
||||
def could_be_domain(self) -> bool:
|
||||
"""Could this instance be a domain?"""
|
||||
# short-circuit if self.website is null/None
|
||||
if not self.website:
|
||||
return False
|
||||
return self.string_could_be_domain(str(self.website))
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.website)
|
Loading…
Add table
Add a link
Reference in a new issue