Opened 5 months ago

Closed 5 months ago

#36347 closed Bug (duplicate)

bulk_create and postgis geometry field error

Reported by: David Binetti Owned by:
Component: Database layer (models, ORM) Version: 5.2
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I am trying to use bulk_create to create/update records on an existing model. One of the fields of this model is a nullable geometry field (PointField.). When I run the operation, I get an error that the geometry field is not properly formatted, although it is not one of the fields I'm updating. Sample model and query here:

class Person(models.Model):
    id = HashidAutoField(
        primary_key=True,
    )
    name = models.CharField(
        max_length=100,
        blank=False,
        validators=[
            MinLengthValidator(1),
        ]
    )
    external_id = models.IntegerField(
        blank=False,
        null=False,
    )
   geometry = models.PointField(
        null=True,
        blank=True,
   )


def load_persons(rows):
    person = Person(
        [Person(**row) for row in rows],
        update_conflicts=True,
        update_fields=[
            'name',
        ],
        unique_fields=[
            'external_id',
        ]
    )
    return persons

I then get the following error:

InternalError: parse error - invalid geometry
LINE 1: ...LL,NULL,NULL,NULL,NULL,NULL,NULL}')::tsvector[], ('{NULL,NUL...
                                                             ^
HINT:  "NU" <-- parse error at position 2 within geometry

full stacktrace:

---------------------------------------------------------------------------
InternalError_                            Traceback (most recent call last)
File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/django/db/backends/utils.py:105, in CursorWrapper._execute(self, sql, params, *ignored_wrapper_args)
    104 else:
--> 105     return self.cursor.execute(sql, params)

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/psycopg/cursor.py:97, in Cursor.execute(self, query, params, prepare, binary)
     96 except e._NO_TRACEBACK as ex:
---> 97     raise ex.with_traceback(None)
     98 return self

InternalError_: parse error - invalid geometry
LINE 1: ...LL,NULL,NULL,NULL,NULL,NULL,NULL}')::tsvector[], ('{NULL,NUL...
                                                             ^
HINT:  "NU" <-- parse error at position 2 within geometry

The above exception was the direct cause of the following exception:

InternalError                             Traceback (most recent call last)
Cell In[7], line 1
----> 1 load_persons(rows)

File ~/repos/registrar/project/app/loaders.py:18, in load_persons(rows)
     17 def load_persons(rows):
---> 18     persons = Person.objects.bulk_create(
     19         [Person(**row) for row in rows],
     20         update_conflicts=True,
     21         update_fields=[
     22             'last_name',
     23             'first_name',
     24             'middle_name',
     25             'age',
     26             'gender',
     27             'registered',
     28             'party',
     29             'status',
     30             'imported',
     31         ],
     32         unique_fields=[
     33             'external_id',
     34         ]
     35     )
     36     return persons

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/django/db/models/manager.py:87, in BaseManager._get_queryset_methods.<locals>.create_method.<locals>.manager_method(self, *args, **kwargs)
     85 @wraps(method)
     86 def manager_method(self, *args, **kwargs):
---> 87     return getattr(self.get_queryset(), name)(*args, **kwargs)

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/django/db/models/query.py:823, in QuerySet.bulk_create(self, objs, batch_size, ignore_conflicts, update_conflicts, update_fields, unique_fields)
    821 if objs_without_pk:
    822     fields = [f for f in fields if not isinstance(f, AutoField)]
--> 823     returned_columns = self._batched_insert(
    824         objs_without_pk,
    825         fields,
    826         batch_size,
    827         on_conflict=on_conflict,
    828         update_fields=update_fields,
    829         unique_fields=unique_fields,
    830     )
    831     connection = connections[self.db]
    832     if (
    833         connection.features.can_return_rows_from_bulk_insert
    834         and on_conflict is None
    835     ):

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/django/db/models/query.py:1882, in QuerySet._batched_insert(self, objs, fields, batch_size, on_conflict, update_fields, unique_fields)
   1877 for item in [objs[i : i + batch_size] for i in range(0, len(objs), batch_size)]:
   1878     if bulk_return and (
   1879         on_conflict is None or on_conflict == OnConflict.UPDATE
   1880     ):
   1881         inserted_rows.extend(
-> 1882             self._insert(
   1883                 item,
   1884                 fields=fields,
   1885                 using=self.db,
   1886                 on_conflict=on_conflict,
   1887                 update_fields=update_fields,
   1888                 unique_fields=unique_fields,
   1889                 returning_fields=self.model._meta.db_returning_fields,
   1890             )
   1891         )
   1892     else:
   1893         self._insert(
   1894             item,
   1895             fields=fields,
   (...)   1899             unique_fields=unique_fields,
   1900         )

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/django/db/models/query.py:1854, in QuerySet._insert(self, objs, fields, returning_fields, raw, using, on_conflict, update_fields, unique_fields)
   1847 query = sql.InsertQuery(
   1848     self.model,
   1849     on_conflict=on_conflict,
   1850     update_fields=update_fields,
   1851     unique_fields=unique_fields,
   1852 )
   1853 query.insert_values(fields, objs, raw=raw)
-> 1854 return query.get_compiler(using=using).execute_sql(returning_fields)

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/django/db/models/sql/compiler.py:1881, in SQLInsertCompiler.execute_sql(self, returning_fields)
   1879 with self.connection.cursor() as cursor:
   1880     for sql, params in self.as_sql():
-> 1881         cursor.execute(sql, params)
   1882     if not self.returning_fields:
   1883         return []

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/django/db/backends/utils.py:122, in CursorDebugWrapper.execute(self, sql, params)
    120 def execute(self, sql, params=None):
    121     with self.debug_sql(sql, params, use_last_executed_query=True):
--> 122         return super().execute(sql, params)

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/sentry_sdk/utils.py:1811, in ensure_integration_enabled.<locals>.patcher.<locals>.runner(*args, **kwargs)
   1808 if sentry_sdk.get_client().get_integration(integration) is None:
   1809     return original_function(*args, **kwargs)
-> 1811 return sentry_patched_function(*args, **kwargs)

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/sentry_sdk/integrations/django/__init__.py:651, in install_sql_hook.<locals>.execute(self, sql, params)
    642 with record_sql_queries(
    643     cursor=self.cursor,
    644     query=sql,
   (...)    648     span_origin=DjangoIntegration.origin_db,
    649 ) as span:
    650     _set_db_data(span, self)
--> 651     result = real_execute(self, sql, params)
    653 with capture_internal_exceptions():
    654     add_query_source(span)

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/django/db/backends/utils.py:79, in CursorWrapper.execute(self, sql, params)
     78 def execute(self, sql, params=None):
---> 79     return self._execute_with_wrappers(
     80         sql, params, many=False, executor=self._execute
     81     )

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/django/db/backends/utils.py:92, in CursorWrapper._execute_with_wrappers(self, sql, params, many, executor)
     90 for wrapper in reversed(self.db.execute_wrappers):
     91     executor = functools.partial(wrapper, executor)
---> 92 return executor(sql, params, many, context)

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/django/db/backends/utils.py:100, in CursorWrapper._execute(self, sql, params, *ignored_wrapper_args)
     98     warnings.warn(self.APPS_NOT_READY_WARNING_MSG, category=RuntimeWarning)
     99 self.db.validate_no_broken_transaction()
--> 100 with self.db.wrap_database_errors:
    101     if params is None:
    102         # params default might be backend specific.
    103         return self.cursor.execute(sql)

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/django/db/utils.py:91, in DatabaseErrorWrapper.__exit__(self, exc_type, exc_value, traceback)
     89 if dj_exc_type not in (DataError, IntegrityError):
     90     self.wrapper.errors_occurred = True
---> 91 raise dj_exc_value.with_traceback(traceback) from exc_value

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/django/db/backends/utils.py:105, in CursorWrapper._execute(self, sql, params, *ignored_wrapper_args)
    103     return self.cursor.execute(sql)
    104 else:
--> 105     return self.cursor.execute(sql, params)

File ~/.local/share/virtualenvs/registrar-D123KGkv/lib/python3.13/site-packages/psycopg/cursor.py:97, in Cursor.execute(self, query, params, prepare, binary)
     93         self._conn.wait(
     94             self._execute_gen(query, params, prepare=prepare, binary=binary)
     95         )
     96 except e._NO_TRACEBACK as ex:
---> 97     raise ex.with_traceback(None)
     98 return self

InternalError: parse error - invalid geometry
LINE 1: ...LL,NULL,NULL,NULL,NULL,NULL,NULL}')::tsvector[], ('{NULL,NUL...
                                                             ^
HINT:  "NU" <-- parse error at position 2 within geometry

Change History (1)

comment:1 by Simon Charette, 5 months ago

Resolution: duplicate
Status: newclosed

Duplicate of #36289 (Postgres bulk_create error when geometry is null or a different SRID) fixed by d9bf0d07cc13c0351b65b025d7e8063864086975 in Django 5.2.1 meant to be
released on May 7th.

Note: See TracTickets for help on using tickets.
Back to Top