Another Modification: Add an Instructor Model and additional relationship¶
Let's further enhance our model such that any given instance of a Course (a CourseTerm) can have zero
or more Instructors. We'll:
1. Add an Instructor model.
2. Do a new migration.
3. Create new serializers and views for that Instructor model.
4. Update the existing serializers to demonstrate a second relationship for CourseTerm.
5. Add some fancy searching features that allow a client to, for instance. GET all the instructors for
a given course name:
http://localhost:8000/v1/instructors/?filter[course_name]=accounting%20for%20consultants
classInstructor(CommonModel):""" An instructor. """name=models.TextField(max_length=100,unique=True)course_terms=models.ManyToManyField('myapp.CourseTerm',related_name='instructors')classMeta:ordering=['name']def__str__(self):return'%s,%s'%(self.id,self.name)
See myapp/migrations/0004_instructor.py for the migration
and some updates to the test case fixture(myapp/fixtures/testcases.yaml).
We define a new InstructorSerializer but also add an instructorsResourceRelatedField to the
CourseTermSerializer. This demonstrates a second relationship for CourseTerm such that GET
http://localhost:8000/v1/course_terms/00d14ddb-9fb5-4cff-9954-d52fc33217e7/ now returns two flavors
of relationships and related links: course (to-one) and instructors (to-many).
Here's the new InstructorViewSet and InstructorRelationshipView:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
classInstructorViewSet(CourseBaseViewSet):""" API endpoint that allows Instructor to be viewed or edited. """queryset=Instructor.objects.all()serializer_class=InstructorSerializerclassInstructorRelationshipView(AuthnAuthzSchemaMixIn,RelationshipView):""" view for instructors.relationships """queryset=Instructor.objectsself_link_view_name='instructor-relationships'
Let's further make the InstructorViewSet more useful by adding a few filters. In this case,
we'll use the django-filter
"long form" method of specifying a FilterSet to the DjangoFilterBackend
as a means of aliasing a more complex ORM-based filter search keyword for course_name:
fromdjango_filtersimportrest_frameworkasfiltersclassInstructorFilterSet(filters.FilterSet):""" :py:class:`django_filters.rest_framework.FilterSet` for the Instructor model """# A filter "alias" for a chained search from instructor->course_term->course:# There does not appear to be a way to supply a list of `lookup_expr`'s as is allowed with the `fields` dict.#: `course_name` is an alias for the path `course_terms.course.course_name`course_name=filters.CharFilter(field_name="course_terms__course__course_name",lookup_expr="iexact")course_name__gt=filters.CharFilter(field_name="course_terms__course__course_name",lookup_expr="gt")course_name__gte=filters.CharFilter(field_name="course_terms__course__course_name",lookup_expr="gte")course_name__lt=filters.CharFilter(field_name="course_terms__course__course_name",lookup_expr="lt")course_name__lte=filters.CharFilter(field_name="course_terms__course__course_name",lookup_expr="lte")classMeta:usual_rels=('exact','lt','gt','gte','lte')model=Instructorfields={'id':usual_rels,'name':usual_rels,}
We'll also use the rest_framework.filters.SearchFilter to add a keyword search that spans multiple fields,
including via ORM paths.
1
2
3
4
5
6
7
8
classInstructorViewSet(CourseBaseViewSet):""" API endpoint that allows Instructor to be viewed or edited. """queryset=Instructor.objects.all()serializer_class=InstructorSerializerfilterset_class=InstructorFilterSetsearch_fields=('name','course_terms__course__course_name')
Using a filter we can see which named Courses have CourseTerm and Instructor instances associated with them,
but wouldn't it be cool to GET /courses/<id> and get back an instructors relationship? This means drilling
down to the CourseTerm model and then from there to the Instructor model.