基于 Django Web 开发中医针灸管理经络图系统的实用
以下是基于 Django Web 开发中医针灸管理经络图系统的实用示例,涵盖模型设计、视图逻辑、模板渲染等关键环节,提供典型代码片段:
模型设计(Meridian 和 Acupoint)
# models.py
class Meridian(models.Model):
name = models.CharField(max_length=50, verbose_name="经络名称")
abbreviation = models.CharField(max_length=10, verbose_name="缩写")
description = models.TextField(verbose_name="经络描述")
image = models.ImageField(upload_to='meridians/', null=True)
def __str__(self):
return self.name
class Acupoint(models.Model):
name = models.CharField(max_length=50, verbose_name="穴位名称")
code = models.CharField(max_length=10, verbose_name="代号")
meridian = models.ForeignKey(Meridian, on_delete=models.CASCADE)
location = models.TextField(verbose_name="定位")
function = models.TextField(verbose_name="功能")
needling_method = models.TextField(verbose_name="针刺方法")
class Meta:
ordering = ['meridian', 'code']
数据导入示例(JSON 批量导入)
# management/commands/import_meridians.py
from django.core.management.base import BaseCommand
import json
class Command(BaseCommand):
def handle(self, *args, **options):
with open('meridians.json', encoding='utf-8') as f:
data = json.load(f)
for item in data['meridians']:
Meridian.objects.create(
name=item['name'],
abbreviation=item['abbreviation'],
description=item['description']
)
视图逻辑(经络列表+搜索)
# views.py
def meridian_list(request):
query = request.GET.get('q')
meridians = Meridian.objects.all()
if query:
meridians = meridians.filter(
models.Q(name__icontains=query) |
models.Q(abbreviation__icontains=query)
)
return render(request, 'tcm/meridian_list.html', {'meridians': meridians})
模板渲染(经络卡片展示)
<!-- templates/tcm/meridian_list.html -->
{% for meridian in meridians %}
<div class="card">
<img src="{
{ meridian.image.url }}" class="card-img-top">
<div class="card-body">
<h5>{
{ meridian.name }} ({
{ meridian.abbreviation }})</h5>
<p>{
{ meridian.description|truncatechars:100 }}</p>
<a href="{% url 'meridian_detail' meridian.id %}" class="btn btn-primary">
查看详情
</a>
</div>
</div>
{% endfor %}
经络详情页(带穴位列表)
# views.py
def meridian_detail(request, pk):
meridian = get_object_or_404(Meridian, pk=pk)
acupoints = Acupoint.objects.filter(meridian=meridian)
return render(request, 'tcm/meridian_detail.html', {
'meridian': meridian,
'acupoints': acupoints
})
交互式经络图(SVG+JavaScript)
<!-- templates/tcm/meridian_map.html -->
<svg id="meridian-map" viewBox="0 0 800 600">
{% for meridian in meridians %}
<path
class="meridian-path"
d="{
{ meridian.svg_path }}"
data-meridian-id="{
{ meridian.id }}"
fill="none"
stroke="#d35400"
stroke-width="3"
/>
{% endfor %}
</svg>
<script>
document.querySelectorAll('.meridian-path').forEach(path => {
path.addEventListener('click', () => {
window.location.href = `/meridians/${path.dataset.meridianId}/`;
});
});
</script>
REST API 设计(DRF)
# serializers.py
class AcupointSerializer(serializers.ModelSerializer):
class Meta:
model = Acupoint
fields = ['id', 'name', 'code', 'location']
# views.py
class MeridianViewSet(viewsets.ModelViewSet):
queryset = Meridian.objects.all()
serializer_class = MeridianSerializer
@action(detail=True, methods=['get'])
def acupoints(self, request, pk=None):
meridian = self.get_object()
acupoints = Acupoint.objects.filter(meridian=meridian)
serializer = AcupointSerializer(acupoints, many=True)
return Response(serializer.data)
穴位搜索(自动完成)
# views.py
class AcupointAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
qs = Acupoint.objects.all()
if self.q:
qs = qs.filter(name__istartswith=self.q)
return qs
数据可视化(Chart.js)
// 经络穴位数量统计
const ctx = document.getElementById('meridianChart').getContext('2d');
fetch('/api/meridians/stats/')
.then(res => res.json())
.then(data => {
new Chart(ctx, {
type: 'bar',
data: {
labels: data.labels,
datasets: [{
label: '穴位数量',
data: data.counts,
backgroundColor: '#e74c3c'
}]
}
});
});
测试用例(模型验证)
# tests.py
class MeridianModelTest(TestCase):
@classmethod
def setUpTestData(cls):
Meridian.objects.create(
name="手太阴肺经",
abbreviation="LU",
description="起于中焦,下络大肠..."
)
def test_abbreviation_max_length(self):
meridian = Meridian.objects.get(id=1)
max_length = meridian._meta.get_field('abbreviation').max_length
self.assertEqual(max_length, 10)
信号处理(图片缩略图生成)
# signals.py
@receiver(models.signals.post_save, sender=Meridian)
def create_meridian_thumbnail(sender, instance, **kwargs):
if instance.image:
img = Image.open(instance.image.path)
img.thumbnail((300, 300))
thumb_path = f"{instance.image.path}_thumb"
img.save(thumb_path)
管理后台定制
# admin.py
class AcupointInline(admin.TabularInline):
model = Acupoint
extra = 1
@admin.register(Meridian)
class MeridianAdmin(admin.ModelAdmin):
inlines = [AcupointInline]
list_display = ('name', 'abbreviation', 'acupoint_count')
def acupoint_count(self, obj):
return obj.acupoint_set.count()
表单设计(带富文本编辑)
# forms.py
class MeridianForm(forms.ModelForm):
description = forms.CharField(widget=CKEditorWidget())
class Meta:
model = Meridian
fields = ['name', 'abbreviation', 'description', 'image']
权限控制(自定义权限)
# models.py
class Meridian(models.Model):
# ... 其他字段 ...
class Meta:
permissions = [
("can_edit_meridian", "Can edit meridian details"),
("can_upload_image", "Can upload meridian images"),
]
任务队列(生成经络PDF)
# tasks.py
@shared_task
def generate_meridian_pdf(meridian_id):
meridian = Meridian.objects.get(pk=meridian_id)
html = render_to_string('tcm/meridian_pdf.html', {'meridian': meridian})
pdf = HTML(string=html).write_pdf()
with open(f'media/meridians/{meridian_id}.pdf', 'wb') as f:
f.write(pdf)
国际化和本地化
# models.py
class Acupoint(models.Model):
name = models.CharField(_("Acupoint Name"), max_length=50)
location = models.TextField(_("Location Description"))
# 其他字段...
缓存策略(经络列表缓存)
# views.py
@cache_page(60 * 15)
def meridian_list(request):
meridians = Meridian.objects.all().prefetch_related('acupoint_set')
return render(request, 'tcm/meridian_list.html', {'meridians': meridians})
日志记录(关键操作)
# views.py
logger = logging.getLogger('tcm.views')
def add_acupoint(request):
if request.method == 'POST':
form = AcupointForm(request.POST)
if form.is_valid():
acupoint = form.save()
logger.info(f'Acupoint created: {acupoint.name}')
return redirect('meridian_detail', pk=acupoint.meridian.id)
信号量(数据同步)
# signals.py
@receiver(post_save, sender=Acupoint)
def sync_to_elasticsearch(sender, instance, **kwargs):
document = {
'name': instance.name,
'code': instance.code,
'meridian': instance.meridian.name,
'location': instance.location
}
es.index(index='acupoints', id=instance.id, body=document)
微信小程序 API
# api/views.py
class MiniProgramMeridianView(APIView):
authentication_classes = [SessionAuthentication]
def get(self, request):
meridians = Meridian.objects.values('id', 'name', 'abbreviation')
return Response({'data': list(meridians)})
数据导出(Excel 报表)
# views.py
def export_meridians(request):
response = HttpResponse(content_type='application/ms-excel')
response['Content-Disposition'] = 'attachment; filename="meridians.xlsx"'
wb = Workbook()
ws = wb.active
ws.append(['ID', 'Name', 'Abbreviation', 'Acupoints Count'])
for m in Meridian.objects.all():
ws.append([m.id, m.name, m.abbreviation, m.acupoint_set.count()])
wb.save(response)
return response
实时通知(WebSocket)
# consumers.py
class MeridianConsumer(AsyncWebsocketConsumer):
async def connect(self):
await self.accept()
await self.channel_layer.group_add("meridians", self.channel_name)
async def send_update(self, event):
await self.send(text_data=json.dumps(event["data"]))
性能优化(select_related)
# views.py
def meridian_detail_optimized(request, pk):
meri