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

时间:2014-10-21 13:22:13

标签: django many-to-many django-orm

到目前为止做了什么:

  • 我在使用Django 1.5并升级到1.6(因为我们在Python 2.6上不能更高)但这并没有解决问题。
  • 我已经研究过这个问题,似乎无法找到明确的答案。通过Django Project 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_env\sitar_env2\lib\site-packages\django\core\handlers\base.py" in get_response
  114.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\virtual_env\sitar_env2\lib\site-packages\django\contrib\auth\decorators.py" in _wrapped_view
  22.                 return view_func(request, *args, **kwargs)
File "C:\virtual_env\sitar_env2\cissimp\trending\views.py" in trend
  418.             list_result = utils.convert_queryset_to_lists(q_results, form)
File "C:\virtual_env\sitar_env2\cissimp\trending\utils.py" in convert_queryset_to_lists
  918.                            for dt in affpart.damage_types.all()]),
File "C:\virtual_env\sitar_env2\lib\site-packages\django\db\models\manager.py" in all
  133.         return self.get_queryset()
File "C:\virtual_env\sitar_env2\lib\site-packages\django\db\models\fields\related.py" in get_queryset
  539.                 return super(ManyRelatedManager, self).get_queryset().using(db)._next_is_sticky().filter(**self.core_filters)
File "C:\virtual_env\sitar_env2\lib\site-packages\django\db\models\query.py" in filter
  590.         return self._filter_or_exclude(False, *args, **kwargs)
File "C:\virtual_env\sitar_env2\lib\site-packages\django\db\models\query.py" in _filter_or_exclude
  608.             clone.query.add_q(Q(*args, **kwargs))
File "C:\virtual_env\sitar_env2\lib\site-packages\django\db\models\sql\query.py" in add_q
  1198.         clause = self._add_q(where_part, used_aliases)
File "C:\virtual_env\sitar_env2\lib\site-packages\django\db\models\sql\query.py" in _add_q
  1232.                     current_negated=current_negated)
File "C:\virtual_env\sitar_env2\lib\site-packages\django\db\models\sql\query.py" in build_filter
  1100.                     allow_explicit_fk=True)
File "C:\virtual_env\sitar_env2\lib\site-packages\django\db\models\sql\query.py" in setup_joins
  1351.             names, opts, allow_many, allow_explicit_fk)
File "C:\virtual_env\sitar_env2\lib\site-packages\django\db\models\sql\query.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

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

由于

3 个答案:

答案 0 :(得分:0)

看起来像是在试图打电话的地方:

damage_type.affectedpart.all()

或类似的东西。 要获得损坏类型的所有受影响部件,您需要执行以下操作:

damage_type.affectedpart_set.all()

您可以在documentation.

中详细了解相关信息

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

答案 1 :(得分:0)

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

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

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

否则,请自行创建"through"表。

答案 2 :(得分:0)

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

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

感谢Collin Anderson在django-users@googlegroups.com论坛上发表的演讲。

Collin感兴趣的一些评论:

  1. 你怎么知道的?! 我破解...开玩笑:)。我怀疑有些东西没有被加载,已安装的应用程序列表在你发布的追溯中。
  2. 2.我在生产中使用相同的代码,它没有任何问题。知道为什么吗? 在开发中,大多数(如果不是全部)代码在启动时和生产中(至少在1.7之前)加载和运行,实际上只是根据需要加载。基本上使用app加载重构和django.setup(),我们终于解决了这样的问题。