PostgreSQL has this nifty feature called composite types that you can use to create your own types from the built-in PostgreSQL types. It’s a bit like hstore, only structured, which makes it great for structured data that you might reuse multiple times in a model, like addresses.
Unfortunately to date, they were pretty much a pain to use in Django. There were some older implementations for versions of Django before 1.7, but they tended to do things like create surprise new objects in the namespace, not be migrateable, and require connection to the DB at any time (i.e. during your build).
Anyway, after reading a bunch of their implementations and then the Django source code I wrote django-postgres-composite-types.
pip install django-postgres-composite-types
Then you can define a composite type declaratively:
from django.db import models
from postgres_composite_type import CompositeType
address_1 = models.CharField(max_length=255)
address_2 = models.CharField(max_length=255)
suburb = models.CharField(max_length=50)
state = models.CharField(max_length=50)
postcode = models.CharField(max_length=10)
country = models.CharField(max_length=50)
db_type = 'x_address' # Required
And use it in a model:
address = Address.Field()
The field should provide all of the things you need, including formfield etc and you can even inherit this field to extend it in your own way:
def __init__(self, in_australia=True, **kwargs):
self.in_australia = in_australia
Finally to set up the DB there is a migration operation that will create the type that you can add:
from django.db import migrations
operations = [
# Registers the type
It’s not smart enough to add it itself (can you do that?). Nor would it be smart enough to write the operations to alter a type. That would be a pretty cool trick. But it’s useful functionality all the same, especially when the alternative is creating lots of 1:1 models that are hard to work with and hard to garbage collect.
It’s still pretty early days, so the APIs are subject to change. PRs accepted of course.