Python 如何处理/映射自定义postgresql类型到django模型

Python 如何处理/映射自定义postgresql类型到django模型,python,django,postgresql,rdkit,Python,Django,Postgresql,Rdkit,我正在使用rdkit,这是一个化学信息工具包,它提供了一个postgresql盒带,允许存储化学分子。 我想创建一个django模型,如下所示: from rdkit.Chem import Mol class compound(models.Model): internal = models.CharField(max_length=10 ,db_index=True) external = models.CharField(max_length=15,db_index=Tr

我正在使用rdkit,这是一个化学信息工具包,它提供了一个postgresql盒带,允许存储化学分子。 我想创建一个django模型,如下所示:

from rdkit.Chem import Mol

class compound(models.Model):
    internal = models.CharField(max_length=10 ,db_index=True)
    external = models.CharField(max_length=15,db_index=True)
    smiles   = models.TextField()
    # This is my proposed custom "mol" type defined by rdkit cartridge and that probably maps
    # to the Mol object imported from rdkit.Chem
    rdkit_mol = models.MyCustomMolField()
所以我想把“rdkit_mol”映射到rdkit postgres数据库的catridge类型“mol”。在SQL中,“mol”列是使用如下语法从“smiles”字符串创建的

postgres@compounds=# insert into compound (smiles,rdkit_mol,internal,external)  VALUES ('C1=CC=C[N]1',mol_from_smiles('C1=CC=C[N]1'), 'MYID-111111', 'E-2222222');
这些函数调用盒带定义的“mol_from_smiles”数据库函数来创建mol对象

我是否应该让数据库在保存期间处理此列的创建。我可以在postgres中定义一个自定义触发器,它运行mol_from_smiles函数来填充rdkit_mol列

我还希望能够使用返回django模型的mol自定义特性执行查询。例如,其中一个SQL查询可以返回类似于此的复合模型。目前在SQL中我是这样做的

select * from compound where rdkit_mol @> 'C1=CC=C[N]1';
然后基本上返回化学“化合物”对象

我的问题是:鉴于我所在领域的习惯性质。有没有办法将数据库“mol”类型的特性与django复合模型混合匹配?实现这一目标的方法有哪些

目前,我倾向于不使用Django ORM,而只使用原始SQL往返于数据库。我想知道是否有一种django方法可以处理这种自定义类型

在我目前的混合方法中,我的观点是这样的

def get_similar_compounds(request):
    # code to get the raw smiles string for eg 'C1=CC=C[N]1' from a form
    db_cursor.execute("select internal from compound where rdkit_mol @> 'C1=CC=C[N]1';")
    # code to get internal ids from database cursor
    similar_compounds = compound.objects.filter(internal__in = ids_from_query_above)
    # Then process queryset

这种混合方法是可取的,还是有一种更具pythonic/django风格的方法来处理这种自定义数据类型。

混合的方法是提供自定义字段实现—您已经在做的事情。没有更多的了

自定义字段用于自定义其行为。您可以自定义在将值发送到数据库之前发生的情况、收到值时发生的情况、使用特定查找(例如
mol\uu in=sth
)时发生的情况

在当前的开发版本中,Django允许提供自定义的查找类型,因此您甚至可以实现
@>
操作符(尽管我建议使用官方的稳定版本)


最后,这取决于什么对你来说更容易。为MolField提供良好、连贯的实现可能会非常耗时。因此,这实际上取决于你需要它的地方有多少。在这几个地方只使用原始SQL可能更实用。

我的问题主要涉及创建django自定义字段以处理postgres rdkit数据盒带定义的“mol”数据类型的机制

我制定的解决方案包括一个自定义字段,该字段将与我的模型共存,然后使用原始SQL对mol类型运行查询

由于每次实例化包含SMILES的模型实例时,我都需要创建一个rdkit“mol”类型,我创建了一个数据库过程和一个在表插入或更新时触发的触发器

# A south migration that defines a function called write_rdkit_mol_south in PL/PGSQL

from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models


class Migration(DataMigration):
    def forwards(self, orm):
        "Write your forwards methods here."
        db.execute("""create function write_rdkit_mol_south() RETURNS trigger as $write_rdkit_mol_south$
BEGIN
NEW.rdkit_mol := mol_from_smiles(NEW.smiles::cstring);
RETURN NEW;
END;
$write_rdkit_mol_south$ LANGUAGE plpgsql;""")
        db.execute(
            "create TRIGGER write_rdkit_mol_trig BEFORE INSERT OR UPDATE on strucinfo_compound  FOR EACH ROW EXECUTE PROCEDURE write_rdkit_mol_south();")

        # Note: Don't use "from appname.models import ModelName".
        # Use orm.ModelName to refer to models in this application,
        # and orm['appname.ModelName'] for models in other applications.

    def backwards(self, orm):
        "Write your backwards methods here."
        db.execute("drop TRIGGER write_rdkit_mol_trig ON strucinfo_compound;")
        db.execute("DROP FUNCTION write_rdkit_mol_south();")
接下来,我创建了自定义字段和模型

# My Django model:
class compound(models.Model):
    internalid = models.CharField(max_length=10 ,db_index=True)
    externalid = models.CharField(max_length=15,db_index=True)
    smiles = models.TextField()
    rdkit_mol = RdkitMolField()

    def save(self,*args,**kwargs):
        self.rdkit_mol = ""
        super(compound,self).save(*args,**kwargs)

# The custom field


class RdkitMolField(models.Field):

    description = "Rdkit molecule field"

    def __init__(self,*args,**kwds):
        super(RdkitMolField,self).__init__(*args,**kwds)

    def db_type(self, connection):
        if connection.settings_dict['ENGINE'] == 'django.db.backends.postgresql_psycopg2':
            return None
        else:
            raise DatabaseError('Field type only supported for Postgres with rdkit cartridge')


    def to_python(self, value):
        if isinstance(value,Chem.Mol):
            return value
        if isinstance(value,basestring):
            # The database normally returns the smiles string
            return Chem.MolFromSmiles(str(value))
        else:
            if value:
            #if mol_send was used then we will have a pickled object
                return Chem.Mol(str(value))
            else:
            # The None Case
                return "NO MOL"
   def get_prep_value(self, value):
        # This gets called during save
        # the method should return data in a format that has been prepared for use as a parameter in a query : say the docs
        # rdkit_mol queries do accept smiles strings

        if isinstance(value,basestring):
            db_smiles = str(value)
            if db_smiles:
                my_mol = Chem.MolFromSmiles(db_smiles)
            else:
                return None
            if my_mol:
                # Roundtrip with object could be avoided
                return str(Chem.MolToSmiles(my_mol))
        elif isinstance(value,(str,unicode)):
            valid_smiles = str(Chem.MolToSmiles(Chem.MolFromSmiles(str(value))))
            if valid_smiles:
                return valid_smiles
            else:
                # This is the None case
                # The database trigger will handle this as this should happen only during insert or update
                return None

    def validate(self, value, model_instance):
        # This field is handled by database trigger so we do not want it to be used for object initiation
        if value is None:
            return
        else:
            super(RdkitMolField,self).validate(value,model_instance)

谢谢,很高兴知道我在正确的轨道上。对于如何创建自定义模型字段以映射到rdkit mol类,我仍然感到困惑。部分问题是我不知道ORM如何构造和解构数据库的值。所有的例子都是以文本为中心的,这似乎更为琐碎。这里我的mol对象不是真正的文本,对吗?除非smiles字符串具有创建自定义字段的键。有关于如何编写此自定义字段的指针吗?