Python 如何处理/映射自定义postgresql类型到django模型
我正在使用rdkit,这是一个化学信息工具包,它提供了一个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
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字符串具有创建自定义字段的键。有关于如何编写此自定义字段的指针吗?