Repository: netbox-community/netbox

Issue:
Return site in circuit-list API
<!--
    NOTE: This form is only for proposing specific new features or enhancements.
    If you have a general idea or question, please post to our mailing list
    instead of opening an issue:

        https://groups.google.com/forum/#!forum/netbox-discuss

    NOTE: Due to an excessive backlog of feature requests, we are not currently
    accepting any proposals which significantly extend NetBox's feature scope.

    Please describe the environment in which you are running NetBox. Be sure
    that you are running an unmodified instance of the latest stable release
    before submitting a bug report.
-->
### Environment
* Python version:  3.6.8<!-- Example: 3.5.4 -->
* NetBox version:  2.5.13<!-- Example: 2.3.6 -->

<!--
    Describe in detail the new functionality you are proposing. Include any
    specific changes to work flows, data models, or the user interface.
-->
### Proposed Functionality
Add `a_site` and `z_site` keys to output of `/circuits/circuits`
<!--
    Convey an example use case for your proposed feature. Write from the
    perspective of a NetBox user who would benefit from the proposed
    functionality and describe how.
--->
### Use Case
In the GUI when viewing the circuit listing, the table has a column that shows the site names for each termination (if it exists). This is extremely useful for filtering by site and generally viewing the circuit list.  The API for circuits does not return this matched data however. In order to get the site, you also have to call `/circuits/circuit-terminations/` as well, then pair the data up. This is a slow workflow within a script, and even more-so when trying to represent the data in an external tool.

The circuit API does allow you to filter by sites so it would also make it more manageable to know which circuit belongs to which site if you're filtering on multiple sites, without having to do even more API calls.

<!--
    Note any changes to the database schema necessary to support the new
    feature. For example, does the proposal require adding a new model or
    field? (Not all new features require database changes.)
--->
### Database Changes
N/A
<!--
    List any new dependencies on external libraries or services that this new
    feature would introduce. For example, does the proposal require the
    installation of a new Python package? (Not all new features introduce new
    dependencies.)
-->
### External Dependencies
N/A


Discussion:
If we're going to go that route, it would probably make more sense to return the whole termination rather than just the assigned site.
Fair point - I'd considered that but opted for the 'lighter' output. Having the full terminations would definitely have value as well. 

Resolving patch:
diff --git a/netbox/circuits/api/serializers.py b/netbox/circuits/api/serializers.py
--- a/netbox/circuits/api/serializers.py
+++ b/netbox/circuits/api/serializers.py
@@ -3,11 +3,11 @@
 
 from circuits.choices import CircuitStatusChoices
 from circuits.models import Provider, Circuit, CircuitTermination, CircuitType
-from dcim.api.nested_serializers import NestedCableSerializer, NestedSiteSerializer
+from dcim.api.nested_serializers import NestedCableSerializer, NestedInterfaceSerializer, NestedSiteSerializer
 from dcim.api.serializers import ConnectedEndpointSerializer
 from extras.api.customfields import CustomFieldModelSerializer
 from tenancy.api.nested_serializers import NestedTenantSerializer
-from utilities.api import ChoiceField, ValidatedModelSerializer
+from utilities.api import ChoiceField, ValidatedModelSerializer, WritableNestedSerializer
 from .nested_serializers import *
 
 
@@ -39,18 +39,30 @@ class Meta:
         fields = ['id', 'name', 'slug', 'description', 'circuit_count']
 
 
+class CircuitCircuitTerminationSerializer(WritableNestedSerializer):
+    url = serializers.HyperlinkedIdentityField(view_name='circuits-api:circuittermination-detail')
+    site = NestedSiteSerializer()
+    connected_endpoint = NestedInterfaceSerializer()
+
+    class Meta:
+        model = CircuitTermination
+        fields = ['id', 'url', 'site', 'connected_endpoint', 'port_speed', 'upstream_speed', 'xconnect_id']
+
+
 class CircuitSerializer(TaggitSerializer, CustomFieldModelSerializer):
     provider = NestedProviderSerializer()
     status = ChoiceField(choices=CircuitStatusChoices, required=False)
     type = NestedCircuitTypeSerializer()
     tenant = NestedTenantSerializer(required=False, allow_null=True)
+    termination_a = CircuitCircuitTerminationSerializer(read_only=True)
+    termination_z = CircuitCircuitTerminationSerializer(read_only=True)
     tags = TagListSerializerField(required=False)
 
     class Meta:
         model = Circuit
         fields = [
             'id', 'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description',
-            'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+            'termination_a', 'termination_z', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
         ]
 
 
diff --git a/netbox/circuits/api/views.py b/netbox/circuits/api/views.py
--- a/netbox/circuits/api/views.py
+++ b/netbox/circuits/api/views.py
@@ -62,7 +62,9 @@ class CircuitTypeViewSet(ModelViewSet):
 #
 
 class CircuitViewSet(CustomFieldModelViewSet):
-    queryset = Circuit.objects.prefetch_related('type', 'tenant', 'provider').prefetch_related('tags')
+    queryset = Circuit.objects.prefetch_related(
+        'type', 'tenant', 'provider', 'terminations__site', 'terminations__connected_endpoint'
+    ).prefetch_related('tags')
     serializer_class = serializers.CircuitSerializer
     filterset_class = filters.CircuitFilterSet
 


Test patch: