django-rdkit icon indicating copy to clipboard operation
django-rdkit copied to clipboard

Migrations with Fingerprint Fields

Open jan-grimo opened this issue 8 years ago • 0 comments

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!

jan-grimo avatar Apr 07 '16 07:04 jan-grimo