diff --git a/djangoldp/admin.py b/djangoldp/admin.py
index 9211d4074cf8d035f0829c82e34fbfb5bb67e5a7..673eb8c74a35313e4dac594bc492d487fc5d6805 100644
--- a/djangoldp/admin.py
+++ b/djangoldp/admin.py
@@ -1,6 +1,9 @@
+from csv import DictWriter
 from django.contrib import admin
-from guardian.admin import GuardedModelAdmin
 from django.contrib.auth.admin import UserAdmin
+from django.core.exceptions import FieldDoesNotExist
+from django.http import HttpResponse
+from guardian.admin import GuardedModelAdmin
 from djangoldp.models import Activity, ScheduledActivity, Follower
 from djangoldp.activities.services import ActivityQueueService
 
@@ -10,10 +13,33 @@ class DjangoLDPAdmin(GuardedModelAdmin):
     An admin model representing a federated object. Inherits from GuardedModelAdmin to provide Django-Guardian
     object-level permissions
     '''
-    pass
-
-
-class DjangoLDPUserAdmin(UserAdmin, GuardedModelAdmin):
+    actions = ['export_csv']
+    export_fields = []
+
+    def resolve_verbose_name(self, field_path):
+        field = self
+        for field_name in field_path.split('__'):
+            try:
+                field = field.model._meta.get_field(field_name)
+            except FieldDoesNotExist:
+                return None
+        return field.verbose_name
+
+    @admin.action(description="Export CSV")
+    def export_csv(self, request, queryset):
+        response = HttpResponse(content_type="text/csv")
+        response['Content-Disposition'] = f'attachment; filename="{self.model.__name__}.csv"'
+        # only keep fields that can be resolved, keep only urlid if none
+        field_list = list(filter(self.resolve_verbose_name, self.export_fields or self.list_display)) or ['urlid']
+        headers = {field:self.resolve_verbose_name(field) for field in field_list}
+
+        writer = DictWriter(response, fieldnames=field_list)
+        writer.writerow(headers)
+        writer.writerows(queryset.values(*field_list))
+        return response
+
+
+class DjangoLDPUserAdmin(UserAdmin, DjangoLDPAdmin):
     '''An extension of UserAdmin providing the functionality of DjangoLDPAdmin'''
 
     list_display = ('urlid', 'email', 'first_name', 'last_name', 'date_joined', 'last_login', 'is_staff')
diff --git a/docs/create_model.md b/docs/create_model.md
index 92c0aec7642ff02d84fd20aad4eeea137b3d7258..9297ba5274fcbe142aab297e38b3c3dbdb2a1bac 100644
--- a/docs/create_model.md
+++ b/docs/create_model.md
@@ -217,6 +217,15 @@ LDPUser.circles = lambda self: Circle.objects.filter(members__user=self)
 LDPUser.circles.field = DynamicNestedField(Circle, 'circles')
 ```
 
+### Configuring CSV export
+
+DjangoLDP automaticallly provides CSV export on the admin site. By default, it exports the columns given in the `list_display` attribute. This can be overridden with the attribute `export_fields`. This setting can include fields of related object using `__`.
+
+```python
+class CustomAdmin(DjangoLDPAdmin):
+    export_fields = ['email', 'account__slug']
+```
+
 ### Improving Performance
 
 On certain endpoints, you may find that you only need a subset of fields on a model, and serializing them all is expensive (e.g. if I only need the `name` and `id` of each group chat, then why serialize all of their members?). To optimise the fields serialized, you can pass a custom header in the request, `Accept-Model-Fields`, with a `list` value of desired fields e.g. `['@id', 'name']`