首页 > 解决方案 > Django REST 框架。APITestCase。如何修复'bad_value、referenced_table_name、referenced_column_name'

问题描述

我有以下型号:

class Student(models.Model):
   first_name = models.CharField(verbose_name='student first name', max_length=64)
   last_name = models.CharField(verbose_name='student last name', max_length=64)
   email = models.EmailField()

class Meta:
    db_table = 'student'

def __str__(self):
    return self.first_name + ' ' + self.last_name

class Course(models.Model):
   name = models.CharField(max_length=255)
   description = models.TextField()
   start_date = models.DateField(null=True, blank=True, default=None)
   end_date = models.DateField(null=True, blank=True, default=None)

class Meta:
    db_table = 'course'

def __str__(self):
    return self.name

class CourseParticipant(models.Model):
    course = models.ForeignKey(Course, related_name='courses', on_delete=models.CASCADE)
    student = models.ForeignKey(Student, related_name='student_name', on_delete=models.CASCADE)
    completed = models.BooleanField(null=False, default=False)

    class Meta:
        db_table = 'course_participant'

    def __str__(self):
        return self.course, self.student

网址:

app_name = 'student'

urlpatterns = [
    path(
        'student', StudentAPIView.as_view(),
        name='list'
    ),
    path(
        'student/detail/<int:pk>/',
        StudentAPIDetailView.as_view(),
        name='detail'
    ),
    path(
        'student/assign_student_to_course',
        StudentAPIAssignToCourse.as_view(),
        name='assign_student_to_course'
    ),
    path(
        'student/assigned_to_course',
        StudentAPIAssignedToTheCourseView.as_view(),
        name='assigned_to_course_list'
    ),
    path(
        'student/assigned_to_course/detail/<int:pk>/',
        StudentAPIUnassignedFromTheCourseView.as_view(),
        name='unassigned_to_course_list'
    ),
    path(
        'student/report/<int:pk>/',
        StudentAPIPerformanceReport.as_view(),
        name='student_performance'
    )
]

意见:

import csv

from django.http import HttpResponse
from rest_framework import mixins, generics

from course.models import (
    CourseParticipant
)
from student.models import (
    Student
)
from student.serializers import (
    StudentSerializer, AssignStudentToCourseSerializer, StudentAssignedToTheCourseSerializer
)


class StudentAPIView(
    mixins.CreateModelMixin,
    generics.ListAPIView
):
    serializer_class = StudentSerializer

    def get_queryset(self):
        queryset = Student.objects.all()
        return queryset

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class StudentAPIDetailView(
    mixins.UpdateModelMixin,
    mixins.DestroyModelMixin,
    generics.RetrieveAPIView,
):
    serializer_class = StudentSerializer

    def get_queryset(self):
        query_set = Student.objects.all()
        return query_set

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def patch(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)


class StudentAPIAssignToCourse(
    generics.CreateAPIView
):
    serializer_class = AssignStudentToCourseSerializer

    def get_queryset(self):
        queryset = CourseParticipant.objects.all()
        return queryset

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class StudentAPIAssignedToTheCourseView(
    generics.ListAPIView
):
    serializer_class = StudentAssignedToTheCourseSerializer

    def get_queryset(self):
        queryset = CourseParticipant.objects.all()
        return queryset


class StudentAPIUnassignedFromTheCourseView(
    mixins.DestroyModelMixin,
    generics.RetrieveAPIView,
):
    serializer_class = StudentAssignedToTheCourseSerializer

    def get_queryset(self):
        queryset = CourseParticipant.objects.all()
        return queryset

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)


class StudentAPIPerformanceReport(
    generics.RetrieveAPIView,
):
    def get(self, request, *args, **kwargs):
        response = HttpResponse(content_type='text/csv')
        response['Content-Disposition'] = 'attachment; filename="report.csv"'

        writer = csv.writer(response)
        for student in Student.objects.filter(pk=self.kwargs['pk']):
            assigned_courses = CourseParticipant.objects.filter(student=student)
            completed_courses = assigned_courses.filter(completed=True)

            headings = (
                "student full Name",
                "number of assigned courses to student",
                "number of completed courses by student"
            )
            rows = (
                student.full_name,
                assigned_courses.count(),
                completed_courses.count()
            )

            writer.writerow(headings)
            writer.writerow(rows)

        return response

并对其进行测试:

from rest_framework import status
from rest_framework.test import APITestCase
from rest_framework.reverse import reverse as api_reverse
from course.models import CourseParticipant
from .models import Student


class StudentAPITestCase(APITestCase):
    def setUp(self):
        student_obj = Student.objects.create(
            first_name='test',
            last_name='student',
            email='test_student@gmail.com',
        )

    def create_item(self):
        url = api_reverse('student:list')
        data = {
            'first_name': 'some_first_name_for_test',
            'last_name': 'some_last_name_for_test',
            'email': 'test_student@gmail.com'

        }
        response = self.client.post(url, data, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(Student.objects.count(), 2)
        return response.data

    def test_student_create(self):
        data = self.create_item()
        data_id = data.get('id')
        rud_url = api_reverse('student:detail', kwargs={'pk': data_id})
        rud_data = {
            'first_name': 'some_new_first_name_for_test',
            'last_name': 'some_new_last_name_for_test'
        }

        get_response = self.client.get(rud_url, rud_data, format='json')
        self.assertEqual(get_response.status_code, status.HTTP_200_OK)

    def test_student_update(self):
        data = self.create_item()
        data_id = data.get('id')
        rud_url = api_reverse('student:detail', kwargs={'pk': data_id})
        rud_data = {
            'first_name': 'some_new_first_name_for_test',
            'last_name': 'some_new_last_name_for_test',
            'email': 'test_student@gmail.com'
        }

        put_response = self.client.put(rud_url, rud_data, format='json')
        self.assertEqual(put_response.status_code, status.HTTP_200_OK)

    def test_student_delete(self):
        data = self.create_item()
        data_id = data.get('id')
        rud_url = api_reverse('student:detail', kwargs={'pk': data_id})

        delete_response = self.client.delete(rud_url, data_id, format='json')
        self.assertEqual(delete_response.status_code, status.HTTP_204_NO_CONTENT)

        get_response = self.client.delete(rud_url, format='json')
        self.assertEqual(get_response.status_code, status.HTTP_404_NOT_FOUND)


class StudentAssignToCourseAPITestCase(APITestCase):
    def setUp(self):
        student_obj = CourseParticipant.objects.create(
            course_id=1,
            student_id=1,
        )

    def test_create_item(self):
        url = api_reverse('student:assign_student_to_course')
        data = {
            'course_id': 1,
            'student_id': 1,

        }
        response = self.client.post(url, data, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(Student.objects.count(), 2)
        return response.data

StudentAPITestCase 的所有测试均已通过,但 StudentAssignToCourseAPITestCase 的测试失败并带有一些 Traceback:

回溯(最近一次通话最后一次):文件“/home/leonidsoputnyak/Документы/project/test_task/venv/lib/python3.7/site-packages/django/test/testcases.py”,第 274 行,通话中 self._post_teardown() 文件“/home/leonidsoputnyak/Документы/project/test_task/venv/lib/python3.7/site-packages/django/test/testcases.py”,第 1009 行,在 _post_teardown self._fixture_teardown() 文件中“/home/leonidsoputnyak/Документы/project/test_task/venv/lib/python3.7/site-packages/django/test/testcases.py”,第 1177 行,在 _fixture_teardown 连接[db_name].check_constraints() 文件中“/home /leonidsoputnyak/Документы/project/test_task/venv/lib/python3.7/site-packages/django/db/backends/sqlite3/base.py”,第 318 行,在 check_constraints bad_value、referenced_table_name、referenced_column_name django.db.utils 中。 IntegrityError:主键为“1”的表“course_participant”中的行具有无效的外键:course_participant.student_id 包含值“1”在 student.id 中没有对应的值。

有人可以解释为什么我的 StudentAssignTo Course APITestCase 测试失败以及我该如何解决它?

标签: djangounit-testingdjango-rest-framework

解决方案


测试失败,setUp因为您尝试CourseParticipant使用不存在的 student_id 值创建方法。所以你应该首先创建UserCourse对象并使用它们的ID:

class StudentAssignToCourseAPITestCase(APITestCase):
    def setUp(self):
        self.student_obj = Student.objects.create(
            first_name='test',
            last_name='student',
            email='test_student@gmail.com',
        )
        self.course_obj = Course.objects.create(
            # couse's attribute here
        )
        student_obj = CourseParticipant.objects.create(
            course_id=self.course_obj.pk,
            student_id=self.student_obj.pk,
        )

    def test_create_item(self):
        url = api_reverse('student:assign_student_to_course')
        data = {
            'course_id': self.course_obj.pk,
            'student_id': self.student_obj.pk,

        }
        response = self.client.post(url, data, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(Student.objects.count(), 2)
        return response.data

推荐阅读