This commit refactors numerous Pydantic models to align with the V3 ID Vision standard, ensuring that primary and foreign key fields are represented as clean string IDs in the API. It also introduces and populates the ClassVar in each model to prevent view-only fields and linked objects from being inadvertently written to the database during PATCH/POST operations. Specifically, this includes: - Adding to exclude view-derived or joined fields such as , , nested objects (e.g., , ), and convenience fields (e.g., ). - Adjusting root validators to correctly map string IDs and strip internal integer IDs for API responses. - Resolving a KeyError by adding to . These changes are crucial for maintaining data integrity and consistency with the V3 API architecture.
151 lines
6.4 KiB
Python
151 lines
6.4 KiB
Python
import datetime, pytz
|
|
|
|
from typing import Dict, List, Optional, Set, Union, ClassVar
|
|
from pydantic import BaseModel, EmailStr, Field, Json, PrivateAttr, ValidationError, validator, root_validator
|
|
|
|
from app.db_sql import redis_lookup_id_random
|
|
from app.lib_general import log, logging
|
|
|
|
from app.models.common_field_schema import base_fields, default_num_bytes
|
|
from app.models.contact_models import Contact_Base
|
|
from app.models.event_cfg_models import Event_Cfg_Base
|
|
from app.models.organization_models import Organization_Base
|
|
|
|
|
|
# ### BEGIN ### API Event Person Profile Models ### Event_Person_Profile_Base() ###
|
|
class Event_Person_Profile_Base(BaseModel):
|
|
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
|
log.debug(locals())
|
|
|
|
# --- Standardized Vision IDs (Strings) ---
|
|
id: Optional[str] = Field(None, **base_fields['event_person_profile_id_random'])
|
|
event_person_profile_id: Optional[str] = Field(None, **base_fields['event_person_profile_id_random'])
|
|
|
|
account_id: Optional[str] = Field(None, **base_fields['account_id_random'])
|
|
contact_id: Optional[str] = Field(None, **base_fields['contact_id_random'])
|
|
event_id: Optional[str] = Field(None, **base_fields['event_id_random'])
|
|
event_person_id: Optional[str] = Field(None, **base_fields['event_person_id_random'])
|
|
organization_id: Optional[str] = Field(None, **base_fields['organization_id_random'])
|
|
|
|
# --- Standardized Legacy / Internal IDs (Excluded) ---
|
|
id_random: Optional[str] = Field(None, alias='event_person_profile_id_random', exclude=True)
|
|
account_id_random: Optional[str] = Field(None, exclude=True)
|
|
contact_id_random: Optional[str] = Field(None, exclude=True)
|
|
event_id_random: Optional[str] = Field(None, exclude=True)
|
|
event_person_id_random: Optional[str] = Field(None, exclude=True)
|
|
organization_id_random: Optional[str] = Field(None, exclude=True)
|
|
|
|
pronouns: Optional[str] # Preferred pronouns
|
|
informal_name: Optional[str]
|
|
|
|
title_names: Optional[str] # Title for generation, official position, or professional or academic qualification, other honorific, or other name prefix
|
|
given_name: Optional[str]
|
|
middle_name: Optional[str]
|
|
family_name: Optional[str]
|
|
designations: Optional[str] # Temporary or long-term designations related to family, relationships, person differentiation (Junior/Senior), location, social status, professional qualifications, legal status, or other name suffix
|
|
|
|
professional_title: Optional[str] # Professional title
|
|
|
|
display_name: Optional[str] # This will be changed to full_name_override to match event_badge, event_presenter, and person. View of display_override. Actual name shown in the directory and other "public" areas
|
|
|
|
# BEGIN # Auto created name variations
|
|
full_name: Optional[str] # title_names given_name middle_name family_name designations
|
|
full_name_override: Optional[str] # Actual name shown in the directory and other "public" areas
|
|
|
|
affiliations: Optional[str] # One or more affiliations with organizations, companies, and other groups
|
|
|
|
tagline: Optional[str]
|
|
biography: Optional[str]
|
|
|
|
email: Optional[str]
|
|
website_url: Optional[str]
|
|
|
|
thumbnail_hosted_file_id: Optional[str] = Field(None, **base_fields['hosted_file_id_random'])
|
|
thumbnail_path: Optional[str]
|
|
thumbnail_bg_color: Optional[str]
|
|
|
|
# photo_path: Optional[str]
|
|
# photo_bg_color: Optional[str]
|
|
|
|
picture_hosted_file_id: Optional[str] = Field(None, **base_fields['hosted_file_id_random'])
|
|
picture_path: Optional[str]
|
|
picture_bg_color: Optional[str]
|
|
|
|
about_hosted_file_id: Optional[str] = Field(None, **base_fields['hosted_file_id_random'])
|
|
about_path: Optional[str]
|
|
|
|
email_allowed: Optional[bool]
|
|
email_marketing: Optional[bool]
|
|
email_updates: Optional[bool]
|
|
|
|
show_online: Optional[bool]
|
|
show_printed: Optional[bool]
|
|
|
|
enable: Optional[bool]
|
|
priority: Optional[bool]
|
|
sort: Optional[int]
|
|
group: Optional[str]
|
|
|
|
notes: Optional[str]
|
|
created_on: Optional[datetime.datetime] = None
|
|
updated_on: Optional[datetime.datetime] = None
|
|
|
|
# Including JSON data
|
|
extended_json: Optional[Json]
|
|
|
|
# Including other related objects
|
|
contact: Optional[Contact_Base]
|
|
event_cfg: Optional[Event_Cfg_Base] = Field(
|
|
alias = 'cfg',
|
|
)
|
|
organization: Optional[Organization_Base]
|
|
|
|
_processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now)
|
|
|
|
@root_validator(pre=True)
|
|
def map_v3_ids(cls, values):
|
|
"""
|
|
Vision Transformer:
|
|
Map DB keys to clean API keys and strip internal integers.
|
|
"""
|
|
# 1. Map Random Strings to Clean Names
|
|
if rid := values.get('id_random') or values.get('event_person_profile_id_random'):
|
|
values['id'] = rid
|
|
values['event_person_profile_id'] = rid
|
|
|
|
if a_rid := values.get('account_id_random'): values['account_id'] = a_rid
|
|
if c_rid := values.get('contact_id_random'): values['contact_id'] = c_rid
|
|
if e_rid := values.get('event_id_random'): values['event_id'] = e_rid
|
|
if ep_rid := values.get('event_person_id_random'): values['event_person_id'] = ep_rid
|
|
if o_rid := values.get('organization_id_random'): values['organization_id'] = o_rid
|
|
|
|
# 2. Prevent "Collision Population"
|
|
for k in ['id', 'event_person_profile_id', 'account_id', 'contact_id', 'event_id', 'event_person_id', 'organization_id']:
|
|
if k in values and not isinstance(values[k], str) and values[k] is not None:
|
|
del values[k]
|
|
|
|
return values
|
|
|
|
# Fields that are part of the model (for reading) but should not be saved to the DB table
|
|
fields_to_exclude_from_db: ClassVar[list] = [
|
|
'account_id', 'event_id', 'event_person_id',
|
|
'full_name', 'full_name_override', 'display_name',
|
|
'thumbnail_path', 'picture_path', 'about_path',
|
|
'contact', 'event_cfg', 'organization'
|
|
]
|
|
|
|
class Config:
|
|
underscore_attrs_are_private = True
|
|
allow_population_by_field_name = True
|
|
fields = base_fields
|
|
|
|
|
|
class Event_Person_Profile_Base_Up(Event_Person_Profile_Base):
|
|
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
|
log.debug(locals())
|
|
|
|
|
|
# Including JSON data
|
|
extended_json: Optional[str]
|
|
# ### END ### API Event Person Profile Models ### Event_Person_Profile_Base() ###
|