I've devised a DRY method of declaring django fieldsets:
** Example usage: **
1. Include the attached code in `fieldsets.py`
2. `models.py`:
        from django.db import models
        from fieldsets import Fieldset, ModelWithFieldsets
        
        class Person(ModelWithFieldsets): #instead of models.Model
            # this field will be placed in nameless fieldset
            example_field = models.IntegerField() 
            
            # this fieldset will be grouped into one row
            Fieldset(grouped=True) 
            first_name = models.CharField(max_length=64)
            surname = models.CharField(max_length=64)
            
            Fieldset("Contact Details",  classes=('collapse',)) 
            mobile_phone = models.CharField(max_length=10)
            email_address = models.EmailField()
            
            Fieldset("Address")
            street_address = models.CharField(max_length=255)
            # the next two fields will be grouped into one row of this fieldset
            Fieldset.new_group(2) 
            suburb = models.CharField(max_length=64)
            state = models.CharField(max_length=64)
3. `admin.py`:
        from django.contrib import admin
        from models import Person
        from fieldsets import Fieldset
    
        class PersonAdmin(admin.ModelAdmin):
            fieldsets = Fieldset.get_fieldsets(Person)
    
        admin.site.register(Person, PersonAdmin)
This example produces the equivalent of manually typing:
    fieldsets = (
        (None, {'fields': ('example_field')}), 
        (None, {'fields': (('first_name', 'surname'),)}), 
        ('Contact Details', {
                'fields': ('mobile_phone', 'email_address'), 
                'classes': ('collapse',)}), 
        ('Address', {'fields': ('street_address', ('suburb', 'state'))})
    )
But now if you want to rearrange your fields, rename, delete, insert, etc, you won't need to remember to update the fieldsets in the ModelAdmin.
This implementation is a bit of a hack, but I believe a cleaner equivalent should be implemented in django itself.