﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
37339	`GenericIPAddressField` cannot be assigned `ipaddress.IPv4Address/IPv6Address` values	Thibaut Decombe	Thibaut Decombe	"Assigning a value from the stdlib `ipaddress` module to a `GenericIPAddressField` fails on save:

{{{#!python
>>> obj.ip = ipaddress.ip_address('80.15.186.250') # Is of type IPv4Address('80.15.186.250')
>>> obj.save()
  File ""/home/thibaut/Desktop/.venv/lib/python3.11/site-packages/django/db/models/base.py"", line 902, in save
    self.save_base(
  File ""/home/thibaut/Desktop/.venv/lib/python3.11/site-packages/django/db/models/base.py"", line 1008, in save_base
    updated = self._save_table(
              ^^^^^^^^^^^^^^^^^
  File ""/home/thibaut/Desktop/.venv/lib/python3.11/site-packages/django/db/models/base.py"", line 1138, in _save_table
    updated = self._do_update(
              ^^^^^^^^^^^^^^^^
  File ""/home/thibaut/Desktop/.venv/lib/python3.11/site-packages/django/db/models/base.py"", line 1203, in _do_update
    return filtered._update(values) > 0
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/thibaut/Desktop/.venv/lib/python3.11/site-packages/django/db/models/query.py"", line 1288, in _update
    return query.get_compiler(self.db).execute_sql(ROW_COUNT)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/thibaut/Desktop/.venv/lib/python3.11/site-packages/django/db/models/sql/compiler.py"", line 2060, in execute_sql
    row_count = super().execute_sql(result_type)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/thibaut/Desktop/.venv/lib/python3.11/site-packages/django/db/models/sql/compiler.py"", line 1610, in execute_sql
    sql, params = self.as_sql()
                  ^^^^^^^^^^^^^
  File ""/home/thibaut/Desktop/.venv/lib/python3.11/site-packages/django/db/backends/mysql/compiler.py"", line 49, in as_sql
    update_query, update_params = super().as_sql()
                                  ^^^^^^^^^^^^^^^^
  File ""/home/thibaut/Desktop/.venv/lib/python3.11/site-packages/django/db/models/sql/compiler.py"", line 2023, in as_sql
    val = field.get_db_prep_save(val, connection=self.connection)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/thibaut/Desktop/.venv/lib/python3.11/site-packages/django/db/models/fields/__init__.py"", line 1012, in get_db_prep_save
    return self.get_db_prep_value(value, connection=connection, prepared=False)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/thibaut/Desktop/.venv/lib/python3.11/site-packages/django/db/models/fields/__init__.py"", line 2284, in get_db_prep_value
    value = self.get_prep_value(value)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/thibaut/Desktop/.venv/lib/python3.11/site-packages/django/db/models/fields/__init__.py"", line 2291, in get_prep_value
    if value and "":"" in value:
                 ^^^^^^^^^^^^
TypeError: argument of type 'IPv4Address' is not iterable
}}}


Since `ipaddress` is the stdlib's representation of an IP address, and since it is what `validate_ipv4_address()`/`validate_ipv6_address()` already use internally, it is a natural type to hold an address in before assigning it to a model. 

This looks like an oversight rather than a deliberate restriction, since the field already accepts these values elsewhere, `to_python()` coerces non-strings, while `get_prep_value()` does not. Which means it's ok to create using this tpye, but not update

{{{#!python
>>> GenericIPAddressField().to_python(ipaddress.ip_address('80.15.186.250'))
'80.15.186.250'
>>> Bank(ip= IPv4Address('80.15.186.250')).save()                 # OK
>>> Bank.objects.update(ip=IPv4Address('80.15.186.250'))  # OK
>>> b = Bank.objects.last()
>>> b.ip  = IPv4Address('80.15.186.250')
>>> b.save()         
  File ""/home/thibaut/Desktop/.venv/lib/python3.11/site-packages/django/db/models/fields/__init__.py"", line 2291, in get_prep_value
    if value and "":"" in value:
                 ^^^^^^^^^^^^
TypeError: argument of type 'IPv4Address' is not iterable
}}}

{{{#!python
def get_prep_value(self, value):
    value = super().get_prep_value(value)
    if value is None:
        return None
    if value and "":"" in value:     # TypeError for any non-str value
        ...
    return str(value)              # unreachable for non-str values
}}}

The trailing `str(value)` suggests non-string values were meant to be handled, but the membership test runs first. 
Coercing before the colon check (or delegating to`to_python()`, as `CharField` does) is enough. 

I'd be happy to submit a fix "	Bug	assigned	Database layer (models, ORM)	6.1	Normal			Thibaut Decombe	Accepted	0	0	0	0	1	0
