Location Models (Country · Province · City)
ZiWound's geographic hierarchy is country → province → city, and each level carries extensive, localized war-history content. These three models share one important design decision: their rich text lives in nested localizedWarInfo objects ({ fa, en, ar, zh, pt, es, nl, tr, ru }), so they have no selected_language field. See the Localization pattern for the full reasoning.
The shared shape
All three models carry the same localized war-history payload, defined once in models/utils/localizedFields.ts:
export const localizedWarInfo = object({
fa: optional(string()),
en: optional(string()),
ar: optional(string()),
zh: optional(string()),
pt: optional(string()),
es: optional(string()),
nl: optional(string()),
tr: optional(string()),
ru: optional(string()),
});
| Field group | Example fields |
|---|---|
| Basics | name, english_name, registrar (User) |
| War history | wars_history, conflict_timeline, casualties_info |
| Documentation | war_crimes_documentation, human_rights_violations, genocide_info |
| Impact | displacement_info, reconstruction_status, international_sanctions |
| Events | notable_war_events, notable_battles, mass_graves_info, war_crimes_events |
| Photo | a single file relation (one-directional, optional) |
The relations ladder
Each level points to its parent, and relatedRelations builds the reverse list automatically — Lesan's classic one-directional pattern:
// city.ts — the deepest level
export const city_relations = {
province: {
schemaName: "province",
type: "single" as RelationDataType,
optional: false,
excludes: location_excludes,
relatedRelations: { cities: { type: "multiple" as RelationDataType, limit: 50 } },
},
country: {
schemaName: "country",
type: "single" as RelationDataType,
optional: false,
excludes: location_excludes,
relatedRelations: { cities: { type: "multiple" as RelationDataType, limit: 50 } },
},
registrar: { schemaName: "user", type: "single" as RelationDataType, optional: true, ... },
photo: { schemaName: "file", type: "single" as RelationDataType, optional: true, ... },
};
// province.ts
export const province_relations = {
country: {
schemaName: "country",
type: "single" as RelationDataType,
optional: false,
excludes: location_excludes,
relatedRelations: { provinces: { type: "multiple" as RelationDataType, limit: 50 } },
},
// registrar, photo ...
};
Indexes
- Text index on
name,english_name, and the localized war-history fields (en/fa/arsub-fields) for search. updatevsupdateRelationssplit — pure fields update viaupdate;countryId/photomove throughupdateRelations. This is Lesan's recommended separation (see the Relationship Management docs).
Acts
Each of the three models exposes the standard CRUD set: add, get, gets, update, updateRelations, remove, count.
Next: Catalog Models.