Skip to content

Migrations with Fingerprint Fields #2

@jan-grimo

Description

@jan-grimo

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!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions