@@ -1459,6 +1459,141 @@ class XViewset(viewsets.ModelViewSet):
14591459 assert '/x/{related_field}/{id}/' in schema ['paths' ]
14601460
14611461
1462+ def test_path_parameter_with_relationships (no_warnings ):
1463+ class PathParamParent (models .Model ):
1464+ pass
1465+
1466+ class PathParamChild (models .Model ):
1467+ parent = models .ForeignKey (PathParamParent , on_delete = models .CASCADE )
1468+
1469+ class PathParamGrandChild (models .Model ):
1470+ parent = models .ForeignKey (PathParamChild , on_delete = models .CASCADE )
1471+
1472+ class PathParamChildSerializer (serializers .ModelSerializer ):
1473+ class Meta :
1474+ fields = '__all__'
1475+ model = PathParamChild
1476+
1477+ class XViewset1 (viewsets .ModelViewSet ):
1478+ serializer_class = PathParamChildSerializer
1479+ queryset = PathParamChild .objects .none ()
1480+ lookup_field = 'id'
1481+
1482+ class XViewset2 (viewsets .ModelViewSet ):
1483+ serializer_class = PathParamChildSerializer
1484+ queryset = PathParamChild .objects .none ()
1485+ lookup_field = 'parent'
1486+
1487+ class XViewset3 (viewsets .ModelViewSet ):
1488+ serializer_class = PathParamChildSerializer
1489+ queryset = PathParamChild .objects .none ()
1490+ lookup_field = 'parent__id' # Functionally the same as above
1491+
1492+ class PathParamGrandChildSerializer (serializers .ModelSerializer ):
1493+ class Meta :
1494+ fields = '__all__'
1495+ model = PathParamGrandChild
1496+
1497+ class XViewset4 (viewsets .ModelViewSet ):
1498+ serializer_class = PathParamGrandChildSerializer
1499+ queryset = PathParamGrandChild .objects .none ()
1500+ lookup_field = 'parent__parent'
1501+
1502+ class XViewset5 (viewsets .ModelViewSet ):
1503+ serializer_class = PathParamGrandChildSerializer
1504+ queryset = PathParamGrandChild .objects .none ()
1505+ lookup_field = 'parent__parent__id'
1506+
1507+ router = routers .SimpleRouter ()
1508+ router .register ('child_by_id' , XViewset1 )
1509+ router .register ('child_by_parent_id' , XViewset2 )
1510+ router .register ('child_by_parent_id_alt' , XViewset3 )
1511+ router .register ('grand_child_by_grand_parent_id' , XViewset4 )
1512+ router .register ('grand_child_by_grand_parent_id_alt' , XViewset5 )
1513+
1514+ schema = generate_schema (None , patterns = router .urls )
1515+
1516+ # Basic cases:
1517+ assert schema ['paths' ]['/child_by_id/{id}/' ]['get' ]['parameters' ][0 ] == {
1518+ 'description' : 'A unique integer value identifying this path param child.' ,
1519+ 'in' : 'path' ,
1520+ 'name' : 'id' ,
1521+ 'schema' : {'type' : 'integer' },
1522+ 'required' : True
1523+ }
1524+ assert schema ['paths' ]['/child_by_parent_id/{parent}/' ]['get' ]['parameters' ][0 ] == {
1525+ 'in' : 'path' ,
1526+ 'name' : 'parent' ,
1527+ 'schema' : {'type' : 'integer' },
1528+ 'required' : True
1529+ }
1530+
1531+ # Can we traverse relationships?
1532+ assert schema ['paths' ]['/grand_child_by_grand_parent_id/{parent__parent}/' ]['get' ]['parameters' ][0 ] == {
1533+ 'in' : 'path' ,
1534+ 'name' : 'parent__parent' ,
1535+ 'schema' : {'type' : 'integer' },
1536+ 'required' : True
1537+ }
1538+
1539+ # Explicit `__id` handling:
1540+ assert schema ['paths' ]['/grand_child_by_grand_parent_id_alt/{parent__parent__id}/' ]['get' ]['parameters' ][0 ] == {
1541+ 'description' : 'A unique integer value identifying this path param grand child.' ,
1542+ 'in' : 'path' ,
1543+ 'name' : 'parent__parent__id' ,
1544+ 'schema' : {'type' : 'integer' },
1545+ 'required' : True
1546+ }
1547+ assert schema ['paths' ]['/child_by_parent_id_alt/{parent__id}/' ]['get' ]['parameters' ][0 ] == {
1548+ 'description' : 'A unique integer value identifying this path param child.' ,
1549+ 'in' : 'path' ,
1550+ 'name' : 'parent__id' ,
1551+ 'schema' : {'type' : 'integer' },
1552+ 'required' : True
1553+ }
1554+
1555+
1556+ def test_path_parameter_with_lookups (no_warnings ):
1557+ class JournalEntry (models .Model ):
1558+ recorded_at = models .DateTimeField ()
1559+
1560+ class JournalEntrySerializer (serializers .ModelSerializer ):
1561+ class Meta :
1562+ fields = '__all__'
1563+ model = JournalEntry
1564+
1565+ class JournalEntryViewset (viewsets .ModelViewSet ):
1566+ serializer_class = JournalEntrySerializer
1567+ queryset = JournalEntry .objects .none ()
1568+ lookup_field = 'recorded_at__date'
1569+
1570+ class JournalEntryAltViewset (viewsets .ModelViewSet ):
1571+ serializer_class = JournalEntrySerializer
1572+ queryset = JournalEntry .objects .none ()
1573+ lookup_field = 'recorded_at__date'
1574+ lookup_url_kwarg = 'recorded_at'
1575+
1576+ router = routers .SimpleRouter ()
1577+ router .register ('journal' , JournalEntryViewset )
1578+ router .register ('journal_alt' , JournalEntryAltViewset )
1579+
1580+ schema = generate_schema (None , patterns = router .urls )
1581+
1582+ assert schema ['paths' ]['/journal/{recorded_at__date}/' ]['get' ]['parameters' ][0 ] == {
1583+ 'in' : 'path' ,
1584+ 'name' : 'recorded_at__date' ,
1585+ 'required' : True ,
1586+ 'schema' : {'format' : 'date-time' , 'type' : 'string' },
1587+ }
1588+
1589+ assert schema ['paths' ]['/journal_alt/{recorded_at}/' ]['get' ]['parameters' ][0 ] == {
1590+ 'in' : 'path' ,
1591+ 'name' : 'recorded_at' ,
1592+ 'required' : True ,
1593+ 'schema' : {'format' : 'date-time' , 'type' : 'string' },
1594+ }
1595+
1596+
14621597@pytest .mark .contrib ('psycopg2' )
14631598def test_multiple_choice_enum (no_warnings ):
14641599 from django .contrib .postgres .fields import ArrayField
0 commit comments