diff --git a/README.rst b/README.rst
index 66b45310..7f8a32eb 100644
--- a/README.rst
+++ b/README.rst
@@ -250,6 +250,9 @@ to be serialized as the "geometry". For example:
             # as with a ModelSerializer.
             fields = ('id', 'address', 'city', 'state')
 
+If your model is geometry-less, you can set ``geo_field`` to ``None``
+and a null geometry will be produced.
+
 Using GeometrySerializerMethodField as "geo_field"
 ##################################################
 
diff --git a/rest_framework_gis/serializers.py b/rest_framework_gis/serializers.py
index 60ac9384..1b808e44 100644
--- a/rest_framework_gis/serializers.py
+++ b/rest_framework_gis/serializers.py
@@ -72,8 +72,11 @@ def __init__(self, *args, **kwargs):
             default_id_field = primary_key
         meta.id_field = getattr(meta, 'id_field', default_id_field)
 
-        if not hasattr(meta, 'geo_field') or not meta.geo_field:
-            raise ImproperlyConfigured("You must define a 'geo_field'.")
+        if not hasattr(meta, 'geo_field'):
+            raise ImproperlyConfigured(
+                "You must define a 'geo_field'. "
+                "Set it to None if there is no geometry."
+            )
 
         def check_excludes(field_name, field_role):
             """make sure the field is not excluded"""
@@ -93,7 +96,9 @@ def add_to_fields(field_name):
                     meta.fields += additional_fields
 
         check_excludes(meta.geo_field, 'geo_field')
-        add_to_fields(meta.geo_field)
+
+        if meta.geo_field is not None:
+            add_to_fields(meta.geo_field)
 
         meta.bbox_geo_field = getattr(meta, 'bbox_geo_field', None)
         if meta.bbox_geo_field:
@@ -128,12 +133,15 @@ def to_representation(self, instance):
         # must be "Feature" according to GeoJSON spec
         feature["type"] = "Feature"
 
-        # required geometry attribute
-        # MUST be present in output according to GeoJSON spec
-        field = self.fields[self.Meta.geo_field]
-        geo_value = field.get_attribute(instance)
-        feature["geometry"] = field.to_representation(geo_value)
-        processed_fields.add(self.Meta.geo_field)
+        # geometry attribute
+        # must be present in output according to GeoJSON spec
+        if self.Meta.geo_field:
+            field = self.fields[self.Meta.geo_field]
+            geo_value = field.get_attribute(instance)
+            feature["geometry"] = field.to_representation(geo_value)
+            processed_fields.add(self.Meta.geo_field)
+        else:
+            feature["geometry"] = None
 
         # Bounding Box
         # if auto_bbox feature is enabled
@@ -211,7 +219,7 @@ def unformat_geojson(self, feature):
         """
         attrs = feature["properties"]
 
-        if 'geometry' in feature:
+        if 'geometry' in feature and self.Meta.geo_field:
             attrs[self.Meta.geo_field] = feature['geometry']
 
         if self.Meta.id_field and 'id' in feature:
diff --git a/tests/django_restframework_gis_tests/migrations/0004_auto_20240228_2357.py b/tests/django_restframework_gis_tests/migrations/0004_auto_20240228_2357.py
new file mode 100644
index 00000000..ff0da97a
--- /dev/null
+++ b/tests/django_restframework_gis_tests/migrations/0004_auto_20240228_2357.py
@@ -0,0 +1,29 @@
+# Generated by Django 3.2.24 on 2024-02-28 22:57
+
+import django.contrib.gis.db.models.fields
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('django_restframework_gis_tests', '0003_schema_models'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='boxedlocation',
+            name='geometry',
+            field=django.contrib.gis.db.models.fields.GeometryField(srid=4326),
+        ),
+        migrations.AlterField(
+            model_name='locatedfile',
+            name='geometry',
+            field=django.contrib.gis.db.models.fields.GeometryField(srid=4326),
+        ),
+        migrations.AlterField(
+            model_name='location',
+            name='geometry',
+            field=django.contrib.gis.db.models.fields.GeometryField(srid=4326),
+        ),
+    ]
diff --git a/tests/django_restframework_gis_tests/models.py b/tests/django_restframework_gis_tests/models.py
index 3c8713d0..e9c044af 100644
--- a/tests/django_restframework_gis_tests/models.py
+++ b/tests/django_restframework_gis_tests/models.py
@@ -20,7 +20,6 @@ class BaseModel(models.Model):
     name = models.CharField(max_length=32)
     slug = models.SlugField(max_length=128, unique=True, blank=True)
     timestamp = models.DateTimeField(null=True, blank=True)
-    geometry = models.GeometryField()
 
     class Meta:
         abstract = True
@@ -44,15 +43,22 @@ def save(self, *args, **kwargs):
         super().save(*args, **kwargs)
 
 
-class Location(BaseModel):
+class BaseModelGeometry(BaseModel):
+    class Meta:
+        abstract = True
+
+    geometry = models.GeometryField()
+
+
+class Location(BaseModelGeometry):
     pass
 
 
-class LocatedFile(BaseModel):
+class LocatedFile(BaseModelGeometry):
     file = models.FileField(upload_to='located_files', blank=True, null=True)
 
 
-class BoxedLocation(BaseModel):
+class BoxedLocation(BaseModelGeometry):
     bbox_geometry = models.PolygonField()
 
 
diff --git a/tests/django_restframework_gis_tests/serializers.py b/tests/django_restframework_gis_tests/serializers.py
index 95ef3371..6d4f7874 100644
--- a/tests/django_restframework_gis_tests/serializers.py
+++ b/tests/django_restframework_gis_tests/serializers.py
@@ -12,6 +12,7 @@
     MultiLineStringModel,
     MultiPointModel,
     MultiPolygonModel,
+    Nullable,
     PointModel,
     PolygonModel,
 )
@@ -185,6 +186,13 @@ class Meta:
         fields = ['name', 'slug', 'id']
 
 
+class NoGeoFeatureMethodSerializer(gis_serializers.GeoFeatureModelSerializer):
+    class Meta:
+        model = Nullable
+        geo_field = None
+        fields = ['name', 'slug', 'id']
+
+
 class PointSerializer(gis_serializers.GeoFeatureModelSerializer):
     class Meta:
         model = PointModel
diff --git a/tests/django_restframework_gis_tests/tests.py b/tests/django_restframework_gis_tests/tests.py
index d4305800..948a168c 100644
--- a/tests/django_restframework_gis_tests/tests.py
+++ b/tests/django_restframework_gis_tests/tests.py
@@ -634,6 +634,16 @@ def test_geometry_serializer_method_field_none(self):
         self.assertEqual(response.data['properties']['name'], 'None value')
         self.assertEqual(response.data['geometry'], None)
 
+    def test_geometry_serializer_method_field_nogeo(self):
+        nullable = Nullable.objects.create(name='No geometry value')
+        nullable_loaded = Nullable.objects.get(pk=nullable.id)
+        self.assertEqual(nullable_loaded.name, "No geometry value")
+        url = reverse('api_geojson_nullable_details_nogeo', args=[nullable.id])
+        response = self.client.generic('GET', url, content_type='application/json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.data['properties']['name'], 'No geometry value')
+        self.assertEqual(response.data['geometry'], None)
+
     def test_nullable_empty_geometry(self):
         empty = Nullable(name='empty', geometry='POINT EMPTY')
         empty.full_clean()
diff --git a/tests/django_restframework_gis_tests/urls.py b/tests/django_restframework_gis_tests/urls.py
index 8199e773..17c61eba 100644
--- a/tests/django_restframework_gis_tests/urls.py
+++ b/tests/django_restframework_gis_tests/urls.py
@@ -22,6 +22,11 @@
         views.geojson_nullable_details,
         name='api_geojson_nullable_details',
     ),
+    path(
+        'geojson_nogeo/<int:pk>/',
+        views.geojson_nullable_details_nogeo,
+        name='api_geojson_nullable_details_nogeo',
+    ),
     path(
         'geojson_hidden/<int:pk>/',
         views.geojson_location_details_hidden,
diff --git a/tests/django_restframework_gis_tests/views.py b/tests/django_restframework_gis_tests/views.py
index 7d0107e6..cc96337b 100644
--- a/tests/django_restframework_gis_tests/views.py
+++ b/tests/django_restframework_gis_tests/views.py
@@ -23,6 +23,7 @@
     LocationGeoFeatureSlugSerializer,
     LocationGeoFeatureWritableIdSerializer,
     LocationGeoSerializer,
+    NoGeoFeatureMethodSerializer,
     NoneGeoFeatureMethodSerializer,
     PaginatedLocationGeoSerializer,
     PolygonModelSerializer,
@@ -167,6 +168,15 @@ class GeojsonLocationDetailsNone(generics.RetrieveUpdateDestroyAPIView):
 geojson_location_details_none = GeojsonLocationDetailsNone.as_view()
 
 
+class GeojsonNullableDetailsNoGeo(generics.RetrieveUpdateDestroyAPIView):
+    model = Nullable
+    serializer_class = NoGeoFeatureMethodSerializer
+    queryset = Nullable.objects.all()
+
+
+geojson_nullable_details_nogeo = GeojsonNullableDetailsNoGeo.as_view()
+
+
 class GeojsonLocationSlugDetails(generics.RetrieveUpdateDestroyAPIView):
     model = Location
     lookup_field = 'slug'