django-rdkit
django-rdkit copied to clipboard
Migrations with Fingerprint Fields
I recently tried to add a fingerprint field to a model and then perform a migration. Following the documentation, which suggests
class django_rdkit.models.BfpField(**options)
A bit vector fingerprint. It may be assigned using an ExplicitBitVect instance or with an update query using one of the implemented fingerprint functions.
I attempted to set a default using an instance of ExplicitBitVect
like so:
class Compound(models.Model):
[...]
morganbv_fp = models.BfpField(default=ExplicitBitVect(1))
to which Django's makemigrations
complains that ExplicitBitVect
is unserializable. Since it is initializable with an unsigned integer which Django can serialize just fine, I derived the class and decorated it (see Django Docs Migration Serializing):
from django.utils.deconstruct import deconstructible
[...]
@deconstructible
class DeconExplicitBitVect(ExplicitBitVect):
pass
[...]
class Compound(models.Model):
[...]
morganbv_fp = models.BfpField(default=DeconExplicitBitVect(1))
This allows the class to be serialized and makemigrations
passes, but migrate
fails due to:
django.db.utils.ProgrammingError: column "morganbv_fp" is of type bfp but default expression is of type bytea
The SQL expression generated by makemigrations
is:
BEGIN;
--
-- Add field morganbv_fp to compound
--
ALTER TABLE "rest_compound" ADD COLUMN "morganbv_fp" bfp DEFAULT '\000'::bytea NOT NULL;
ALTER TABLE "rest_compound" ALTER COLUMN "morganbv_fp" DROP DEFAULT;
COMMIT;
Which is of course nonsense. Now I don't know where exactly this would be corrected, but I suspect it's due to models/fields.py:
[...]
class BfpField(Field):
[...]
def get_prep_value(self, value):
# convert the ExplicitBitVect instance to the value used by the
# db driver
if isinstance(value, ExplicitBitVect):
value = six.memoryview(DataStructs.BitVectToBinaryText(value))
return value
[...]
I fixed it by just setting the field to be nullable and the default null. It doesn't really matter whether I fill it with a nonsensical sfp instance or just allow it to be null in practice to me.
Is there a better way I completely missed? Otherwise I would recommend adapting the documentation to suggest allowing the fingerprint fields to be null in order for migrations to work.
Cheers and thanks for your time!