feat: teacher-student relation
This commit is contained in:
@@ -34,26 +34,43 @@ class CmsRelationSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class CmsRelationWriteSerializer(serializers.ModelSerializer):
|
class CmsRelationWriteSerializer(serializers.ModelSerializer):
|
||||||
"""新增 / 编辑:teacher 必须是医生、student 必须是学生;医院管理员限本院。"""
|
"""新增 / 编辑:入参用**带教老师手机号 / 学生手机号**;按手机号解析为用户
|
||||||
teacher = serializers.PrimaryKeyRelatedField(queryset=User.objects.filter(role_type='doctor'))
|
(带教老师须 doctor、学生须 student);医院管理员限本院。"""
|
||||||
student = serializers.PrimaryKeyRelatedField(queryset=User.objects.filter(role_type='student'))
|
teacher_phone = serializers.CharField(write_only=True, required=False, help_text='带教老师手机号(新增必填)')
|
||||||
|
student_phone = serializers.CharField(write_only=True, required=False, help_text='学生手机号(新增必填)')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TeacherStudentRelation
|
model = TeacherStudentRelation
|
||||||
fields = ['teacher', 'student', 'relation_type', 'status']
|
fields = ['teacher_phone', 'student_phone', 'relation_type', 'status']
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
actor = self.context['request'].user
|
actor = self.context['request'].user
|
||||||
teacher = attrs.get('teacher') or (self.instance.teacher if self.instance else None)
|
creating = self.instance is None
|
||||||
student = attrs.get('student') or (self.instance.student if self.instance else None)
|
t_phone = (attrs.pop('teacher_phone', None) or '').strip()
|
||||||
|
s_phone = (attrs.pop('student_phone', None) or '').strip()
|
||||||
|
|
||||||
|
if creating and (not t_phone or not s_phone):
|
||||||
|
raise AppError('CMS_VALIDATION_ERROR', '带教老师手机号和学生手机号均必填', status_code=400)
|
||||||
|
|
||||||
|
# 按手机号解析(带教老师须 doctor、学生须 student);编辑时未传则沿用原值
|
||||||
|
teacher = self.instance.teacher if self.instance else None
|
||||||
|
student = self.instance.student if self.instance else None
|
||||||
|
if t_phone:
|
||||||
|
teacher = User.objects.filter(phone=t_phone, role_type='doctor').first()
|
||||||
|
if teacher is None:
|
||||||
|
raise AppError('CMS_REL_TEACHER_NOT_FOUND', f'带教老师不存在或非医生:{t_phone}', status_code=400)
|
||||||
|
if s_phone:
|
||||||
|
student = User.objects.filter(phone=s_phone, role_type='student').first()
|
||||||
|
if student is None:
|
||||||
|
raise AppError('CMS_REL_STUDENT_NOT_FOUND', f'学生不存在或非学生:{s_phone}', status_code=400)
|
||||||
if teacher is None or student is None:
|
if teacher is None or student is None:
|
||||||
raise AppError('CMS_VALIDATION_ERROR', '带教医生和学生均必填', status_code=400)
|
raise AppError('CMS_VALIDATION_ERROR', '带教老师和学生均必填', status_code=400)
|
||||||
|
|
||||||
if not is_super(actor):
|
if not is_super(actor):
|
||||||
if not actor.institution_id:
|
if not actor.institution_id:
|
||||||
raise AppError('CMS_NO_INSTITUTION', '当前医院管理员无所属机构', status_code=403)
|
raise AppError('CMS_NO_INSTITUTION', '当前医院管理员无所属机构', status_code=403)
|
||||||
if teacher.institution_id != actor.institution_id:
|
if teacher.institution_id != actor.institution_id:
|
||||||
raise AppError('CMS_REL_SCOPE_FORBIDDEN', '带教医生不属于本院', status_code=403)
|
raise AppError('CMS_REL_SCOPE_FORBIDDEN', '带教老师不属于本院', status_code=403)
|
||||||
if student.institution_id != actor.institution_id:
|
if student.institution_id != actor.institution_id:
|
||||||
raise AppError('CMS_REL_SCOPE_FORBIDDEN', '学生不属于本院', status_code=403)
|
raise AppError('CMS_REL_SCOPE_FORBIDDEN', '学生不属于本院', status_code=403)
|
||||||
|
|
||||||
@@ -62,6 +79,9 @@ class CmsRelationWriteSerializer(serializers.ModelSerializer):
|
|||||||
dup = dup.exclude(pk=self.instance.pk)
|
dup = dup.exclude(pk=self.instance.pk)
|
||||||
if dup.exists():
|
if dup.exists():
|
||||||
raise AppError('CMS_REL_EXISTS', '该师生关系已存在', status_code=400)
|
raise AppError('CMS_REL_EXISTS', '该师生关系已存在', status_code=400)
|
||||||
|
|
||||||
|
attrs['teacher'] = teacher
|
||||||
|
attrs['student'] = student
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class CmsRelationTest(CacheTestCase):
|
|||||||
self.assertEqual(stu_client.get(REL_URL).status_code, 403)
|
self.assertEqual(stu_client.get(REL_URL).status_code, 403)
|
||||||
|
|
||||||
def test_create_and_list(self):
|
def test_create_and_list(self):
|
||||||
resp = self.client.post(REL_URL, {'teacher': self.doc.id, 'student': self.stu.id})
|
resp = self.client.post(REL_URL, {'teacher_phone': '13932000002', 'student_phone': '13932000003'})
|
||||||
self.assertEqual(resp.status_code, 201, resp.content)
|
self.assertEqual(resp.status_code, 201, resp.content)
|
||||||
self.assertEqual(resp.json()['teacher_phone'], '13932000002')
|
self.assertEqual(resp.json()['teacher_phone'], '13932000002')
|
||||||
self.assertEqual(resp.json()['student_phone'], '13932000003')
|
self.assertEqual(resp.json()['student_phone'], '13932000003')
|
||||||
@@ -55,19 +55,25 @@ class CmsRelationTest(CacheTestCase):
|
|||||||
self.assertEqual(resp.status_code, 200)
|
self.assertEqual(resp.status_code, 200)
|
||||||
self.assertEqual(len(resp.json()['results']), 1)
|
self.assertEqual(len(resp.json()['results']), 1)
|
||||||
|
|
||||||
|
def test_create_missing_phone_400(self):
|
||||||
|
resp = self.client.post(REL_URL, {'teacher_phone': '13932000002'})
|
||||||
|
self.assertEqual(resp.status_code, 400, resp.content)
|
||||||
|
self.assertEqual(resp.json()['code'], 'CMS_VALIDATION_ERROR')
|
||||||
|
|
||||||
def test_create_duplicate(self):
|
def test_create_duplicate(self):
|
||||||
self.client.post(REL_URL, {'teacher': self.doc.id, 'student': self.stu.id})
|
self.client.post(REL_URL, {'teacher_phone': '13932000002', 'student_phone': '13932000003'})
|
||||||
resp = self.client.post(REL_URL, {'teacher': self.doc.id, 'student': self.stu.id})
|
resp = self.client.post(REL_URL, {'teacher_phone': '13932000002', 'student_phone': '13932000003'})
|
||||||
self.assertEqual(resp.status_code, 400, resp.content)
|
self.assertEqual(resp.status_code, 400, resp.content)
|
||||||
self.assertEqual(resp.json()['code'], 'CMS_REL_EXISTS')
|
self.assertEqual(resp.json()['code'], 'CMS_REL_EXISTS')
|
||||||
|
|
||||||
def test_teacher_must_be_doctor(self):
|
def test_teacher_must_be_doctor(self):
|
||||||
# 用学生当 teacher → 无效 pk
|
# 用学生手机号当带教老师 → 解析不到 doctor
|
||||||
resp = self.client.post(REL_URL, {'teacher': self.stu.id, 'student': self.stu.id})
|
resp = self.client.post(REL_URL, {'teacher_phone': '13932000003', 'student_phone': '13932000003'})
|
||||||
self.assertEqual(resp.status_code, 400, resp.content)
|
self.assertEqual(resp.status_code, 400, resp.content)
|
||||||
|
self.assertEqual(resp.json()['code'], 'CMS_REL_TEACHER_NOT_FOUND')
|
||||||
|
|
||||||
def test_scope_other_institution_student_rejected(self):
|
def test_scope_other_institution_student_rejected(self):
|
||||||
resp = self.client.post(REL_URL, {'teacher': self.doc.id, 'student': self.other_stu.id})
|
resp = self.client.post(REL_URL, {'teacher_phone': '13932000002', 'student_phone': '13932000004'})
|
||||||
self.assertEqual(resp.status_code, 403, resp.content)
|
self.assertEqual(resp.status_code, 403, resp.content)
|
||||||
self.assertEqual(resp.json()['code'], 'CMS_REL_SCOPE_FORBIDDEN')
|
self.assertEqual(resp.json()['code'], 'CMS_REL_SCOPE_FORBIDDEN')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user