Django应用程序之间的多对多关系失败



到目前为止做了什么:

  • 我在Django 1.5上,升级到1.6(不能像在Python 2.6上那样更高),但这并没有解决问题
  • 我对这个问题研究得死去活来,似乎找不到确切的答案。通过查看Django项目的Bug Tracker,我看到了类似的问题,但似乎没有一个适合我的特定情况
  • 我过去已经解决了这个问题,例如使用原始SQL调用将affpart.damage_types.all()替换为自定义函数,但现在这种情况开始更频繁地发生,并且变得非常痛苦

描述:

我在一个项目下有两个Django应用程序。其中一个应用程序使用另一个应用中的模型,使用多对多关系。

这已经顺利工作了几个月,事实上,它在我的生产机器上运行良好,但在我的开发机器上失败了。最近的情况是,我被要求添加一个新功能,当我开始工作时,我在相关代码中得到了一个FieldError,我甚至没有碰过。

此最新问题的违规代码行是:for dt in affpart.damage_types.all()

错误为:

Cannot resolve keyword u'affectedpart' into field. Choices are: cgs_damage_code, description, id, reg_exp, sti

错误发生在query.py模块中Django的内部。

从高层来看,当我试图在不同的Django应用程序中使用多对多模型时,就会出现这个错误。例如,一个受影响的零件可能有多种类型的损坏,并且在不同的受影响零件上可以找到一种损坏类型。

这两个应用程序是:trendingsitar

sitar是第一个构建的,它有我想从趋势中使用的模型。

trending中,我的models.py文件有一个类似于以下的AffectedPart模型:

from sitar.models import (Part, DamageType, Aircraft)
# this model is in trendin.models
class AffectedPart(models.Model):
    res = models.ForeignKey(Res, null=True)
    arising = models.ForeignKey(Arising, null=True)
    aircraft = models.ForeignKey(Aircraft)
    # filled out automatically only if part to Damage/Repair type matching done
    maintenance_phase = models.CharField(max_length=10,
                                         choices=MAINTENANCE_PHASE_CHOICES)
    occurrence_date = models.DateField()
    partnumber = models.ForeignKey(Part)
    damage_types = models.ManyToManyField(DamageType, null=True, blank=True)
    repair_types = models.ManyToManyField(RepairType, null=True, blank=True)
    def __unicode__(self, ):
        if self.res:
            parent = self.res.number
        else:
            parent = str(self.arising)
        return '{0} - {1}'.format(self.partnumber.number, parent)
# The following models are in sitar.models    
class Part(models.Model):
    ''' This model is used to create pick-lists so the user can associate
        one or more applicable parts from a pre-defined list to
        a tracked item.
        It will also allow for regular CRUD functionality which is
        implemented by taking advantage of the Django admin interface. '''
    # Added to associate a zone with a part
    zones = models.ManyToManyField("Zone", null=True, blank=True)
    number = models.CharField(max_length=50, unique=True)
    description = models.CharField(max_length=100, blank=True)
    comments = models.TextField(blank=True)
    material = models.CharField(max_length=100, blank=True)
    class Meta:
        ''' Order by part number field (ascending) when presenting data '''
        ordering = ['number']

    def __unicode__(self):
        ''' Return unicode description of a part instance '''
        if self.description:
            return '%s -- %s' % (self.number, self.description)
        else:
            return self.number

    def get_encoded_part_number(self):
        '''
           This method will remove any '/' in part numbers and replace them
           with '~' so that they can be used in URLs.
        '''
        return self.number.replace('/','~')

class DamageType(models.Model):
    description = models.CharField(max_length=50, unique=True)
    # a regular expression to account for possible spelling mistakes when
    # querying the database
    reg_exp = models.CharField(max_length=50, blank=True)
    # Added to provide damage code for TRENDING
    cgs_damage_code = models.CharField(max_length=10, blank=True,
                                       verbose_name="CGS Damage Code")
    def __unicode__(self):
        ''' Return unicode representation of a DamageType instance. '''
        return self.description
    class Meta:
        ''' Order by description field (ascending) when presenting data '''
        ordering = ['description']
    def save(self):
        ''' Override the save method of the DamageType model in order to assign
            a regexp if one does not exist.'''
        # if the tracked item does not have a reg_exp just use
        # the description
        if not self.reg_exp:
            self.reg_exp = self.description
        super(DamageType,self).save()

堆栈跟踪

Environment:

Request Method: POST
Request URL: http://127.0.0.1:8000/trending/trend/
Django Version: 1.6
Python Version: 2.6.7
Installed Applications:
('django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.sites',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.admin',
 'sitar')
Installed Middleware:
('django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware')

Traceback:
File "C:virtual_envsitar_env2libsite-packagesdjangocorehandlersbase.py" in get_response
  114.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:virtual_envsitar_env2libsite-packagesdjangocontribauthdecorators.py" in _wrapped_view
  22.                 return view_func(request, *args, **kwargs)
File "C:virtual_envsitar_env2cissimptrendingviews.py" in trend
  418.             list_result = utils.convert_queryset_to_lists(q_results, form)
File "C:virtual_envsitar_env2cissimptrendingutils.py" in convert_queryset_to_lists
  918.                            for dt in affpart.damage_types.all()]),
File "C:virtual_envsitar_env2libsite-packagesdjangodbmodelsmanager.py" in all
  133.         return self.get_queryset()
File "C:virtual_envsitar_env2libsite-packagesdjangodbmodelsfieldsrelated.py" in get_queryset
  539.                 return super(ManyRelatedManager, self).get_queryset().using(db)._next_is_sticky().filter(**self.core_filters)
File "C:virtual_envsitar_env2libsite-packagesdjangodbmodelsquery.py" in filter
  590.         return self._filter_or_exclude(False, *args, **kwargs)
File "C:virtual_envsitar_env2libsite-packagesdjangodbmodelsquery.py" in _filter_or_exclude
  608.             clone.query.add_q(Q(*args, **kwargs))
File "C:virtual_envsitar_env2libsite-packagesdjangodbmodelssqlquery.py" in add_q
  1198.         clause = self._add_q(where_part, used_aliases)
File "C:virtual_envsitar_env2libsite-packagesdjangodbmodelssqlquery.py" in _add_q
  1232.                     current_negated=current_negated)
File "C:virtual_envsitar_env2libsite-packagesdjangodbmodelssqlquery.py" in build_filter
  1100.                     allow_explicit_fk=True)
File "C:virtual_envsitar_env2libsite-packagesdjangodbmodelssqlquery.py" in setup_joins
  1351.             names, opts, allow_many, allow_explicit_fk)
File "C:virtual_envsitar_env2libsite-packagesdjangodbmodelssqlquery.py" in names_to_path
  1274.                                      "Choices are: %s" % (name, ", ".join(available)))
Exception Type: FieldError at /trending/trend/
Exception Value: Cannot resolve keyword u'affectedpart' into field. Choices are: cgs_damage_code, description, id, reg_exp, sti

如果有人对此有解决方案,或者知道一个应用程序中的模型与另一个应用软件中的模型有多对多关系的最佳实践,我很乐意听到

感谢

看起来像是您试图调用的地方:

damage_type.affectedpart.all()

或者类似的东西。要获得你的损伤类型的所有受影响的零件,你需要做一些类似的事情:

damage_type.affectedpart_set.all()

您可以在文档中阅读更多相关内容。

如果这不是您的错误,请跟踪有问题的代码段。

是否将DamageType关系从ForeignKey更改为ManyToMany

我猜你已经创建了,Django从未创建过中间表,所以它试图使用DamageType表。这就是为什么它在寻找与AffectedPart的关系。

当您进行模型更改时,应该使用类似South的东西进行迁移,因为更改为ManyToMany字段并不像听起来那么简单。

否则,您可以自己创建一个"直通"表。

发生错误是因为我忽略了将"trending"放入INSTALLED_APPS中。

由于它在生产中使用完全相同的设置文件,所以我从未想过要查看那里。

感谢科林·安德森django-users@googlegroups.com论坛

科林感兴趣的一些评论:

  1. 你怎么知道的!?!我破解。。。开玩笑:)。我怀疑有什么东西没有加载,安装的应用程序列表在你发布的回溯中

2.我在生产中也有同样的代码,它运行起来没有问题。知道为什么吗?在开发中,如果不是所有的代码都是在启动时加载和运行的,那么在生产中(至少在1.7之前),实际上只是根据需要加载。基本上,通过应用程序加载重构和django.setup(),我们终于解决了这样的问题。

相关内容

最新更新