Skip to content

Examples

This page provides examples of how to use Django API Forms in different scenarios.

Configuration Settings

Django API Forms can be configured through Django settings. Here are the default settings:

# Settings for population strategies
DJANGO_API_FORMS_POPULATION_STRATEGIES = {
    'django_api_forms.fields.FormFieldList': 'django_api_forms.population_strategies.IgnoreStrategy',
    'django_api_forms.fields.FileField': 'django_api_forms.population_strategies.IgnoreStrategy',
    'django_api_forms.fields.ImageField': 'django_api_forms.population_strategies.IgnoreStrategy',
    'django_api_forms.fields.FormField': 'django_api_forms.population_strategies.IgnoreStrategy',
    'django.forms.models.ModelMultipleChoiceField': 'django_api_forms.population_strategies.IgnoreStrategy',
    'django.forms.models.ModelChoiceField': 'django_api_forms.population_strategies.ModelChoiceFieldStrategy'
}

# Default population strategy
DJANGO_API_FORMS_DEFAULT_POPULATION_STRATEGY = 'django_api_forms.population_strategies.BaseStrategy'

# Content type parsers
DJANGO_API_FORMS_PARSERS = {
    'application/json': 'json.loads',
    'application/x-msgpack': 'msgpack.loads'
}

Basic Example: Music Album API

This example demonstrates a music album API with nested data structures, field mapping, and custom validation.

JSON Request

{
  "title": "Unknown Pleasures",
  "type": "vinyl",
  "artist": {
    "_name": "Joy Division",
    "genres": [
      "rock",
      "punk"
    ],
    "members": 4
  },
  "year": 1979,
  "songs": [
    {
      "title": "Disorder",
      "duration": "3:29"
    },
    {
      "title": "Day of the Lords",
      "duration": "4:48",
      "metadata": {
        "_section": {
          "type": "ID3v2",
          "offset": 0,
          "byteLength": 2048
        },
        "header": {
          "majorVersion": 3,
          "minorRevision": 0,
          "flagsOctet": 0,
          "unsynchronisationFlag": false,
          "extendedHeaderFlag": false,
          "experimentalIndicatorFlag": false,
          "size": 2038
        }
      }
    }
  ],
  "metadata": {
    "created_at": "2019-10-21T18:57:03+0100",
    "updated_at": "2019-10-21T18:57:03+0100"
  }
}

Python Implementation

from enum import Enum

from django.core.exceptions import ValidationError
from django.forms import fields
from django.http import JsonResponse

from django_api_forms import FieldList, FormField, FormFieldList, DictionaryField, EnumField, AnyField, Form


class AlbumType(Enum):
    CD = 'cd'
    VINYL = 'vinyl'


class ArtistForm(Form):
    class Meta:
        mapping = {
            '_name': 'name'  # Map '_name' in JSON to 'name' in form
        }

    name = fields.CharField(required=True, max_length=100)
    genres = FieldList(field=fields.CharField(max_length=30))
    members = fields.IntegerField()


class SongForm(Form):
    title = fields.CharField(required=True, max_length=100)
    duration = fields.DurationField(required=False)
    metadata = AnyField(required=False)


class AlbumForm(Form):
    title = fields.CharField(max_length=100)
    year = fields.IntegerField()
    artist = FormField(form=ArtistForm)  # Nested form
    songs = FormFieldList(form=SongForm)  # List of nested forms
    type = EnumField(enum=AlbumType, required=True)
    metadata = DictionaryField(value_field=fields.DateTimeField())

    def clean_year(self):
        # Field-level validation
        if 'param' not in self.extras:
            raise ValidationError("You can use request GET params in form validation!")

        if self.cleaned_data['year'] == 1992:
            raise ValidationError("Year 1992 is forbidden!", 'forbidden-value')
        return self.cleaned_data['year']

    def clean(self):
        # Form-level validation
        if (self.cleaned_data['year'] == 1998) and (self.cleaned_data['artist']['name'] == "Nirvana"):
            raise ValidationError("Sounds like a bullshit", code='time-traveling')
        if 'param' not in self.extras:
            self.add_error(
                ('param', ),
                ValidationError("You can use extra optional arguments in form validation!", code='param-where')
            )
        return self.cleaned_data


# Django view example
def create_album(request):
    # Create form from request and pass extra parameters
    form = AlbumForm.create_from_request(request, param=request.GET.get('param'))

    if not form.is_valid():
        # Return validation errors
        return JsonResponse({"errors": form.errors}, status=400)

    # Access validated data
    album_data = form.cleaned_data

    # Do something with the data (e.g., save to database)
    # ...

    return JsonResponse({"status": "success", "id": 123})

Example: User Registration with File Upload

This example demonstrates a user registration API with profile image upload.

JSON Request

{
  "username": "johndoe",
  "email": "john@example.com",
  "password": "securepassword123",
  "confirm_password": "securepassword123",
  "profile": {
    "first_name": "John",
    "last_name": "Doe",
    "bio": "Software developer and music enthusiast",
    "avatar": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFdwI2Q4g02QAAAABJRU5ErkJggg=="
  }
}

Python Implementation

from django.core.exceptions import ValidationError
from django.forms import fields
from django.http import JsonResponse
from django.contrib.auth.models import User
from django.contrib.auth.password_validation import validate_password

from django_api_forms import Form, FormField, ImageField


class ProfileForm(Form):
    first_name = fields.CharField(max_length=30)
    last_name = fields.CharField(max_length=30)
    bio = fields.CharField(max_length=500, required=False)
    avatar = ImageField(required=False, mime=('image/jpeg', 'image/png'))


class UserRegistrationForm(Form):
    username = fields.CharField(max_length=150)
    email = fields.EmailField()
    password = fields.CharField(min_length=8)
    confirm_password = fields.CharField()
    profile = FormField(form=ProfileForm)

    def clean_username(self):
        username = self.cleaned_data['username']
        if User.objects.filter(username=username).exists():
            raise ValidationError("Username already exists")
        return username

    def clean_email(self):
        email = self.cleaned_data['email']
        if User.objects.filter(email=email).exists():
            raise ValidationError("Email already exists")
        return email

    def clean_password(self):
        password = self.cleaned_data['password']
        # Use Django's password validation
        validate_password(password)
        return password

    def clean(self):
        cleaned_data = self.cleaned_data
        if cleaned_data.get('password') != cleaned_data.get('confirm_password'):
            raise ValidationError("Passwords do not match")
        return cleaned_data

    def populate_avatar(self, user, value):
        # Custom population for avatar field
        if value and hasattr(user, 'profile'):
            filename = f"{user.username}_avatar.png"
            user.profile.avatar.save(filename, value, save=False)
        return value


def register_user(request):
    form = UserRegistrationForm.create_from_request(request)

    if not form.is_valid():
        return JsonResponse({"errors": form.errors}, status=400)

    # Create user
    user = User(
        username=form.cleaned_data['username'],
        email=form.cleaned_data['email']
    )
    user.set_password(form.cleaned_data['password'])
    user.save()

    # Create profile
    profile_data = form.cleaned_data['profile']
    profile = user.profile  # Assuming a profile is created via signal
    profile.first_name = profile_data['first_name']
    profile.last_name = profile_data['last_name']
    profile.bio = profile_data.get('bio', '')

    # Handle avatar upload
    if 'avatar' in profile_data:
        form.populate_avatar(user, profile_data['avatar'])

    profile.save()

    return JsonResponse({"status": "success", "id": user.id})

Example: API with Django Models

This example demonstrates how to use Django API Forms with Django models.

Models

from django.db import models

class Category(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

class Tag(models.Model):
    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name

class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    tags = models.ManyToManyField(Tag)
    published = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

JSON Request

{
  "title": "Introduction to Django API Forms",
  "content": "Django API Forms is a powerful library for validating API requests...",
  "category_id": 1,
  "tags": [1, 2, 3],
  "published": true
}

Python Implementation

from django.forms import fields, ModelChoiceField, ModelMultipleChoiceField
from django.http import JsonResponse

from django_api_forms import Form, BooleanField
from myapp.models import Article, Category, Tag


class ArticleForm(Form):
    title = fields.CharField(max_length=200)
    content = fields.CharField()
    category_id = ModelChoiceField(queryset=Category.objects.all())
    tags = ModelMultipleChoiceField(queryset=Tag.objects.all())
    published = BooleanField(required=False, default=False)

    def clean_title(self):
        title = self.cleaned_data['title']
        if Article.objects.filter(title=title).exists():
            raise ValidationError("An article with this title already exists")
        return title


def create_article(request):
    form = ArticleForm.create_from_request(request)

    if not form.is_valid():
        return JsonResponse({"errors": form.errors}, status=400)

    # Create article
    article = Article()
    form.populate(article)
    article.save()

    # Many-to-many relationships need to be set after save
    article.tags.set(form.cleaned_data['tags'])

    return JsonResponse({
        "status": "success",
        "id": article.id,
        "title": article.title
    })

These examples demonstrate different ways to use Django API Forms in real-world scenarios. You can adapt them to your specific needs and requirements.