Skip to main content

Custom Fields System

Ralph’s custom fields system allows you to add dynamic fields to models without modifying the database schema. This is useful for organization-specific data that doesn’t fit into the core model structure.

Overview

Custom fields are:
  • Dynamic: Created through the admin interface, no code changes required
  • Typed: Support string, integer, date, URL, and choice list types
  • Inheritable: Can be inherited from related objects
  • API-enabled: Automatically exposed in the REST API
  • Permission-controlled: Can be restricted to specific user groups

Adding Custom Fields to Models

Step 1: Add the Mixin

Mix WithCustomFieldsMixin into your model:
from django.db import models
from ralph.lib.custom_fields.models import WithCustomFieldsMixin
from ralph.lib.mixins.models import AdminAbsoluteUrlMixin

class Asset(WithCustomFieldsMixin, AdminAbsoluteUrlMixin, models.Model):
    hostname = models.CharField(max_length=255)
    barcode = models.CharField(max_length=200, unique=True)
    # ... other fields
Source: src/ralph/lib/custom_fields/models.py:212-221

Step 2: Enable Admin Integration

Mix CustomFieldValueAdminMixin into your admin class:
from ralph.admin import RalphAdmin, register
from ralph.lib.custom_fields.admin import CustomFieldValueAdminMixin
from myapp.models import Asset

@register(Asset)
class AssetAdmin(CustomFieldValueAdminMixin, RalphAdmin):
    list_display = ['hostname', 'barcode', 'status']
    # ... other admin configuration
Source: src/ralph/lib/custom_fields/admin.py:67-84

Step 3: Enable API Integration

For Django Rest Framework, mix WithCustomFieldsSerializerMixin into your serializer:
from rest_framework import serializers
from ralph.lib.custom_fields.api import WithCustomFieldsSerializerMixin
from myapp.models import Asset

class AssetSerializer(WithCustomFieldsSerializerMixin, serializers.ModelSerializer):
    class Meta:
        model = Asset
        fields = '__all__'
Source: docs/development/custom-fields.md:12-14

Field Types

Ralph supports five custom field types:

String

Plain text values:
# Field configuration in admin:
# Type: String
# Default value: "N/A"

Integer

Numeric values:
# Field configuration in admin:
# Type: Integer
# Default value: 0

Date

Date values (YYYY-MM-DD format):
# Field configuration in admin:
# Type: Date
# Default value: 2024-01-01

URL

Validated URL values:
# Field configuration in admin:
# Type: URL
# Default value: https://example.com

Choice List

Predefined choices separated by pipe (|):
# Field configuration in admin:
# Type: Choice List
# Choices: Production|Staging|Development|Testing
# Default value: Production
Source: src/ralph/lib/custom_fields/models.py:23-48

Creating Custom Fields

Custom fields are created through the admin interface at /admin/custom_fields/customfield/:
  1. Navigate to Custom Fields in the admin
  2. Click Add Custom Field
  3. Fill in the details:
    • Name: Display name (e.g., “Purchase Order Number”)
    • Attribute Name: Auto-generated slug (e.g., “purchase_order_number”)
    • Type: Select from String, Integer, Date, URL, or Choice List
    • Choices: For Choice List type, enter options separated by |
    • Default Value: Optional default value
    • Managing Group: Restrict editing to specific group (optional)
    • Use as Configuration Variable: Expose in API configuration_variables

Using Custom Fields

In Python Code

Access custom field values:
# Get all custom fields as a dictionary
asset = Asset.objects.get(id=1)
custom_data = asset.custom_fields_as_dict
# {'Purchase Order': 'PO-12345', 'Warranty Expiry': '2025-12-31'}

# Update a custom field
asset.update_custom_field('Purchase Order', 'PO-67890')

# Get configuration variables only
config_vars = asset.custom_fields_configuration_variables
Source: src/ralph/lib/custom_fields/models.py:222-242

Direct QuerySet Access

from ralph.lib.custom_fields.models import CustomField, CustomFieldValue

# Get a specific custom field
purchase_order_field = CustomField.objects.get(name='Purchase Order')

# Find all assets with a specific custom field value
assets_with_po = Asset.objects.filter(
    custom_fields__custom_field=purchase_order_field,
    custom_fields__value='PO-12345'
)

# Create a custom field value
CustomFieldValue.objects.create(
    custom_field=purchase_order_field,
    object=asset,
    value='PO-12345'
)
Source: src/ralph/lib/custom_fields/models.py:125-149

In Templates

Display custom fields in admin templates:
{% for cfv in object.custom_fields.all %}
    <div class="field">
        <label>{{ cfv.custom_field.name }}:</label>
        <span>{{ cfv.value }}</span>
    </div>
{% endfor %}

Custom Field Inheritance

Custom fields can be inherited from related objects. For example, a virtual server can inherit custom fields from its hypervisor:
class Hypervisor(WithCustomFieldsMixin, models.Model):
    name = models.CharField(max_length=255)
    # No custom_fields_inheritance needed - this is the parent

class VirtualServer(WithCustomFieldsMixin, models.Model):
    name = models.CharField(max_length=255)
    hypervisor = models.ForeignKey(Hypervisor, on_delete=models.CASCADE)
    
    # Define inheritance path
    custom_fields_inheritance = {
        'hypervisor': 'myapp.Hypervisor'
    }
Now when you access virtual_server.custom_fields.all(), it returns both:
  • Custom fields set directly on the virtual server
  • Custom fields inherited from its hypervisor
Source: src/ralph/lib/custom_fields/models.py:190-210

Clearing Inherited Values

When a parent object’s custom field is deleted, you can clear it from child objects:
# This is handled automatically when using the admin interface
hypervisor.clear_children_custom_field_value(custom_field)
Source: src/ralph/lib/custom_fields/models.py:244-265

Permission Management

Restrict custom field editing to specific groups:
# In admin, when creating/editing a custom field:
# Managing Group: IT Administrators
When a managing group is set:
  • Only users in that group can create/edit/delete values for this field
  • Other users can still view the field values
  • Useful for sensitive data like license keys or compliance information
Source: src/ralph/lib/custom_fields/models.py:75-84

Configuration Variables

Mark custom fields as configuration variables to expose them in the API:
# In admin, when creating/editing a custom field:
# Use as Configuration Variable: ✓ (checked)
These fields are then available in API responses under configuration_variables:
{
  "id": 123,
  "hostname": "server01",
  "configuration_variables": {
    "ENVIRONMENT": "production",
    "APP_VERSION": "2.1.0",
    "MONITORING_ENABLED": "true"
  }
}
This is useful for configuration management tools like Ansible or Puppet. Source: src/ralph/lib/custom_fields/models.py:86-93, src/ralph/lib/custom_fields/models.py:229-236

Admin Interface Features

Custom Field Values Summary

The admin shows a summary of all custom field values, including inherited ones:
@register(Asset)
class AssetAdmin(CustomFieldValueAdminMixin, RalphAdmin):
    # Show summary of custom field values (default: True)
    show_custom_fields_values_summary = True
The summary displays:
  • Field name
  • Current value
  • Source object (if inherited)
  • Link to source object
Source: src/ralph/lib/custom_fields/admin.py:67-133

Inline Editing

Custom fields appear as an inline formset in the change form, allowing you to:
  • Add new custom field values
  • Edit existing values
  • Delete custom field values
  • Clear inherited values (if applicable)
Source: src/ralph/lib/custom_fields/admin.py:55-65

Data Model

CustomField Model

Defines the custom field configuration:
class CustomField(models.Model):
    name = models.CharField(max_length=255, unique=True)
    attribute_name = models.SlugField(max_length=255, unique=True)  # auto-generated
    type = models.PositiveIntegerField(choices=CustomFieldTypes())
    choices = models.TextField(null=True, blank=True)  # for choice lists
    default_value = models.CharField(max_length=1000)
    managing_group = models.ForeignKey(Group, null=True, blank=True)
    use_as_configuration_variable = models.BooleanField(default=False)
Source: src/ralph/lib/custom_fields/models.py:50-95

CustomFieldValue Model

Stores the actual values:
class CustomFieldValue(models.Model):
    custom_field = models.ForeignKey(CustomField)
    value = models.CharField(max_length=1000)  # stored as string
    content_type = models.ForeignKey(ContentType)  # generic relation
    object_id = models.PositiveIntegerField()
    object = GenericForeignKey('content_type', 'object_id')
    
    class Meta:
        unique_together = ('custom_field', 'content_type', 'object_id')
Source: src/ralph/lib/custom_fields/models.py:125-149

Best Practices

  1. Use descriptive names: “Purchase Order Number” instead of “PO Num”
  2. Choose appropriate types: Use Integer for numbers, Date for dates, etc.
  3. Set defaults wisely: Provide sensible defaults to reduce data entry
  4. Use managing groups: Protect sensitive fields from unauthorized edits
  5. Mark config variables: Only mark fields as configuration variables if they’re needed by external tools
  6. Avoid overuse: Don’t replace proper model fields with custom fields for core functionality

Complete Example

Here’s a complete example of adding custom fields to an Asset model:
# models.py
from django.db import models
from ralph.lib.custom_fields.models import WithCustomFieldsMixin
from ralph.lib.mixins.models import AdminAbsoluteUrlMixin, TimeStampMixin

class Asset(WithCustomFieldsMixin, AdminAbsoluteUrlMixin, TimeStampMixin, models.Model):
    hostname = models.CharField(max_length=255)
    barcode = models.CharField(max_length=200, unique=True)
    status = models.PositiveIntegerField(choices=AssetStatus())
    
    class Meta:
        verbose_name = 'Asset'
        verbose_name_plural = 'Assets'

# admin.py
from ralph.admin import RalphAdmin, register
from ralph.lib.custom_fields.admin import CustomFieldValueAdminMixin
from myapp.models import Asset

@register(Asset)
class AssetAdmin(CustomFieldValueAdminMixin, RalphAdmin):
    list_display = ['hostname', 'barcode', 'status']
    search_fields = ['hostname', 'barcode']
    list_filter = ['status']
    
    # Show custom field summary in change form
    show_custom_fields_values_summary = True

# serializers.py (for API)
from rest_framework import serializers
from ralph.lib.custom_fields.api import WithCustomFieldsSerializerMixin
from myapp.models import Asset

class AssetSerializer(WithCustomFieldsSerializerMixin, serializers.ModelSerializer):
    class Meta:
        model = Asset
        fields = ['id', 'hostname', 'barcode', 'status']
        # custom_fields and configuration_variables are added automatically
Now you can:
  1. Create custom fields in the admin at /admin/custom_fields/customfield/
  2. Add custom field values when editing assets
  3. Access values via asset.custom_fields_as_dict in Python
  4. See custom fields in API responses automatically

Next Steps

Build docs developers (and LLMs) love