Skip to content

Views

myapp.views

log = logging.getLogger(__name__) module-attribute

usual_rels = ('exact', 'lt', 'gt', 'gte', 'lte', 'in') module-attribute

text_rels = ('icontains', 'iexact', 'contains') module-attribute

ColumbiaGroupClaimPermission

Bases: HasClaim

Use OIDC claim 'https://api.columbia.edu/claim/group' to determine permission to create/update/delete stuff: If the user has the claim demo_d_demo2, then they can do writes. Read access doesn't require a claim.

Source code in myapp/views.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class ColumbiaGroupClaimPermission(HasClaim):
    """
    Use OIDC claim 'https://api.columbia.edu/claim/group' to determine permission
    to create/update/delete stuff: If the user has the claim `demo_d_demo2`, then
    they can do writes. Read access doesn't require a claim.
    """

    #: in order to be able to do a write, the user must have claim `demo_d_demo2`
    WRITE_CLAIM = "demo_d_demo2"
    #: any user can do a read (empty string indicates so vs. None which means deny).
    READ_CLAIM = ""
    #: the name of our custom claim group
    claim = "https://api.columbia.edu/claim/group"
    #: mapping of HTTP methods to required claim group values
    claims_map = {
        "GET": READ_CLAIM,
        "HEAD": READ_CLAIM,
        "OPTIONS": READ_CLAIM,
        "POST": WRITE_CLAIM,
        "PATCH": WRITE_CLAIM,
        "DELETE": WRITE_CLAIM,
    }

WRITE_CLAIM = 'demo_d_demo2' class-attribute instance-attribute

READ_CLAIM = '' class-attribute instance-attribute

claim = 'https://api.columbia.edu/claim/group' class-attribute instance-attribute

claims_map = {'GET': READ_CLAIM, 'HEAD': READ_CLAIM, 'OPTIONS': READ_CLAIM, 'POST': WRITE_CLAIM, 'PATCH': WRITE_CLAIM, 'DELETE': WRITE_CLAIM} class-attribute instance-attribute

ColumbiaSubClaimPermission

Bases: HasClaim

Use OIDC 'sub' claim to determine if the subject is from the Columbia University OIDC service. Combine this with the preceding ColumbiaGroupClaimPermission.

Source code in myapp/views.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class ColumbiaSubClaimPermission(HasClaim):
    """
    Use OIDC 'sub' claim to determine if the subject is from the Columbia University OIDC service.
    Combine this with the preceding ColumbiaGroupClaimPermission.
    """

    claim = "sub"
    CU_CLAIM = re.compile(".+@columbia.edu$")  # sub ends in @columbia.edu
    claims_map = {
        "GET": CU_CLAIM,
        "HEAD": CU_CLAIM,
        "OPTIONS": CU_CLAIM,
        "POST": CU_CLAIM,
        "PATCH": CU_CLAIM,
        "DELETE": CU_CLAIM,
    }

claim = 'sub' class-attribute instance-attribute

CU_CLAIM = re.compile('.+@columbia.edu$') class-attribute instance-attribute

claims_map = {'GET': CU_CLAIM, 'HEAD': CU_CLAIM, 'OPTIONS': CU_CLAIM, 'POST': CU_CLAIM, 'PATCH': CU_CLAIM, 'DELETE': CU_CLAIM} class-attribute instance-attribute

DOTGroupClaimPermission

Bases: HasClaim

Use OIDC custom claim 'https://api.columbia.edu/claim/group' to determine permission to create/update/delete stuff: If the user has the claim 'team-c' then they can do writes. Read access requires 'team-a'.

With our demo environment fixture: - user1 is a member of team-a and team-c so can read and write. - user2 is a member of team-a and team-b so can only read. - user3 is not a member of any team so can't read or write.

Source code in myapp/views.py
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
class DOTGroupClaimPermission(HasClaim):
    """
    Use OIDC custom claim 'https://api.columbia.edu/claim/group' to determine permission
    to create/update/delete stuff: If the user has the claim 'team-c' then
    they can do writes. Read access requires 'team-a'.

    With our demo environment fixture:
    - user1 is a member of team-a and team-c so can read and write.
    - user2 is a member of team-a and team-b so can only read.
    - user3 is not a member of any team so can't read or write.
    """

    WRITE_CLAIM = "team-c"
    READ_CLAIM = "team-a"
    #: the name of our custom claim group
    claim = "https://api.columbia.edu/claim/group"
    #: mapping of HTTP methods to required claim group values
    claims_map = {
        "GET": READ_CLAIM,
        "HEAD": READ_CLAIM,
        "OPTIONS": READ_CLAIM,
        "POST": WRITE_CLAIM,
        "PATCH": WRITE_CLAIM,
        "DELETE": WRITE_CLAIM,
    }

WRITE_CLAIM = 'team-c' class-attribute instance-attribute

READ_CLAIM = 'team-a' class-attribute instance-attribute

claim = 'https://api.columbia.edu/claim/group' class-attribute instance-attribute

claims_map = {'GET': READ_CLAIM, 'HEAD': READ_CLAIM, 'OPTIONS': READ_CLAIM, 'POST': WRITE_CLAIM, 'PATCH': WRITE_CLAIM, 'DELETE': WRITE_CLAIM} class-attribute instance-attribute

DOTSubClaimPermission

Bases: HasClaim

Use OIDC 'sub' claim to determine if the subject is from a service where the sub does not contain '@'. This is probably the local DOT but could be an external AS too. (It would be nice if 'iss' where part of a standard Userinfo response, but that is not the case.) Combine this with the preceding DOTGroupClaimPermission.

Source code in myapp/views.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
class DOTSubClaimPermission(HasClaim):
    """
    Use OIDC 'sub' claim to determine if the subject is from a service where the sub does not
    contain '@'. This is probably the local DOT but could be an external AS too.
    (It would be nice if 'iss' where part of a standard Userinfo response, but that is not the case.)
    Combine this with the preceding DOTGroupClaimPermission.
    """

    claim = "sub"
    DOT_CLAIM = re.compile("^((?!@).)*$")  # sub does not contain "@"
    claims_map = {
        "GET": DOT_CLAIM,
        "HEAD": DOT_CLAIM,
        "OPTIONS": DOT_CLAIM,
        "POST": DOT_CLAIM,
        "PATCH": DOT_CLAIM,
        "DELETE": DOT_CLAIM,
    }

claim = 'sub' class-attribute instance-attribute

DOT_CLAIM = re.compile('^((?!@).)*$') class-attribute instance-attribute

claims_map = {'GET': DOT_CLAIM, 'HEAD': DOT_CLAIM, 'OPTIONS': DOT_CLAIM, 'POST': DOT_CLAIM, 'PATCH': DOT_CLAIM, 'DELETE': DOT_CLAIM} class-attribute instance-attribute

MyDjangoModelPermissions

Bases: DjangoModelPermissions

Override DjangoModelPermissions <https://docs.djangoproject.com/en/dev/topics/auth/#permissions>_ to require view permission as well: The default allows view by anybody.

Source code in myapp/views.py
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
class MyDjangoModelPermissions(DjangoModelPermissions):
    """
    Override `DjangoModelPermissions <https://docs.djangoproject.com/en/dev/topics/auth/#permissions>`_
    to require view permission as well: The default allows view by anybody.
    """

    #: the usual permissions map plus GET. Also, we omit PUT since we only use PATCH with {json:api}.
    perms_map = {
        "GET": ["%(app_label)s.view_%(model_name)s"],
        "OPTIONS": ["%(app_label)s.view_%(model_name)s"],
        "HEAD": ["%(app_label)s.view_%(model_name)s"],
        "POST": ["%(app_label)s.add_%(model_name)s"],
        # PUT not allowed by JSON:API; use PATCH
        # 'PUT': ['%(app_label)s.change_%(model_name)s'],
        "PATCH": ["%(app_label)s.change_%(model_name)s"],
        "DELETE": ["%(app_label)s.delete_%(model_name)s"],
    }

perms_map = {'GET': ['%(app_label)s.view_%(model_name)s'], 'OPTIONS': ['%(app_label)s.view_%(model_name)s'], 'HEAD': ['%(app_label)s.view_%(model_name)s'], 'POST': ['%(app_label)s.add_%(model_name)s'], 'PATCH': ['%(app_label)s.change_%(model_name)s'], 'DELETE': ['%(app_label)s.delete_%(model_name)s']} class-attribute instance-attribute

AuthnAuthzMixIn

Bases: object

Common Authn/Authz mixin for all View and ViewSet-derived classes:

Source code in myapp/views.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
class AuthnAuthzMixIn(object):
    """
    Common Authn/Authz mixin for all View and ViewSet-derived classes:
    """

    #: In production Oauth2 is preferred; Allow Basic and Session for testing and browseable API.
    #: (authentication_classes is an implied OR list)
    authentication_classes = (MyOAuth2Auth,)
    #: permissions are any one of, each requiring demo-djt-sla-* scope:
    #: 1. Authenticated Columbia user: auth-columbia scope plus required user claims.
    #: 2. Authenticated DOT user: scopes as above plus DOT required user claims.
    #: 3. Client Credentials (backend-to-backend): auth-none. No auth user. Claims don't exist.
    permission_classes = [
        TokenMatchesOASRequirements
        & (
            (IsAuthenticated & ColumbiaGroupClaimPermission & ColumbiaSubClaimPermission)
            | (IsAuthenticated & DOTGroupClaimPermission & DOTSubClaimPermission)
            # | (IsAuthenticated & DOTSubClaimPermission)
            | (~IsAuthenticated)
        )
    ]
    #: Implicit/Authorization Code scopes: user via frontend client
    USER_SCOPES = [
        "auth-columbia",
        "demo-djt-sla-bronze",
        "openid",
        "https://api.columbia.edu/scope/group",
    ]
    #: Client Credentials scopes: backend-to-backend
    BACKEND_SCOPES = ["auth-none", "demo-djt-sla-bronze"]
    #: allow either USER_SCOPES or BACKEND_SCOPES
    required_alternate_scopes = {
        "OPTIONS": [["read"]],
        "HEAD": [USER_SCOPES + ["read"], BACKEND_SCOPES + ["read"]],
        "GET": [USER_SCOPES + ["read"], BACKEND_SCOPES + ["read"]],
        "POST": [USER_SCOPES + ["create"], BACKEND_SCOPES + ["create"]],
        "PATCH": [USER_SCOPES + ["update"], BACKEND_SCOPES + ["update"]],
        "DELETE": [USER_SCOPES + ["delete"], BACKEND_SCOPES + ["delete"]],
    }

authentication_classes = (MyOAuth2Auth,) class-attribute instance-attribute

permission_classes = [TokenMatchesOASRequirements & (IsAuthenticated & ColumbiaGroupClaimPermission & ColumbiaSubClaimPermission | IsAuthenticated & DOTGroupClaimPermission & DOTSubClaimPermission | ~IsAuthenticated)] class-attribute instance-attribute

USER_SCOPES = ['auth-columbia', 'demo-djt-sla-bronze', 'openid', 'https://api.columbia.edu/scope/group'] class-attribute instance-attribute

BACKEND_SCOPES = ['auth-none', 'demo-djt-sla-bronze'] class-attribute instance-attribute

required_alternate_scopes = {'OPTIONS': [['read']], 'HEAD': [USER_SCOPES + ['read'], BACKEND_SCOPES + ['read']], 'GET': [USER_SCOPES + ['read'], BACKEND_SCOPES + ['read']], 'POST': [USER_SCOPES + ['create'], BACKEND_SCOPES + ['create']], 'PATCH': [USER_SCOPES + ['update'], BACKEND_SCOPES + ['update']], 'DELETE': [USER_SCOPES + ['delete'], BACKEND_SCOPES + ['delete']]} class-attribute instance-attribute

CourseBaseViewSet

Bases: AuthnAuthzMixIn, ModelViewSet

Base ViewSet for all our ViewSets:

  • Adds Authn/Authz
Source code in myapp/views.py
170
171
172
173
174
175
class CourseBaseViewSet(AuthnAuthzMixIn, ModelViewSet):
    """
    Base ViewSet for all our ViewSets:

    - Adds Authn/Authz
    """

CourseViewSet

Bases: CourseBaseViewSet

Source code in myapp/views.py
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
class CourseViewSet(CourseBaseViewSet):
    __doc__ = Course.__doc__
    queryset = Course.objects.all()
    serializer_class = CourseSerializer
    #: See https://docs.djangoproject.com/en/stable/ref/models/querysets/#field-lookups for all the possible filters.
    filterset_fields = {
        "id": usual_rels,
        "subject_area_code": usual_rels,
        "course_name": ("exact",) + text_rels,
        "course_description": text_rels + usual_rels,
        "course_identifier": text_rels + usual_rels,
        "course_number": ("exact",),
        "course_terms__term_identifier": usual_rels,
        "school_bulletin_prefix_code": ("exact", "regex"),
    }
    #: Keyword searches are across these fields.
    search_fields = (
        "course_name",
        "course_description",
        "course_identifier",
        "course_number",
    )
    #: Constrain sorting to only these fields:
    ordering_fields = [
        "course_name",
        "course_identifier",
        "course_number",
        "subject_area_code",
        "school_bulletin_prefix_code",
    ]

__doc__ = Course.__doc__ class-attribute instance-attribute

queryset = Course.objects.all() class-attribute instance-attribute

serializer_class = CourseSerializer class-attribute instance-attribute

filterset_fields = {'id': usual_rels, 'subject_area_code': usual_rels, 'course_name': ('exact',) + text_rels, 'course_description': text_rels + usual_rels, 'course_identifier': text_rels + usual_rels, 'course_number': ('exact',), 'course_terms__term_identifier': usual_rels, 'school_bulletin_prefix_code': ('exact', 'regex')} class-attribute instance-attribute

search_fields = ('course_name', 'course_description', 'course_identifier', 'course_number') class-attribute instance-attribute

ordering_fields = ['course_name', 'course_identifier', 'course_number', 'subject_area_code', 'school_bulletin_prefix_code'] class-attribute instance-attribute

CourseTermViewSet

Bases: CourseBaseViewSet

Source code in myapp/views.py
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
class CourseTermViewSet(CourseBaseViewSet):
    __doc__ = CourseTerm.__doc__
    queryset = CourseTerm.objects.all()
    serializer_class = CourseTermSerializer
    #: defined filter[] names
    filterset_fields = {
        "id": usual_rels,
        "term_identifier": usual_rels,
        "audit_permitted_code": ["exact"],
        "exam_credit_flag": ["exact"],
        "course__id": usual_rels,
    }
    #: Keyword searches are just this one field.
    search_fields = ("term_identifier",)
    ordering_fields = [
        "term_identifier",
        "audit_permitted_code",
        "exam_credit_flag",
    ]

__doc__ = CourseTerm.__doc__ class-attribute instance-attribute

queryset = CourseTerm.objects.all() class-attribute instance-attribute

serializer_class = CourseTermSerializer class-attribute instance-attribute

filterset_fields = {'id': usual_rels, 'term_identifier': usual_rels, 'audit_permitted_code': ['exact'], 'exam_credit_flag': ['exact'], 'course__id': usual_rels} class-attribute instance-attribute

search_fields = ('term_identifier',) class-attribute instance-attribute

ordering_fields = ['term_identifier', 'audit_permitted_code', 'exam_credit_flag'] class-attribute instance-attribute

PersonViewSet

Bases: CourseBaseViewSet

Source code in myapp/views.py
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
class PersonViewSet(CourseBaseViewSet):
    __doc__ = Person.__doc__
    queryset = Person.objects.all()
    serializer_class = PersonSerializer
    filterset_fields = {}
    search_fields = ("name", "instructor__course_terms__course__course_name")
    ordering_fields = ["name"]

    class Meta:
        """
        In addition to specific filters defined above, also generate some automatic filters.
        """

        model = Person
        fields = {
            "id": usual_rels,
            "name": usual_rels,
        }

__doc__ = Person.__doc__ class-attribute instance-attribute

queryset = Person.objects.all() class-attribute instance-attribute

serializer_class = PersonSerializer class-attribute instance-attribute

filterset_fields = {} class-attribute instance-attribute

search_fields = ('name', 'instructor__course_terms__course__course_name') class-attribute instance-attribute

ordering_fields = ['name'] class-attribute instance-attribute

Meta

In addition to specific filters defined above, also generate some automatic filters.

Source code in myapp/views.py
243
244
245
246
247
248
249
250
251
252
class Meta:
    """
    In addition to specific filters defined above, also generate some automatic filters.
    """

    model = Person
    fields = {
        "id": usual_rels,
        "name": usual_rels,
    }
model = Person class-attribute instance-attribute
fields = {'id': usual_rels, 'name': usual_rels} class-attribute instance-attribute

InstructorFilterSet

Bases: FilterSet

Extend class django_filters.rest_framework.FilterSet for the Instructor model

Includes a filter "alias" for a chained search from instructor->course_term->course

Source code in myapp/views.py
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
class InstructorFilterSet(filters.FilterSet):
    """
    Extend class `django_filters.rest_framework.FilterSet` for the Instructor model

    Includes a filter "alias" for a chained search from instructor->course_term->course
    """

    #: `filter[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"
    )
    #: `filter[course_name_gt]` for greater-than, etc.
    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"
    )
    #: `filter[name]` is an alias for the path `course_terms.instructor.person.name`
    name = filters.CharFilter(
        field_name="course_terms__instructor__person__name", lookup_expr="iexact"
    )
    #: `filter[name_gt]` for greater-than, etc.
    name__gt = filters.CharFilter(
        field_name="course_terms__instructor__person__name", lookup_expr="gt"
    )
    name__gte = filters.CharFilter(
        field_name="course_terms__instructor__person__name", lookup_expr="gte"
    )
    name__lt = filters.CharFilter(
        field_name="course_terms__instructor__person__name", lookup_expr="lt"
    )
    name__lte = filters.CharFilter(
        field_name="course_terms__instructor__person__name", lookup_expr="lte"
    )

    class Meta:
        """
        In addition to specific filters defined above, also generate some automatic filters.
        """

        model = Instructor
        fields = {
            "id": usual_rels,
        }

course_name = filters.CharFilter(field_name='course_terms__course__course_name', lookup_expr='iexact') class-attribute instance-attribute

course_name__gt = filters.CharFilter(field_name='course_terms__course__course_name', lookup_expr='gt') class-attribute instance-attribute

course_name__gte = filters.CharFilter(field_name='course_terms__course__course_name', lookup_expr='gte') class-attribute instance-attribute

course_name__lt = filters.CharFilter(field_name='course_terms__course__course_name', lookup_expr='lt') class-attribute instance-attribute

course_name__lte = filters.CharFilter(field_name='course_terms__course__course_name', lookup_expr='lte') class-attribute instance-attribute

name = filters.CharFilter(field_name='course_terms__instructor__person__name', lookup_expr='iexact') class-attribute instance-attribute

name__gt = filters.CharFilter(field_name='course_terms__instructor__person__name', lookup_expr='gt') class-attribute instance-attribute

name__gte = filters.CharFilter(field_name='course_terms__instructor__person__name', lookup_expr='gte') class-attribute instance-attribute

name__lt = filters.CharFilter(field_name='course_terms__instructor__person__name', lookup_expr='lt') class-attribute instance-attribute

name__lte = filters.CharFilter(field_name='course_terms__instructor__person__name', lookup_expr='lte') class-attribute instance-attribute

Meta

In addition to specific filters defined above, also generate some automatic filters.

Source code in myapp/views.py
297
298
299
300
301
302
303
304
305
class Meta:
    """
    In addition to specific filters defined above, also generate some automatic filters.
    """

    model = Instructor
    fields = {
        "id": usual_rels,
    }
model = Instructor class-attribute instance-attribute
fields = {'id': usual_rels} class-attribute instance-attribute

InstructorViewSet

Bases: CourseBaseViewSet

Source code in myapp/views.py
308
309
310
311
312
313
314
315
316
317
class InstructorViewSet(CourseBaseViewSet):
    __doc__ = Instructor.__doc__
    queryset = Instructor.objects.all()
    serializer_class = InstructorSerializer
    filterset_class = InstructorFilterSet
    search_fields = (
        "person__name",
        "course_terms__course__course_name",
        "course_terms__course__course_identifier"
    )

__doc__ = Instructor.__doc__ class-attribute instance-attribute

queryset = Instructor.objects.all() class-attribute instance-attribute

serializer_class = InstructorSerializer class-attribute instance-attribute

filterset_class = InstructorFilterSet class-attribute instance-attribute

search_fields = ('person__name', 'course_terms__course__course_name', 'course_terms__course__course_identifier') class-attribute instance-attribute

GradeViewSet

Bases: CourseBaseViewSet

Source code in myapp/views.py
320
321
322
323
324
325
326
327
328
329
330
331
class GradeViewSet(CourseBaseViewSet):
    __doc__ = Grade.__doc__
    queryset = Grade.objects.all()
    serializer_class = GradeSerializer
    filterset_fields = {
        "id": usual_rels,
        # 'term_identifier': usual_rels,
        # 'audit_permitted_code': ['exact'],
        # 'exam_credit_flag': ['exact'],
        # 'course__id': usual_rels,
    }
    search_fields = ("person__name", "course_terms__course__course_name")

__doc__ = Grade.__doc__ class-attribute instance-attribute

queryset = Grade.objects.all() class-attribute instance-attribute

serializer_class = GradeSerializer class-attribute instance-attribute

filterset_fields = {'id': usual_rels} class-attribute instance-attribute

search_fields = ('person__name', 'course_terms__course__course_name') class-attribute instance-attribute

NonModelViewSet

Bases: CourseBaseViewSet

We call this a NonModel but it's really a model that has no backing database. This allows us to take advantage of all the model-based features.

Source code in myapp/views.py
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
class NonModelViewSet(CourseBaseViewSet):
    """
    We call this a NonModel but it's really a model that has no backing database.
    This allows us to take advantage of all the model-based features.
    """

    serializer_class = NonModelSerializer
    http_method_names = ["get", "head", "options"]
    description = "this is a demo"
    queryset = NonModel.objects

    def retrieve(self, request, pk, *args, **kwargs):
        foo = NonModel(id="123", field1="hi there")
        serializer_instance = self.get_serializer(foo, context={"request": request})
        return Response(serializer_instance.data)

    def list(self, request, *args, **kwargs):
        # instead of the queryset coming from a database Model, create it right here:
        # TODO: Make this actually base the querset on query parameters.
        self.queryset = [NonModel(field1=f"hi there {k}") for k in range(100)]
        # queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(self.queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer_instance = self.get_serializer(
            self.queryset, many=True, context={"request": request}
        )
        return Response(serializer_instance.data)

serializer_class = NonModelSerializer class-attribute instance-attribute

http_method_names = ['get', 'head', 'options'] class-attribute instance-attribute

description = 'this is a demo' class-attribute instance-attribute

queryset = NonModel.objects class-attribute instance-attribute

retrieve(request, pk, *args, **kwargs)

Source code in myapp/views.py
346
347
348
349
def retrieve(self, request, pk, *args, **kwargs):
    foo = NonModel(id="123", field1="hi there")
    serializer_instance = self.get_serializer(foo, context={"request": request})
    return Response(serializer_instance.data)

list(request, *args, **kwargs)

Source code in myapp/views.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
def list(self, request, *args, **kwargs):
    # instead of the queryset coming from a database Model, create it right here:
    # TODO: Make this actually base the querset on query parameters.
    self.queryset = [NonModel(field1=f"hi there {k}") for k in range(100)]
    # queryset = self.filter_queryset(self.get_queryset())

    page = self.paginate_queryset(self.queryset)
    if page is not None:
        serializer = self.get_serializer(page, many=True)
        return self.get_paginated_response(serializer.data)

    serializer_instance = self.get_serializer(
        self.queryset, many=True, context={"request": request}
    )
    return Response(serializer_instance.data)

CourseRelationshipView

Bases: AuthnAuthzMixIn, RelationshipView

View for courses.relationships

Source code in myapp/views.py
371
372
373
374
375
376
377
class CourseRelationshipView(AuthnAuthzMixIn, RelationshipView):
    """
    View for courses.relationships
    """

    queryset = Course.objects
    self_link_view_name = "course-relationships"

queryset = Course.objects class-attribute instance-attribute

CourseTermRelationshipView

Bases: AuthnAuthzMixIn, RelationshipView

View for course_terms.relationships

Source code in myapp/views.py
380
381
382
383
384
385
386
class CourseTermRelationshipView(AuthnAuthzMixIn, RelationshipView):
    """
    View for course_terms.relationships
    """

    queryset = CourseTerm.objects
    self_link_view_name = "course_term-relationships"

queryset = CourseTerm.objects class-attribute instance-attribute

InstructorRelationshipView

Bases: AuthnAuthzMixIn, RelationshipView

View for instructors.relationships

Source code in myapp/views.py
389
390
391
392
393
394
395
class InstructorRelationshipView(AuthnAuthzMixIn, RelationshipView):
    """
    View for instructors.relationships
    """

    queryset = Instructor.objects
    self_link_view_name = "instructor-relationships"

queryset = Instructor.objects class-attribute instance-attribute

PersonRelationshipView

Bases: AuthnAuthzMixIn, RelationshipView

View for people.relationships

Source code in myapp/views.py
398
399
400
401
402
403
404
class PersonRelationshipView(AuthnAuthzMixIn, RelationshipView):
    """
    View for people.relationships
    """

    queryset = Person.objects
    self_link_view_name = "person-relationships"

queryset = Person.objects class-attribute instance-attribute

GradeRelationshipView

Bases: AuthnAuthzMixIn, RelationshipView

View for grades.relationships

Source code in myapp/views.py
407
408
409
410
411
412
413
class GradeRelationshipView(AuthnAuthzMixIn, RelationshipView):
    """
    View for grades.relationships
    """

    queryset = Grade.objects
    self_link_view_name = "grade-relationships"

queryset = Grade.objects class-attribute instance-attribute