Django Views

Contents

Django Views#

This section provides detailed documentation of the Django views that implement the REST API endpoints for Nopayloaddb. These views handle HTTP requests and coordinate between the API layer and the database models.

Overview#

The Nopayloaddb API is built using Django REST Framework (DRF) and follows RESTful design principles. The views are organized by resource type and provide full CRUD (Create, Read, Update, Delete) operations where appropriate.

View Architecture
  • Class-based views for consistent behavior and easy extension

  • Django REST Framework for serialization and HTTP handling

  • Custom query optimization for complex IOV lookups

  • Bulk operation support for performance-critical operations

Key Components#

ViewSets and APIViews

Most endpoints are implemented as DRF ViewSets or APIViews for maximum flexibility.

Custom Serializers

Specialized serializers handle the complex relationships between models.

Query Optimization

Custom query methods use raw SQL for performance-critical operations.

Error Handling

Consistent error responses with detailed information for debugging.

View Reference#

The following sections provide detailed information about each view, including supported HTTP methods, parameters, and response formats.

Note

Auto-Generated Documentation: The detailed view documentation below is automatically generated from the Django view definitions and docstrings.

class cdb_rest.views.GlobalTagDetailAPIView(**kwargs)[source]#

Bases: RetrieveAPIView

serializer_class#

alias of GlobalTagReadSerializer

queryset = QuerySet#
class cdb_rest.views.GlobalTagByNameDetailAPIView(**kwargs)[source]#

Bases: RetrieveAPIView

serializer_class#

alias of GlobalTagReadSerializer

queryset = QuerySet#
lookup_url_kwarg = 'globalTagName'#
get_object()[source]#

Returns the object the view is displaying.

You may want to override this if you need to provide non-standard queryset lookups. Eg if objects are referenced using multiple keyword arguments in the url conf.

class cdb_rest.views.TimeoutListAPIView(**kwargs)[source]#

Bases: ListAPIView

list(request)[source]#
class cdb_rest.views.GlobalTagListCreationAPIView(**kwargs)[source]#

Bases: ListCreateAPIView

serializer_class#

alias of GlobalTagCreateSerializer

get_queryset()[source]#

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

list(request)[source]#
create(request, *args, **kwargs)[source]#
class cdb_rest.views.GlobalTagDeleteAPIView(**kwargs)[source]#

Bases: DestroyAPIView

serializer_class#

alias of GlobalTagReadSerializer

lookup_url_kwarg = 'globalTagName'#
lookup_field = 'name'#
get_gtag()[source]#
destroy(request, *args, **kwargs)[source]#
class cdb_rest.views.PayloadIOVDeleteAPIView(**kwargs)[source]#

Bases: DestroyAPIView

serializer_class#

alias of PayloadIOVSerializer

get_object()[source]#

Returns the object the view is displaying.

You may want to override this if you need to provide non-standard queryset lookups. Eg if objects are referenced using multiple keyword arguments in the url conf.

destroy(request, *args, **kwargs)[source]#
class cdb_rest.views.PayloadTypeDeleteAPIView(**kwargs)[source]#

Bases: DestroyAPIView

serializer_class#

alias of PayloadTypeSerializer

get_ptype()[source]#
get_plists(ptype)[source]#
destroy(request, *args, **kwargs)[source]#
class cdb_rest.views.PayloadListDeleteAPIView(**kwargs)[source]#

Bases: DestroyAPIView

serializer_class#

alias of PayloadListSerializer

get_plist()[source]#
get_piovs(plist)[source]#
destroy(request, *args, **kwargs)[source]#
class cdb_rest.views.GlobalTagsListAPIView(**kwargs)[source]#

Bases: ListAPIView

serializer_class#

alias of GlobalTagListSerializer

get_queryset()[source]#

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

list(request)[source]#
class cdb_rest.views.GlobalTagsPayloadListsListAPIView(**kwargs)[source]#

Bases: ListAPIView

get_queryset()[source]#

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

list(request, *args, **kwargs)[source]#
class cdb_rest.views.GlobalTagStatusCreationAPIView(**kwargs)[source]#

Bases: ListCreateAPIView

serializer_class#

alias of GlobalTagStatusSerializer

lookup_field = 'name'#
get_queryset()[source]#

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

list(request)[source]#
create(request, *args, **kwargs)[source]#
class cdb_rest.views.PayloadListListCreationAPIView(**kwargs)[source]#

Bases: ListCreateAPIView

serializer_class#

alias of PayloadListCreateSerializer

static get_next_id()[source]#
get_queryset()[source]#

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

list(request)[source]#
create(request, *args, **kwargs)[source]#
class cdb_rest.views.PayloadListDetailAPIView(**kwargs)[source]#

Bases: RetrieveAPIView

serializer_class#

alias of PayloadListCreateSerializer

queryset = QuerySet#
class cdb_rest.views.PayloadTypeListCreationAPIView(**kwargs)[source]#

Bases: ListCreateAPIView

serializer_class#

alias of PayloadTypeSerializer

get_queryset()[source]#

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

list(request)[source]#
create(request, *args, **kwargs)[source]#
class cdb_rest.views.PayloadIOVListCreationAPIView(**kwargs)[source]#

Bases: ListCreateAPIView

serializer_class#

alias of PayloadIOVSerializer

get_queryset()[source]#

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

list(request)[source]#
create(request, *args, **kwargs)[source]#
class cdb_rest.views.PayloadIOVDetailAPIView(**kwargs)[source]#

Bases: RetrieveAPIView

serializer_class#

alias of PayloadIOVSerializer

queryset = QuerySet#
class cdb_rest.views.PayloadIOVBulkCreationAPIView(**kwargs)[source]#

Bases: CreateAPIView

serializer_class#

alias of PayloadIOVSerializer

get_queryset()[source]#

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

create(request, *args, **kwargs)[source]#
class cdb_rest.views.GlobalTagCloneAPIView(**kwargs)[source]#

Bases: CreateAPIView

GT deep copy endpoint

serializer_class#

alias of GlobalTagReadSerializer

get_global_tag()[source]#
get_clone_name()[source]#
static get_payload_lists(global_tag)[source]#
static get_payload_iovs(payload_list)[source]#
static get_next_id()[source]#
create(request, globalTagName, cloneName)[source]#
class cdb_rest.views.PayloadIOVsORMMaxListAPIView(**kwargs)[source]#

Bases: ListAPIView

Interface to take list of PayloadIOVs grouped by PayloadLists for a given GT and IOVs

get_queryset()[source]#

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

list(request)[source]#
class cdb_rest.views.PayloadIOVsORMOrderByListAPIView(**kwargs)[source]#

Bases: ListAPIView

get_queryset()[source]#

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

list(request)[source]#
class cdb_rest.views.PayloadIOVsSQLListAPIView(**kwargs)[source]#

Bases: ListAPIView

list(request)[source]#
class cdb_rest.views.PayloadIOVsRangesListAPIView(**kwargs)[source]#

Bases: ListAPIView

Interface to take list of PayloadIOVs ranges grouped by PayloadLists for a given GT and IOVs

get_queryset()[source]#

Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.

This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.

You may want to override this if you need to provide different querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)

list(request)[source]#
class cdb_rest.views.PayloadListAttachAPIView(**kwargs)[source]#

Bases: UpdateAPIView

serializer_class#

alias of PayloadListCreateSerializer

put(request, *args, **kwargs)[source]#
class cdb_rest.views.PayloadIOVAttachAPIView(**kwargs)[source]#

Bases: UpdateAPIView

serializer_class#

alias of PayloadIOVSerializer

put(request, *args, **kwargs)[source]#
class cdb_rest.views.GlobalTagChangeStatusAPIView(**kwargs)[source]#

Bases: UpdateAPIView

serializer_class#

alias of GlobalTagCreateSerializer

get_global_tag()[source]#
get_gt_status()[source]#
put(request, *args, **kwargs)[source]#

View Categories#

Global Tag Views#

These views handle operations on Global Tags, which represent named collections of payload versions.

Supported Operations: - List all global tags - Get specific global tag by name or ID - Create new global tags - Clone existing global tags - Update global tag status - Delete global tags

Key Features: - Automatic validation of global tag names - Status management integration - Cascade operations for related payload lists

Payload Type Views#

Views for managing payload type definitions, which categorize different kinds of conditions data.

Supported Operations: - List all payload types - Create new payload types - Delete payload types (with safety checks)

Key Features: - Uniqueness validation for payload type names - Dependency checking before deletion - Integration with payload list creation

Payload List Views#

These views manage payload lists, which link global tags to payload types and serve as containers for payload versions.

Supported Operations: - List all payload lists - Get payload lists for a specific global tag - Create new payload lists - Attach payload lists to global tags - Delete payload lists

Key Features: - Automatic relationship management - Bulk operations for efficient data loading - Validation of global tag and payload type relationships

Payload IOV Views#

The most complex views handle payload IOVs (Intervals of Validity), which represent individual conditions data entries.

Supported Operations: - Query payload IOVs by global tag and IOV range - Create individual payload IOVs - Bulk create multiple payload IOVs - Attach payload IOVs to payload lists - Delete payload IOVs (single or range)

Key Features: - Optimized query methods for large datasets - Complex IOV range validation - Support for both ORM and raw SQL queries - Bulk operation support for performance

Custom Query Methods#

The views include several optimized query methods for performance-critical operations:

Main Payload Query#

The primary endpoint for retrieving conditions data uses a custom SQL query for optimal performance:

def get_payloads_by_iov(gt_name, major_iov, minor_iov, payload_type=None):
    """
    Optimized query for payload IOVs using raw SQL.

    This method uses a carefully crafted SQL query with proper
    indexing to handle large datasets efficiently.
    """
    cursor = connection.cursor()

    base_query = """
        SELECT DISTINCT pl.id, pl.name, gt.name as gt_name,
               pt.name as pt_name, piov.payload_url,
               piov.major_iov, piov.minor_iov,
               piov.created, piov.checksum, piov.size
        FROM "PayloadIOV" piov
        JOIN "PayloadList" pl ON piov.payload_list_id = pl.id
        JOIN "GlobalTag" gt ON pl.global_tag_id = gt.id
        JOIN "PayloadType" pt ON pl.payload_type_id = pt.id
        WHERE gt.name = %s AND piov.major_iov <= %s
    """

    params = [gt_name, major_iov]

    if payload_type:
        base_query += " AND pt.name = %s"
        params.append(payload_type)

    cursor.execute(base_query, params)
    return cursor.fetchall()

Alternative Query Endpoints#

Additional query endpoints provide different optimization strategies:

ORM-based Queries

For simpler operations where ORM convenience outweighs raw SQL performance.

Aggregation Queries

Specialized endpoints for getting summary information like maximum IOV values.

Filtered Queries

Pre-filtered endpoints for common query patterns.

Serializer Integration#

The views work closely with custom serializers to handle complex data transformations:

Nested Serialization#

class PayloadListSerializer(serializers.ModelSerializer):
    """Serializer for PayloadList with nested IOV data."""

    payload_iov = PayloadIOVSerializer(many=True, read_only=True)
    global_tag = serializers.StringRelatedField()
    payload_type = serializers.StringRelatedField()

    class Meta:
        model = PayloadList
        fields = ['id', 'name', 'global_tag', 'payload_type',
                 'payload_iov', 'created']

Dynamic Serialization#

Some views use dynamic serialization based on query parameters:

def get_serializer_class(self):
    """Return appropriate serializer based on request parameters."""
    if self.request.query_params.get('include_iovs'):
        return DetailedPayloadListSerializer
    return PayloadListSerializer

Validation and Error Handling#

The views implement comprehensive validation and error handling:

Request Validation#

def validate_iov_range(self, attrs):
    """Validate IOV range parameters."""
    major_start = attrs.get('major_iov')
    major_end = attrs.get('major_iov_end')

    if major_end and major_start > major_end:
        raise serializers.ValidationError(
            "IOV start must be less than or equal to IOV end"
        )

    return attrs

Error Response Format#

Consistent error responses across all endpoints:

def handle_exception(self, exc):
    """Custom exception handler for consistent error responses."""

    if isinstance(exc, ValidationError):
        return Response({
            'error': 'Validation Error',
            'code': 400,
            'details': exc.detail,
            'timestamp': timezone.now().isoformat()
        }, status=400)

    return super().handle_exception(exc)

Performance Optimizations#

The views include several performance optimizations:

Query Optimization#

Select Related

Views use select_related() to minimize database queries:

def get_queryset(self):
    return PayloadIOV.objects.select_related(
        'payload_list',
        'payload_list__global_tag',
        'payload_list__payload_type'
    )
Prefetch Related

For reverse relationships:

def get_queryset(self):
    return GlobalTag.objects.prefetch_related(
        'payloadlist_set__payloadiov_set'
    )

Caching Strategies#

QuerySet Caching

Frequently accessed data is cached at the view level:

@cached_property
def global_tags(self):
    """Cache global tag list for request duration."""
    return GlobalTag.objects.all().values('id', 'name')
Response Caching

Static endpoints use HTTP caching headers:

@cache_control(max_age=3600)  # Cache for 1 hour
def list(self, request):
    return super().list(request)

Bulk Operations#

Specialized views handle bulk operations efficiently:

class BulkPayloadIOVCreateView(APIView):
    """Create multiple payload IOVs in a single transaction."""

    def post(self, request):
        with transaction.atomic():
            serializer = PayloadIOVSerializer(
                data=request.data, many=True
            )
            if serializer.is_valid():
                # Use bulk_create for performance
                PayloadIOV.objects.bulk_create([
                    PayloadIOV(**item) for item in serializer.validated_data
                ])
                return Response({'created': len(request.data)},
                              status=201)
            return Response(serializer.errors, status=400)

Authentication and Permissions#

View-level security controls:

Authentication Classes#

class SecurePayloadIOVViewSet(ModelViewSet):
    """Secured payload IOV operations."""

    authentication_classes = [
        TokenAuthentication,
        SessionAuthentication
    ]
    permission_classes = [IsAuthenticated]

Custom Permissions#

class IsOwnerOrReadOnly(BasePermission):
    """Allow owners to edit, others to read."""

    def has_object_permission(self, request, view, obj):
        if request.method in SAFE_METHODS:
            return True
        return obj.global_tag.author == request.user.username

Testing Views#

The views include comprehensive test coverage:

API Test Cases#

class PayloadIOVViewTestCase(APITestCase):
    """Test cases for payload IOV views."""

    def setUp(self):
        """Set up test data."""
        self.gt = GlobalTag.objects.create(
            name='TestGT', author='tester'
        )

    def test_create_payload_iov(self):
        """Test creating payload IOV via API."""
        url = '/api/cdb_rest/piov'
        data = {
            'payload_url': 'test_data.root',
            'major_iov': 1000,
            'payload_list': self.payload_list.id
        }
        response = self.client.post(url, data, format='json')
        self.assertEqual(response.status_code, 201)

    def test_query_payloads(self):
        """Test querying payloads by IOV."""
        url = '/api/cdb_rest/payloadiovs/'
        params = {
            'gtName': 'TestGT',
            'majorIOV': 1000,
            'minorIOV': 999999
        }
        response = self.client.get(url, params)
        self.assertEqual(response.status_code, 200)

Performance Testing#

class PayloadIOVPerformanceTestCase(TestCase):
    """Performance tests for payload IOV operations."""

    @override_settings(DEBUG=True)
    def test_bulk_create_performance(self):
        """Test bulk creation performance."""

        # Create test data
        bulk_data = [
            {'payload_url': f'test_{i}.root', 'major_iov': i}
            for i in range(1000)
        ]

        with self.assertNumQueries(1):  # Should be single bulk query
            response = self.client.post(
                '/api/cdb_rest/bulk_piov',
                bulk_data,
                format='json'
            )
            self.assertEqual(response.status_code, 201)

Documentation Integration#

The views integrate with Django’s built-in documentation features:

API Schema Generation#

class PayloadIOVViewSet(ModelViewSet):
    """
    ViewSet for managing payload IOVs.

    Provides CRUD operations for payload intervals of validity,
    with support for complex queries by global tag and IOV range.
    """

    def list(self, request):
        """
        List payload IOVs with optional filtering.

        Query Parameters:
        - gtName: Global tag name (required)
        - majorIOV: Major IOV value (required)
        - minorIOV: Minor IOV value (required)
        - payloadType: Filter by payload type (optional)
        """
        pass

Browsable API Integration#

The views work seamlessly with DRF’s browsable API for interactive exploration:

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ],
}

Custom View Mixins#

Reusable functionality through custom mixins:

IOV Query Mixin#

class IOVQueryMixin:
    """Mixin providing IOV query functionality."""

    def get_iov_filter(self, major_iov, minor_iov):
        """Build IOV filter conditions."""
        return Q(major_iov__lte=major_iov) & (
            Q(major_iov_end__isnull=True) |
            Q(major_iov_end__gte=major_iov)
        )

    def filter_by_iov(self, queryset, major_iov, minor_iov):
        """Apply IOV filtering to queryset."""
        return queryset.filter(
            self.get_iov_filter(major_iov, minor_iov)
        )

Bulk Operation Mixin#

class BulkOperationMixin:
    """Mixin for bulk operations."""

    def bulk_create(self, request, *args, **kwargs):
        """Handle bulk creation requests."""
        serializer = self.get_serializer(data=request.data, many=True)
        if serializer.is_valid():
            with transaction.atomic():
                instances = self.perform_bulk_create(serializer)
            return Response(
                {'created': len(instances)},
                status=status.HTTP_201_CREATED
            )
        return Response(serializer.errors, status=400)

Future Enhancements#

Planned improvements to the view layer:

GraphQL Integration

Considering GraphQL endpoints for more flexible querying.

WebSocket Support

Real-time updates for conditions data changes.

Advanced Caching

Redis-based caching for high-performance scenarios.

API Versioning

URL-based versioning for backward compatibility.

Best Practices#

Development Guidelines#

View Design
  • Keep views focused on HTTP handling

  • Move business logic to model methods or services

  • Use appropriate HTTP status codes

  • Provide detailed error messages

Performance
  • Profile database queries during development

  • Use appropriate serializer depth

  • Implement pagination for large datasets

  • Consider caching for read-heavy endpoints

Testing
  • Test all HTTP methods and status codes

  • Include edge cases and error conditions

  • Test authentication and permissions

  • Performance test bulk operations

Documentation
  • Include docstrings for all views

  • Document query parameters and response formats

  • Provide usage examples

  • Keep documentation synchronized with code

See Also#