feat(家庭成员): 添加家庭成员模型并关联感悟和计划

添加家庭成员模型(FamilyMember)并关联到感悟记录(InsightRecord)和今日计划(TodayPlan)
修改相关表单、视图和模板以支持发言人功能
添加数据库迁移文件和初始化脚本
更新报告模板显示发言人信息
This commit is contained in:
2026-01-23 20:35:30 +08:00
parent 5f9bd7da3e
commit 004f528c7f
7 changed files with 109 additions and 11 deletions

View File

@@ -6,7 +6,8 @@ from .models import (
InsightRecord,
FamilyTask,
TodayPlan,
SystemConfig
SystemConfig,
FamilyMember
)
class ReadingRecordForm(forms.ModelForm):
@@ -27,8 +28,9 @@ class InsightRecordForm(forms.ModelForm):
"""感悟记录表单"""
class Meta:
model = InsightRecord
fields = ['content', 'file']
fields = ['speaker', 'content', 'file']
widgets = {
'speaker': forms.Select(attrs={'class': 'form-select'}),
'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 5, 'placeholder': '请输入感悟'}),
'file': forms.FileInput(attrs={'class': 'form-control'}),
}
@@ -50,9 +52,10 @@ class TodayPlanForm(forms.ModelForm):
"""今日计划表单"""
class Meta:
model = TodayPlan
fields = ['date', 'content', 'priority', 'type', 'status']
fields = ['date', 'speaker', 'content', 'priority', 'type', 'status']
widgets = {
'date': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}),
'speaker': forms.Select(attrs={'class': 'form-select'}),
'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 3, 'placeholder': '请输入计划内容'}),
'priority': forms.Select(attrs={'class': 'form-select'}),
'type': forms.Select(attrs={'class': 'form-select'}),

View File

@@ -0,0 +1,38 @@
# Generated by Django 5.1.4
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0004_systemconfig_sender_email'),
]
operations = [
migrations.CreateModel(
name='FamilyMember',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=20, unique=True, verbose_name='姓名')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '家庭成员',
'verbose_name_plural': '家庭成员',
'ordering': ['name'],
},
),
migrations.AddField(
model_name='insightrecord',
name='speaker',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='core.familymember', verbose_name='发言人'),
),
migrations.AddField(
model_name='todayplan',
name='speaker',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='core.familymember', verbose_name='发言人'),
),
]

View File

@@ -71,6 +71,20 @@ class PlanType(models.Model):
def __str__(self):
return self.name
class FamilyMember(models.Model):
"""家庭成员"""
name = models.CharField(max_length=20, unique=True, verbose_name="姓名")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = "家庭成员"
verbose_name_plural = "家庭成员"
ordering = ['name']
def __str__(self):
return self.name
class ReadingRecord(models.Model):
"""阅读记录表"""
date = models.DateField(default=timezone.now, verbose_name="日期")
@@ -95,6 +109,7 @@ class InsightRecord(models.Model):
"""感悟记录表"""
date = models.DateField(default=timezone.now, verbose_name="日期")
content = models.TextField(verbose_name="内容")
speaker = models.ForeignKey(FamilyMember, on_delete=models.CASCADE, verbose_name="发言人")
file = models.FileField(upload_to='insight_files/', blank=True, null=True, verbose_name="上传文件")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
@@ -129,6 +144,7 @@ class TodayPlan(models.Model):
"""今日计划表"""
date = models.DateField(default=timezone.now, verbose_name="日期")
content = models.TextField(verbose_name="内容")
speaker = models.ForeignKey(FamilyMember, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="发言人")
priority = models.ForeignKey(Priority, on_delete=models.CASCADE, default=2, verbose_name="优先级")
type = models.ForeignKey(PlanType, on_delete=models.CASCADE, default=4, verbose_name="类型")
status = models.ForeignKey(Status, on_delete=models.CASCADE, default=1, verbose_name="状态")

View File

@@ -133,7 +133,7 @@
{% if yesterday_insight %}
<ul>
{% for insight in yesterday_insight %}
<li>{{ insight.content }}</li>
<li><strong>{{ insight.speaker.name }}</strong>{{ insight.content }}</li>
{% endfor %}
</ul>
{% else %}
@@ -148,6 +148,7 @@
<table>
<thead>
<tr>
<th>发言人</th>
<th>类型</th>
<th>内容</th>
<th>优先级</th>
@@ -157,6 +158,7 @@
<tbody>
{% for plan in today_plan %}
<tr>
<td>{{ plan.speaker.name }}</td>
<td>{{ plan.get_type_display }}</td>
<td>{{ plan.content }}</td>
<td>{{ plan.get_priority_display }}</td>

View File

@@ -39,7 +39,8 @@ from .models import (
InsightRecord,
FamilyTask,
TodayPlan,
SystemConfig
SystemConfig,
FamilyMember
)
from .forms import (
ReadingRecordForm,
@@ -147,6 +148,7 @@ def delete_reading(request, pk):
# 添加感悟记录
def add_insight(request):
"""添加感悟记录"""
family_members = FamilyMember.objects.all()
if request.method == 'POST':
form = InsightRecordForm(request.POST, request.FILES)
if form.is_valid():
@@ -158,13 +160,14 @@ def add_insight(request):
else:
form = InsightRecordForm()
context = {'form': form}
context = {'form': form, 'family_members': family_members}
return render(request, 'core/add_insight.html', context)
# 编辑感悟记录
def edit_insight(request, pk):
"""编辑感悟记录"""
insight = get_object_or_404(InsightRecord, pk=pk)
family_members = FamilyMember.objects.all()
if request.method == 'POST':
form = InsightRecordForm(request.POST, request.FILES, instance=insight)
if form.is_valid():
@@ -174,7 +177,7 @@ def edit_insight(request, pk):
else:
form = InsightRecordForm(instance=insight)
context = {'form': form, 'insight': insight}
context = {'form': form, 'insight': insight, 'family_members': family_members}
return render(request, 'core/edit_insight.html', context)
# 删除感悟记录
@@ -257,6 +260,7 @@ def delete_today_reading(request, pk):
# 添加今日感悟记录
def add_today_insight(request):
"""添加今日感悟记录"""
family_members = FamilyMember.objects.all()
if request.method == 'POST':
form = InsightRecordForm(request.POST, request.FILES)
if form.is_valid():
@@ -268,13 +272,14 @@ def add_today_insight(request):
else:
form = InsightRecordForm()
context = {'form': form}
context = {'form': form, 'family_members': family_members}
return render(request, 'core/add_insight.html', context)
# 编辑今日感悟记录
def edit_today_insight(request, pk):
"""编辑今日感悟记录"""
insight = get_object_or_404(InsightRecord, pk=pk)
family_members = FamilyMember.objects.all()
if request.method == 'POST':
form = InsightRecordForm(request.POST, request.FILES, instance=insight)
if form.is_valid():
@@ -284,7 +289,7 @@ def edit_today_insight(request, pk):
else:
form = InsightRecordForm(instance=insight)
context = {'form': form, 'insight': insight}
context = {'form': form, 'insight': insight, 'family_members': family_members}
return render(request, 'core/edit_insight.html', context)
# 删除今日感悟记录
@@ -373,6 +378,7 @@ def today_plan(request):
# 添加今日计划
def add_today_plan(request):
"""添加今日计划"""
family_members = FamilyMember.objects.all()
if request.method == 'POST':
form = TodayPlanForm(request.POST)
if form.is_valid():
@@ -382,13 +388,14 @@ def add_today_plan(request):
else:
form = TodayPlanForm()
context = {'form': form}
context = {'form': form, 'family_members': family_members}
return render(request, 'core/add_today_plan.html', context)
# 编辑今日计划
def edit_today_plan(request, pk):
"""编辑今日计划"""
plan = get_object_or_404(TodayPlan, pk=pk)
family_members = FamilyMember.objects.all()
if request.method == 'POST':
form = TodayPlanForm(request.POST, instance=plan)
if form.is_valid():
@@ -398,7 +405,7 @@ def edit_today_plan(request, pk):
else:
form = TodayPlanForm(instance=plan)
context = {'form': form, 'plan': plan}
context = {'form': form, 'plan': plan, 'family_members': family_members}
return render(request, 'core/edit_today_plan.html', context)
# 删除今日计划

22
update_db.py Normal file
View File

@@ -0,0 +1,22 @@
import sqlite3
conn = sqlite3.connect('db.sqlite3')
cursor = conn.cursor()
cursor.execute('''
INSERT INTO core_familymember (name, created_at, updated_at)
VALUES ('默认成员', datetime('now', 'localtime'), datetime('now', 'localtime'));
''')
cursor.execute('''
ALTER TABLE core_insightrecord ADD COLUMN speaker_id INTEGER NOT NULL DEFAULT 1;
''')
cursor.execute('''
ALTER TABLE core_todayplan ADD COLUMN speaker_id INTEGER NOT NULL DEFAULT 1;
''')
conn.commit()
conn.close()
print("Database updated successfully!")

10
verify_db.py Normal file
View File

@@ -0,0 +1,10 @@
import sqlite3
conn = sqlite3.connect('db.sqlite3')
cursor = conn.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'core_%' ORDER BY name;")
print("Tables:", [r[0] for r in cursor.fetchall()])
cursor.execute("SELECT COUNT(*) FROM core_familymember;")
print("Family members count:", cursor.fetchone()[0])
cursor.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='core_insightrecord';")
print("InsightRecord schema:", cursor.fetchone()[0])
conn.close()