【问题标题】:django rest framework - POST when Primary Key is also a Foreign Key - "This field is required." OR 'RelatedManager' object is not iterabledjango rest 框架 - 当主键也是外键时发布 - “此字段是必需的。”或“RelatedManager”对象不可迭代
【发布时间】:2014-09-30 02:25:55
【问题描述】:

我在对以下模型进行 POST 时遇到问题 -

class Tskmst(models.Model):
    tskmst_id = models.IntegerField(db_column='tskmst_id', primary_key=True)
    tskmst_name = models.CharField(max_length=30)
    tskmst_desc = models.TextField(blank=True)
    tskmst_type = models.SmallIntegerField(blank=True, null=True)
    owner_id = models.ForeignKey(Owner, db_column='owner_id')
    tskmst_public = models.CharField(max_length=1, blank=True)
    tskmst_lstchgtm = models.DateTimeField(blank=True, null=True)
    nodmst_id = models.ForeignKey(Nodmst, db_column='nodmst_id', blank=True, null=True)
    servicemst_id = models.ForeignKey(Servicemst, db_column='servicemst_id', related_name='Tskmst_Servicemst_id', blank=True, null=True)
    def __unicode__(self):
        return self.tskmst_name   
    class Meta:
        managed = False
        db_table = 'tskmst'

class Tskmail(models.Model):
    tskmail_id = models.ForeignKey(Tskmst, db_column='tskmail_id', related_name='action_email_details', primary_key=True)
    tskmail_to_int = models.TextField(blank=True)
    tskmail_to_ext = models.TextField(blank=True)
    tskmail_subject = models.TextField(blank=True)
    tskmail_memo = models.TextField(blank=True) # This field type is a guess.
    tskmail_priority = models.SmallIntegerField(blank=True, null=True)
    tskmail_attach = models.TextField(blank=True)
    class Meta:
        managed = False
        db_table = 'tskmail'

在 GET 上,它适用于反向关系 - 这是我的序列化程序

class ActionMailSerializer(serializers.ModelSerializer):

    class Meta:
        model = Tskmail
        resource_name = 'tskmail'
        fields = ('tskmail_id', 'tskmail_to_int', 'tskmail_to_ext', 'tskmail_subject', 'tskmail_memo',
                'tskmail_priority', 'tskmail_attach')

class ActionSerializer(serializers.ModelSerializer):
    owner_name = serializers.Field(source='owner_id.owner_name')
    nodmst_name = serializers.Field(source='nodmst_id.nodmst_name')
    servicemst_name = serializers.Field(source='servicemst_id.servicemst_name')
    action_email_details = ActionMailSerializer

    class Meta:
        model = Tskmst
        resource_name = 'tskmst'
        depth = 1
        fields = ('tskmst_id', 'tskmst_name', 'tskmst_desc', 'tskmst_type', 'owner_name', 'tskmst_public',
                'tskmst_lstchgtm', 'nodmst_name', 'servicemst_name', 'action_email_details')

但在 POST 上,它会在验证阶段抱怨以下内容 -

{
    "action_email_details": [
        {
            "tskmail_id": "This field is required."
        }
    ]
}

tskmail_id 肯定存在,我对 PK 与用于反向关系的 FK 不同的其他模型没有问题。我该如何解决这个问题?

如果我将反向关系上的序列化程序代码更改为这样 -

class ActionSerializer(serializers.ModelSerializer):
    owner_name = serializers.Field(source='owner_id.owner_name')
    nodmst_name = serializers.Field(source='nodmst_id.nodmst_name')
    servicemst_name = serializers.Field(source='servicemst_id.servicemst_name')
    action_email_details = ActionMailSerializer(required=False)

    class Meta:
        model = Tskmst
        resource_name = 'tskmst'
        depth = 1
        fields = ('tskmst_id', 'tskmst_name', 'tskmst_desc', 'tskmst_type', 'owner_name', 'tskmst_public',
                'tskmst_lstchgtm', 'nodmst_name', 'servicemst_name', 'action_email_details')

然后它在 POST 上给出另一条消息 - RelatedManager 对象不可迭代。

Traceback:
File "D:\Python27\lib\site-packages\django\core\handlers\base.py" in get_response
  112.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "D:\Python27\lib\site-packages\django\views\generic\base.py" in view
  69.             return self.dispatch(request, *args, **kwargs)
File "D:\Python27\lib\site-packages\django\views\decorators\csrf.py" in wrapped_view
  57.         return view_func(*args, **kwargs)
File "D:\Python27\lib\site-packages\rest_framework\views.py" in dispatch
  400.             response = self.handle_exception(exc)
File "D:\Python27\lib\site-packages\rest_framework\views.py" in dispatch
  397.             response = handler(request, *args, **kwargs)
File "D:\Tidal\API\views.py" in put
  193.         if serializer.is_valid():
File "D:\Python27\lib\site-packages\rest_framework\serializers.py" in is_valid
  553.         return not self.errors
File "D:\Python27\lib\site-packages\rest_framework\serializers.py" in errors
  545.                 ret = self.from_native(data, files)
File "D:\Python27\lib\site-packages\rest_framework\serializers.py" in from_native
  996.         instance = super(ModelSerializer, self).from_native(data, files)
File "D:\Python27\lib\site-packages\rest_framework\serializers.py" in from_native
  368.             attrs = self.restore_fields(data, files)
File "D:\Python27\lib\site-packages\rest_framework\serializers.py" in restore_fields
  283.                 field.field_from_native(data, files, field_name, reverted_data)
File "D:\Python27\lib\site-packages\rest_framework\serializers.py" in field_from_native
  472.                 if serializer.is_valid():
File "D:\Python27\lib\site-packages\rest_framework\serializers.py" in is_valid
  553.         return not self.errors
File "D:\Python27\lib\site-packages\rest_framework\serializers.py" in errors
  521.                     identities = [self.get_identity(self.to_native(obj)) for obj in objects]

Exception Type: TypeError at /deploy/action/
Exception Value: 'RelatedManager' object is not iterable

【问题讨论】:

  • 您为ForeignKey 字段赋予与db_column 相同的名称是否有特殊原因?通常的约定是将字段命名为 tskmail,在这种情况下,tskmail_id 将是默认的 db_column 值 - 我怀疑您的问题由此产生。
  • 这是一个遗留数据库,因此需要设置列名。按照您的建议,我删除了db_column='tskmail_id' 并将该字段设置为tskmail。再次尝试 POST 失败,KeyError: 'tskmail_id'
  • 您可能还必须更新您的序列化程序以引用 tskmail - tskmail_id 成为一个实现选项,仅当您想读取实际整数 FK 或将其设置在表单上下文。我希望序列化程序类可以通过抽象该细节来正确处理 FK 字段。
  • 哦,对了,如果没有一个整数声明的 PK 字段,Django 会很不高兴。这让你的处境变得艰难。作为您描述的解决方法的替代方法,我可能会尝试将 tskmail_id 声明为纯粹的整数 PK 字段,并将 tskmail 声明为带有 db_column='tskmail_id' 的 ForeignKey。或者看看子类模型在幕后是如何工作的,它们可能有一个 PK 字段,它也是父表的 FK。
  • 感谢您的帮助。如果您想发布作为答案,我会接受它,它让我解决了我遇到的问题。

标签: django django-rest-framework


【解决方案1】:

如果您没有整数 PK 字段,Django 不喜欢它。我认为如果ForeignKey 字段的名称与其db_column 相同,它也会发生冲突。在这种情况下,遗留数据库设计使其变得困难。除了您描述的解决方法:

我想我可能已经“欺骗”了系统。我所做的是创建一个名为tskmail = models.IntegerField(db_column='tskmail_id', primary_key=True) 的新字段,然后我从ForiegnKey 中删除了primary_key=True,所以基本上我要显示该字段两次。

我可能会尝试将tskmail_id 声明为整数PK 字段,并将tskmail 声明为ForeignKeydb_column='tskmail_id'(或不指定db_column,以便应用默认选择)。可能值得检查 Django 如何实现子类化非抽象模型——它们可能有一个 PK 字段,它也是父表的 FK。

【讨论】:

  • 谢谢。我实际上同意了你的建议,因为它让我更容易理解 PK 和 FK 引用的位置。
猜你喜欢
  • 2011-10-05
  • 2021-03-05
  • 2021-12-09
  • 2021-11-09
  • 1970-01-01
  • 2016-04-12
  • 1970-01-01
相关资源
最近更新 更多