过年那会儿,看着家族的子孙辈的孩子们一大群来拜年,竟然还有好多叫不上名字,再看看垂垂老矣的父母,想着很多年前做了一个家族的通讯录,何不做一个家族的管理系统来,本身也算是一种传承吧,当然了,真正的家谱那是有严格的规格和规则限制的,我这个纯属于只为了记录的一种的家谱形式而已。
家族族谱管理系统是一款专为个人和家族设计的本地单机应用,用于记录和管理家族成员信息、血缘关系、家族历史等数据。
主要功能
- 家族成员信息管理(添加、编辑、删除)
- 血缘关系可视化展示(家族树)
- 家族历史事件记录
- 家族成员照片管理
开发目的
本系统的开发旨在:
- 帮助家族记录和传承家族文化
- 提供直观、易用的家族树可视化工具
- 确保家族数据的安全存储和管理
- 促进家族成员之间的联系和了解
技术栈
本系统基于以下技术开发:
- 前端:HTML5, CSS3, JavaScript, Bootstrap 5
- 后端:Python, Flask, SQLAlchemy
- 数据库:SQLite
- 打包工具:PyInstaller
界面如图:


单文件可以单机使用,为了您的意思安全,建议单机使用,随用隋开,不用关上就好了。当然了,也支持多用户。数据库自己保存好就行。

为了测试数据,我就拿了朱元璋的临时举个例子。

家族成员列表页面

按辈分显示的家族列表页面

家族树

感觉以上这样可还行?
3. 项目结构
Family/
├── app.py # 主应用文件
├── family_tree.spec # PyInstaller 打包配置
├── pdf_generator.py # PDF 生成模块
├── templates/ # 模板文件
│ ├── base.html # 基础模板
│ ├── index.html # 首页
│ ├── dashboard.html # 仪表盘
│ ├── family_tree.html # 家族树页面
│ ├── profile.html # 个人信息页面
│ ├── disclaimer.html # 免责声明页面
│ └── about.html # 关于本系统页面
├── static/ # 静态文件
│ ├── css/ # CSS 文件
│ ├── js/ # JavaScript 文件
│ ├── webfonts/ # 字体文件
│ ├── uploads/ # 上传文件
│ └── icon files # 图标文件
└── instance/ # 数据库文件
└── family_tree.db # SQLite 数据库
代码部分
class Family(db.Model):
"""家族模型"""
__tablename__ = 'families'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
description = db.Column(db.Text)
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# 删除状态
is_deleted = db.Column(db.Boolean, default=False)
deleted_at = db.Column(db.DateTime)
deleted_by = db.Column(db.Integer, db.ForeignKey('users.id'))
is_permanently_deleted = db.Column(db.Boolean, default=False) # 是否彻底删除
# 关联
members = db.relationship('FamilyMember', backref='family', lazy=True, cascade='all, delete-orphan')
class FamilyMember(db.Model):
"""家族成员模型"""
__tablename__ = 'family_members'
id = db.Column(db.Integer, primary_key=True)
family_id = db.Column(db.Integer, db.ForeignKey('families.id'), nullable=False)
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
# 基本信息
name = db.Column(db.String(100), nullable=False)
gender = db.Column(db.String(10), nullable=False) # 'male', 'female'
birth_date = db.Column(db.Date)
death_date = db.Column(db.Date)
is_alive = db.Column(db.Boolean, default=True)
# 联系方式
phone = db.Column(db.String(20))
email = db.Column(db.String(120))
address = db.Column(db.Text)
wechat = db.Column(db.String(50))
wechat_qr = db.Column(db.String(255))
# 职业信息
occupation = db.Column(db.String(100))
company = db.Column(db.String(100))
title = db.Column(db.String(50))
# 个人信息
biography = db.Column(db.Text)
personality = db.Column(db.Text)
achievements = db.Column(db.Text)
# 墓地信息(非健在人员)
cemetery_location = db.Column(db.Text)
# 照片
photo = db.Column(db.String(255))
# 树形结构 - 邻接表模式
parent_id = db.Column(db.Integer, db.ForeignKey('family_members.id'), nullable=True)
generation = db.Column(db.Integer, default=0) # 辈分代数
order_in_siblings = db.Column(db.Integer, default=0) # 在同辈中的排序
# 关系类型 - 用于标识非血亲关系
relation_type = db.Column(db.String(50), default='blood') # blood:血亲, adopted:收养, step:继亲, god:义亲, clan:宗族, master:师徒, sworn:结拜, friend:世交
# 元数据
created_at = db.Column(db.DateTime, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# 关联
children = db.relationship('FamilyMember', backref=db.backref('parent', remote_side=[id]), lazy=True)
spouse_relationships = db.relationship('SpouseRelationship', foreign_keys='SpouseRelationship.member1_id', backref='member1', lazy=True)
def to_dict(self, include_relations=False):
"""转换为字典"""
# 检查是否有用户关联,如果有则使用用户的照片
photo = self.photo
if not photo:
# 查找与该成员关联的用户
user_relation = UserFamilyRelation.query.filter_by(member_id=self.id).first()
if user_relation and user_relation.user.photo:
photo = user_relation.user.photo
data = {
'id': self.id,
'family_id': self.family_id,
'name': self.name,
'gender': self.gender,
'birth_date': self.birth_date.strftime('%Y-%m-%d') if self.birth_date else None,
'death_date': self.death_date.strftime('%Y-%m-%d') if self.death_date else None,
'is_alive': self.is_alive,
'phone': self.phone,
'email': self.email,
'address': self.address,
'wechat': self.wechat,
'wechat_qr': self.wechat_qr,
'occupation': self.occupation,
'company': self.company,
'title': self.title,
'biography': self.biography,
'personality': self.personality,
'achievements': self.achievements,
'cemetery_location': self.cemetery_location,
'photo': photo,
'parent_id': self.parent_id,
'generation': self.generation,
'order_in_siblings': self.order_in_siblings,
'relation_type': self.relation_type,
'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S'),
}
if include_relations:
# 获取配偶(双向查询)
spouses = []
# 查询作为member1的关系
for rel in self.spouse_relationships:
spouse = db.session.get(FamilyMember, rel.member2_id)
if spouse:
# 检查配偶是否有照片,如果没有则查找关联用户的照片
spouse_photo = spouse.photo
if not spouse_photo:
user_relation = UserFamilyRelation.query.filter_by(member_id=spouse.id).first()
if user_relation and user_relation.user.photo:
spouse_photo = user_relation.user.photo
spouses.append({
'id': spouse.id,
'name': spouse.name,
'birth_date': spouse.birth_date.strftime('%Y-%m-%d') if spouse.birth_date else None,
'death_date': spouse.death_date.strftime('%Y-%m-%d') if spouse.death_date else None,
'gender': spouse.gender,
'is_alive': spouse.is_alive,
'address': spouse.address,
'cemetery_location': spouse.cemetery_location,
'occupation': spouse.occupation,
'phone': spouse.phone,
'email': spouse.email,
'wechat': spouse.wechat,
'photo': spouse_photo,
'relationship_type': rel.relationship_type,
'generation': spouse.generation
})
# 查询作为member2的关系
for rel in SpouseRelationship.query.filter_by(member2_id=self.id).all():
spouse = db.session.get(FamilyMember, rel.member1_id)
if spouse:
# 检查配偶是否有照片,如果没有则查找关联用户的照片
spouse_photo = spouse.photo
if not spouse_photo:
user_relation = UserFamilyRelation.query.filter_by(member_id=spouse.id).first()
if user_relation and user_relation.user.photo:
spouse_photo = user_relation.user.photo
spouses.append({
'id': spouse.id,
'name': spouse.name,
'birth_date': spouse.birth_date.strftime('%Y-%m-%d') if spouse.birth_date else None,
'death_date': spouse.death_date.strftime('%Y-%m-%d') if spouse.death_date else None,
'gender': spouse.gender,
'is_alive': spouse.is_alive,
'address': spouse.address,
'cemetery_location': spouse.cemetery_location,
'occupation': spouse.occupation,
'phone': spouse.phone,
'email': spouse.email,
'wechat': spouse.wechat,
'photo': spouse_photo,
'relationship_type': rel.relationship_type,
'generation': spouse.generation
})
data['spouses'] = spouses
# 获取子女
children = []
for child in self.children:
children.append({
'id': child.id,
'name': child.name,
'gender': child.gender
})
data['children'] = children
return data
class SpouseRelationship(db.Model):
"""配偶关系模型"""
__tablename__ = 'spouse_relationships'
id = db.Column(db.Integer, primary_key=True)
member1_id = db.Column(db.Integer, db.ForeignKey('family_members.id'), nullable=False)
member2_id = db.Column(db.Integer, db.ForeignKey('family_members.id'), nullable=False)
relationship_type = db.Column(db.String(20), default='spouse') # 'husband', 'wife', 'spouse'
marriage_date = db.Column(db.Date)
is_current = db.Column(db.Boolean, default=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
class MemberRelation(db.Model):
"""成员关系闭包表 - 用于快速查询亲属关系"""
__tablename__ = 'member_relations'
id = db.Column(db.Integer, primary_key=True)
ancestor_id = db.Column(db.Integer, db.ForeignKey('family_members.id'), nullable=False)
descendant_id = db.Column(db.Integer, db.ForeignKey('family_members.id'), nullable=False)
distance = db.Column(db.Integer, default=0) # 距离(辈分差)
relation_type = db.Column(db.String(50)) # 关系类型
__table_args__ = (db.UniqueConstraint('ancestor_id', 'descendant_id', name='unique_relation'),)
def calculate_generation(member_id):
"""计算成员的辈分"""
member = db.session.get(FamilyMember, member_id)
if not member:
return 0
if not member.parent_id:
return 0
generation = 0
current = member
while current.parent_id:
generation += 1
current = db.session.get(FamilyMember, current.parent_id)
return generation
def get_ancestors(member_id, max_depth=10):
"""获取所有祖先"""
ancestors = []
current_id = member_id
depth = 0
while current_id and depth < max_depth:
member = db.session.get(FamilyMember, current_id)
if not member or not member.parent_id:
break
parent = db.session.get(FamilyMember, member.parent_id)
if parent:
ancestors.append(parent)
current_id = parent.id
else:
break
depth += 1
return ancestors
def get_descendants(member_id, max_depth=10):
"""获取所有后代"""
descendants = []
queue = [(member_id, 0)]
while queue:
current_id, depth = queue.pop(0)
if depth >= max_depth:
continue
children = FamilyMember.query.filter_by(parent_id=current_id).all()
for child in children:
descendants.append((child, depth + 1))
queue.append((child.id, depth + 1))
return descendants
def find_common_ancestor(member1_id, member2_id):
"""查找两个成员的共同祖先"""
ancestors1 = {member1_id: 0}
current_id = member1_id
depth = 0
# 获取member1的所有祖先
while True:
member = db.session.get(FamilyMember, current_id)
if not member or not member.parent_id:
break
depth += 1
ancestors1[member.parent_id] = depth
current_id = member.parent_id
# 检查member2及其祖先
if member2_id in ancestors1:
return member2_id, ancestors1[member2_id], 0
current_id = member2_id
depth2 = 0
while True:
member = db.session.get(FamilyMember, current_id)
if not member or not member.parent_id:
break
depth2 += 1
if member.parent_id in ancestors1:
return member.parent_id, ancestors1[member.parent_id], depth2
current_id = member.parent_id
return None, 0, 0
def build_family_tree(family_id, root_id=None, max_depth=10):
"""构建家族树结构"""
if root_id:
root = db.session.get(FamilyMember, root_id)
else:
# 找到根节点(没有parent的节点中最早创建的)
root = FamilyMember.query.filter_by(family_id=family_id, parent_id=None).order_by(FamilyMember.created_at).first()
if not root:
return None
def build_node(member, depth=0):
if depth >= max_depth:
return None
node = member.to_dict(include_relations=True)
node['depth'] = depth
# 获取配偶信息(双向查询)
spouses = []
# 查询 member1_id 是当前成员的情况
for rel in SpouseRelationship.query.filter_by(member1_id=member.id).all():
spouse = db.session.get(FamilyMember, rel.member2_id)
if spouse:
spouses.append({
'id': spouse.id,
'name': spouse.name,
'gender': spouse.gender,
'photo': spouse.photo,
'birth_date': spouse.birth_date.strftime('%Y-%m-%d') if spouse.birth_date else None,
'death_date': spouse.death_date.strftime('%Y-%m-%d') if spouse.death_date else None,
'is_alive': spouse.is_alive,
'occupation': spouse.occupation,
'generation': spouse.generation,
'relationship_type': rel.relationship_type
})
# 查询 member2_id 是当前成员的情况
for rel in SpouseRelationship.query.filter_by(member2_id=member.id).all():
spouse = db.session.get(FamilyMember, rel.member1_id)
if spouse:
spouses.append({
'id': spouse.id,
'name': spouse.name,
'gender': spouse.gender,
'photo': spouse.photo,
'birth_date': spouse.birth_date.strftime('%Y-%m-%d') if spouse.birth_date else None,
'death_date': spouse.death_date.strftime('%Y-%m-%d') if spouse.death_date else None,
'is_alive': spouse.is_alive,
'occupation': spouse.occupation,
'generation': spouse.generation,
'relationship_type': rel.relationship_type
})
node['spouses'] = spouses
# 递归获取子女
children = []
for child in FamilyMember.query.filter_by(parent_id=member.id).order_by(FamilyMember.order_in_siblings, FamilyMember.birth_date).all():
child_node = build_node(child, depth + 1)
if child_node:
children.append(child_node)
node['children'] = children
return node
return build_node(root)
这个其实很简单,就不多说了。

1094

被折叠的 条评论
为什么被折叠?



