我有一个由manage.py inspectdb创建的遗留数据库模型,该模型访问设置中名为'edlserver'的数据库,该数据库是项目使用的众多数据库之一。我不能更改数据库布局。
它有以下类(在其他无关的类中):
一个用于记录条目。
class Logs(models.Model):
time = models.DateTimeField()
job = models.ForeignKey(Jobs, models.DO_NOTHING, db_column='id_job')
msg = models.TextField()
class Meta:
managed = False
db_table = 'logs'
另一个用于Jobs (job字段引用的)
class Jobs(models.Model):
job_type = models.ForeignKey(JobTypes, models.DO_NOTHING, db_column='id_job_type')
time_start = models.DateTimeField()
time_end = models.DateTimeField(blank=True, null=True)
pid = models.IntegerField(blank=True, null=True)
title = models.CharField(max_length=255)
class Meta:
managed = False
db_table = 'jobs'
和另一个用于JobTypes。
class JobTypes(models.Model):
name = models.CharField(max_length=255)
max_processes = models.IntegerField()
class Meta:
managed = False
db_table = 'job_types'
django-rest-framework的视图如下所示
class EDLLogList(generics.ListAPIView):
serializer_class = EDLLogsSerializer
filter_backends = (filters.DjangoFilterBackend, )
filter_class = EDLLogsFilter
def get_queryset(self):
if not 'job_name' in self.request.GET:
raise ParameterRequired('job_name')
else:
return Logs.objects.all().using('edlserver')
它使用过滤器:
class EDLLogsFilter(filters.FilterSet):
time_start = django_filters.DateTimeFilter(name="time", lookup_type='gte')
time_end = django_filters.DateTimeFilter(name="time", lookup_type='lte')
job_name = django_filters.MethodFilter()
class Meta:
model = Logs
fields = ()
def filter_job_name(self, queryset, job_name):
try:
q = queryset.filter(job__job_type__name=job_name)[:10000]
except:
raise InternalError()
if len(q) < 1 and
len(JobTypes.objects.all().using('edlserver').filter(name=job_name)) < 1:
raise InvalidParameter(job_name, 'job_name')
else:
return q
和序列化器:
class EDLLogsSerializer(serializers.HyperlinkedModelSerializer):
time = serializers.DateTimeField()
job_name = serializers.SerializerMethodField()
message = serializers.SerializerMethodField()
class Meta:
model = Logs
fields = ('job_name','time', 'message')
def get_job_name(self, obj):
return obj['id_job__id_job_type__name']
def get_message(self, obj):
return obj.msg
问题是我在序列化器中得到get_job_name()
中的TypeError: 'Logs' object is not subscriptable
,来自psycopg2模块-数据库是MySQL数据库,然而。在调试期间,第一个查询有一个queryset len> 0,这表明模型是正确的,django使用MySQL后端来获取数据。在解决外键出现问题和(我认为)默认数据库得到使用,这是PostGreSQL。
这是一个bug吗?
如果没有,我该怎么办?我在考虑一个路由器,它可以解析一个Meta字段。这将意味着对其他模型进行大量更改,所以我不想这样做。什么好主意吗?
编辑:设置数据库
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'pic',
'USER' : 'pic-5437',
'PASSWORD' : '',
'HOST' : 'host1.url.com',
'PORT' : '5432'
},
'...' : {
...
},
'edlserver': {
'ENGINE': 'django.db.backends.mysql',
'HOST': 'host2.url.com',
'NAME': 'edl',
'USER': 'edl_ro',
'PASSWORD': '',
}
外键为模型添加一个属性,该属性本身就是一个模型实例。要遵循从Logs
到JobType
的完整关系,只需使用属性查找:
def get_job_name(self, obj):
return obj.job.job_type.name
这将是正常的用例。如果使用了多个数据库,而django在解析外键时使用了错误的数据库,可以手动完成:
return JobTypes.objects.all().using('edlserver').filter(id=
Jobs.objects.all().using('edlserver').filter(id=
obj.job_id)[0].job_type_id)[0].name
另一个选项是在模型中引入Meta字段,如下所示:
import django.db.models.options as options
options.DEFAULT_NAMES = options.DEFAULT_NAMES + ('in_db',)
class MyModel(models.Model):
class Meta:
in_db = 'edlserver'
那么需要一个数据库路由器:
class DatabaseMetaRouter(object):
def db_for_read(self, model, **hints):
"""
Route to the given in_db database in Meta
"""
if hasattr(model._meta, 'in_db'):
return model._meta.in_db
else:
return 'default'
def db_for_write(self, model, **hints):
"""
Route to the given in_db database in Meta
"""
if hasattr(model._meta, 'in_db'):
return model._meta.in_db
else:
return 'default'
def allow_relation(self, obj1, obj2, **hints):
"""
Always allow
"""
return True
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
Always allow
"""
return True
问题是这里的obj是Logs
的一个实例
def get_job_name(self, obj):
return obj['id_job__id_job_type__name']
Django模型看起来像字典,闻起来像字典,但它们不是字典。正确的用法是:
return obj.job.job_type.name
我建议你打开一个django shell,加载一个而不是log,并使用help()命令和实验路径。
至于第二个问题,查询使用了错误的数据库,您将需要定义数据库路由器或在查询中添加using()。