Alternate names (maiden/married), self-person link, deletion integrity
Names (the genealogy standard: maiden name primary, married/alias as typed
alternates):
- Name model already supported multiple typed names; expose full CRUD —
NameCreate/Read/Update schemas, name_service (one-primary invariant,
promote-on-delete), nested /persons/{id}/names routes.
- Person page gains a Names card: add/edit/delete + "make primary", with a
curated name_type dropdown (birth/maiden, married, alias, nickname, …).
Self-person ("who am I"):
- users.self_person_id FK (use_alter for the users<->persons<->trees cycle)
+ migration; PATCH /users/me/self-person; "This is me" / "This is you"
on the person page. Soft-deleting the linked person clears it.
Deletion integrity (fixes the broken tree view):
- delete_person now soft-deletes the relationships touching the person, so no
dangling edges remain; family-chart also filters links to missing people.
- Optional cascade=true recursively deletes descendants (GEDCOM cleanup);
the person page asks "only this person" vs "with all descendants".
- DELETE returns {deleted: n}.
Family view surfaces "Not connected to anyone" so dangling people aren't lost.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+602
-2
@@ -280,6 +280,48 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/users/me/self-person": {
|
||||
"patch": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Set Self Person",
|
||||
"description": "Link (or unlink) the Person record that represents this account.",
|
||||
"operationId": "set_self_person_api_v1_users_me_self_person_patch",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UserSelfPersonUpdate"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UserRead"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/trees": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -728,6 +770,7 @@
|
||||
"persons"
|
||||
],
|
||||
"summary": "Delete Person",
|
||||
"description": "Delete a person. ``cascade=true`` also deletes all descendants. Returns\nthe number of persons deleted (1 unless cascading).",
|
||||
"operationId": "delete_person_api_v1_trees__tree_id__persons__person_id__delete",
|
||||
"parameters": [
|
||||
{
|
||||
@@ -749,11 +792,32 @@
|
||||
"format": "uuid",
|
||||
"title": "Person Id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "cascade",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"title": "Cascade"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Successful Response"
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": "Response Delete Person Api V1 Trees Tree Id Persons Person Id Delete"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
@@ -872,6 +936,251 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/trees/{tree_id}/persons/{person_id}/names": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"names"
|
||||
],
|
||||
"summary": "List Names",
|
||||
"operationId": "list_names_api_v1_trees__tree_id__persons__person_id__names_get",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "tree_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Tree Id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "person_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Person Id"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NameRead"
|
||||
},
|
||||
"title": "Response List Names Api V1 Trees Tree Id Persons Person Id Names Get"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"names"
|
||||
],
|
||||
"summary": "Create Name",
|
||||
"operationId": "create_name_api_v1_trees__tree_id__persons__person_id__names_post",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "tree_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Tree Id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "person_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Person Id"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/NameCreate"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/NameRead"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/trees/{tree_id}/persons/{person_id}/names/{name_id}": {
|
||||
"patch": {
|
||||
"tags": [
|
||||
"names"
|
||||
],
|
||||
"summary": "Update Name",
|
||||
"operationId": "update_name_api_v1_trees__tree_id__persons__person_id__names__name_id__patch",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "tree_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Tree Id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "person_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Person Id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "name_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Name Id"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/NameUpdate"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/NameRead"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"names"
|
||||
],
|
||||
"summary": "Delete Name",
|
||||
"operationId": "delete_name_api_v1_trees__tree_id__persons__person_id__names__name_id__delete",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "tree_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Tree Id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "person_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Person Id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "name_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Name Id"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Successful Response"
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/trees/{tree_id}/events": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -3140,6 +3449,267 @@
|
||||
"type": "object",
|
||||
"title": "MediaUpdate"
|
||||
},
|
||||
"NameCreate": {
|
||||
"properties": {
|
||||
"name_type": {
|
||||
"type": "string",
|
||||
"title": "Name Type",
|
||||
"default": "birth"
|
||||
},
|
||||
"given": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Given"
|
||||
},
|
||||
"surname": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Surname"
|
||||
},
|
||||
"prefix": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Prefix"
|
||||
},
|
||||
"suffix": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Suffix"
|
||||
},
|
||||
"nickname": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Nickname"
|
||||
},
|
||||
"is_primary": {
|
||||
"type": "boolean",
|
||||
"title": "Is Primary",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "NameCreate"
|
||||
},
|
||||
"NameRead": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Id"
|
||||
},
|
||||
"tree_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Tree Id"
|
||||
},
|
||||
"person_id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Person Id"
|
||||
},
|
||||
"name_type": {
|
||||
"type": "string",
|
||||
"title": "Name Type"
|
||||
},
|
||||
"given": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Given"
|
||||
},
|
||||
"surname": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Surname"
|
||||
},
|
||||
"prefix": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Prefix"
|
||||
},
|
||||
"suffix": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Suffix"
|
||||
},
|
||||
"nickname": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Nickname"
|
||||
},
|
||||
"is_primary": {
|
||||
"type": "boolean",
|
||||
"title": "Is Primary"
|
||||
},
|
||||
"sort_order": {
|
||||
"type": "integer",
|
||||
"title": "Sort Order"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"title": "Created At"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"tree_id",
|
||||
"person_id",
|
||||
"name_type",
|
||||
"given",
|
||||
"surname",
|
||||
"prefix",
|
||||
"suffix",
|
||||
"nickname",
|
||||
"is_primary",
|
||||
"sort_order",
|
||||
"created_at"
|
||||
],
|
||||
"title": "NameRead"
|
||||
},
|
||||
"NameUpdate": {
|
||||
"properties": {
|
||||
"name_type": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Name Type"
|
||||
},
|
||||
"given": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Given"
|
||||
},
|
||||
"surname": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Surname"
|
||||
},
|
||||
"prefix": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Prefix"
|
||||
},
|
||||
"suffix": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Suffix"
|
||||
},
|
||||
"nickname": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Nickname"
|
||||
},
|
||||
"is_primary": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Is Primary"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "NameUpdate"
|
||||
},
|
||||
"ParentChildQualifier": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
@@ -4063,6 +4633,18 @@
|
||||
],
|
||||
"title": "Email Verified At"
|
||||
},
|
||||
"self_person_id": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Self Person Id"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
@@ -4079,6 +4661,24 @@
|
||||
],
|
||||
"title": "UserRead"
|
||||
},
|
||||
"UserSelfPersonUpdate": {
|
||||
"properties": {
|
||||
"self_person_id": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Self Person Id"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "UserSelfPersonUpdate"
|
||||
},
|
||||
"ValidationError": {
|
||||
"properties": {
|
||||
"loc": {
|
||||
|
||||
Reference in New Issue
Block a user