Django select_related链接的外键不返回非直接相关的对象

时间:2019-05-10 17:04:02

标签: python django

我曾浏览过其他多个帖子,因此这篇帖子似乎重复了,但我找不到答案。我是该平台的新手,所以如果这不是正确的操作方式,我会深感抱歉。

我使用Django 2.2

基于官方文档,它说select_related()返回一个QuerySet,它将“遵循”外键关系,并在执行查询时选择其他相关对象数据:https://docs.djangoproject.com/en/2.2/ref/models/querysets/#select-related

这是官方示例: 您可以按照与查询外键相似的方式来跟踪外键。如果您具有以下型号:

from django.db import models

class City(models.Model):
    # ...
    pass

class Person(models.Model):
    # ...
    hometown = models.ForeignKey(
        City,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )

class Book(models.Model):
    # ...
    author = models.ForeignKey(Person, on_delete=models.CASCADE)

然后调用Book.objects.select_related('author__hometown')。get(id = 4)将缓存相关的Person和相关的城市:

# Hits the database with joins to the author and hometown tables.
b = Book.objects.select_related('author__hometown').get(id=4)
p = b.author         # Doesn't hit the database.
c = p.hometown       # Doesn't hit the database.

# Without select_related()...
b = Book.objects.get(id=4)  # Hits the database.
p = b.author         # Hits the database.
c = p.hometown       # Hits the database.

这是我的代码:

class Campaign(SlugifyNameMixin, TimeStampedModel, TimeFramedModel, 

    StatusModel):

        STATUS = Choices(
            ('draft', _('Draft')),
            ('published', _('Published')),
            ('disabled', _('Disabled')),
        )

        banner = ThumbnailerImageField(verbose_name=_('Banner'),
                                       help_text=_('Choose a representative banner image'),
                                       upload_to='campaigns/banners/',
                                       blank=True
                                      )
        tagline = models.CharField(max_length=150, verbose_name=_('Tagline'), help_text=_('A short campaign description'))


        organization = models.ForeignKey(Organization,
                                        on_delete=models.PROTECT,
                                        related_name='campaigns',
                                        verbose_name=_('Organization'),
                                        help_text=_('Select the organization that this campaign belongs to.')
                                        )

        description = RichTextUploadingField(null=False, default='',
                                             verbose_name=_('Description'),
                                             help_text=_('A description of this campaign and what it aims to accomplish')
                                             )

        class Meta:
            verbose_name = _("Campaign")
            verbose_name_plural = _("Campaigns")
            unique_together = ("organization", "slug")

            permissions = (('change_campaign_status', 'Can change the status of campaigns'),)

        def __str__(self):
            return '%s' % (self.name)

        def get_absolute_url(self):
            return reverse('explore:campaign-detail', args=[self.organization.slug, self.slug])


    class Action(SlugifyNameMixin, TimeStampedModel):

        campaign = models.ForeignKey(Campaign,
                                     on_delete=models.PROTECT,
                                     related_name='actions',
                                     verbose_name=_('campaign'),
                                     help_text=_('Select the campaign that this action belongs to.')
                                    )

        class Meta:
            verbose_name = _('Action')
            verbose_name_plural = _('Actions')

            permissions = (('change_action_status', 'Can change the status of actions'),)

        def __str__(self):
            return '%s' % (self.name)


    class ActionMeta(TimeStampedModel, StatusModel):

        STATUS = Choices(
            ('open', _('Open')),
            ('in-progress', _('In-Progress')),
            ('completed', _('Completed')),
        )

        user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'), on_delete=models.PROTECT)
        action = models.ForeignKey(Action, on_delete=models.CASCADE, related_name='metas', verbose_name=_('action'))


        class Meta:
            verbose_name = _('Action Meta')
            verbose_name_plural = _('Action Metas')
            unique_together = ('user', 'action')

        def __str__(self):
            return '%s-meta-%s' % (self.action.name, self.id)

我的问题是我无法检索广告系列对象

>>> camp11 = Campaign.objects.get(name='camp11')
>>> camp12 = Campaign.objects.get(name='camp12')
>>> action11u1 = Action.objects.get(name='action11u1')
>>> action12u1 = Action.objects.get(name='action12u1')
>>> am1 = ActionMeta.objects.create(user=u, action=action11u1)
>>> am2 = ActionMeta.objects.create(user=u, action=action12u1)

>>> a1 = ActionMeta.objects.select_related('action__campaign').get(pk=am1.pk)
>>> a1.campaign
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'ActionMeta' object has no attribute 'campaign'

>>> a1.action
<Action: action11u1>

这是数据库查询

>>> print(a1.query)
SELECT "core_actionmeta"."id", "core_actionmeta"."created", "core_actionmeta"."modified", "core_actionmeta"."status", "core_actionmeta"."status_changed", "core_actionmeta"."user_id", "core_actionmeta"."action_id", "core_action"."id", "core_action"."created", "core_action"."modified", "core_action"."name", "core_action"."slug", "core_action"."campaign_id", "core_campaign"."id", "core_campaign"."created", "core_campaign"."modified", "core_campaign"."start", "core_campaign"."end", "core_campaign"."status", "core_campaign"."status_changed", "core_campaign"."name", "core_campaign"."name_en", "core_campaign"."name_fa", "core_campaign"."slug", "core_campaign"."banner", "core_campaign"."tagline", "core_campaign"."tagline_en", "core_campaign"."tagline_fa", "core_campaign"."organization_id", "core_campaign"."description", "core_campaign"."description_en", "core_campaign"."description_fa" FROM "core_actionmeta" INNER JOIN "core_action" ON ("core_actionmeta"."action_id" = "core_action"."id") INNER JOIN "core_campaign" ON ("core_action"."campaign_id" = "core_campaign"."id") WHERE "core_actionmeta"."id" = 3

1 个答案:

答案 0 :(得分:1)

您仍然需要经历$ npx codeceptjs run --grep "tabletest" --debug --verbose CodeceptJS v2.1.1 Using test root "C:\PISTARD\diffusion\dev\pistard-diffusion" Helpers: Protractor Plugins: screenshotOnFail, wdio Recherche par cote @PDIFF-56 -- Emitted | suite.before ([object Object]) » Started SeleniumStandaloneLauncher » [Session] Starting singleton browser session Les champs @tabletest Emitted | test.before ([object Object]) Emitted | hook.start ([object Object]) Emitted | step.before (I am on page "recherche-avancee") Emitted | step.after (I am on page "recherche-avancee") Emitted | step.start (I am on page "recherche-avancee") Etant donné que je suis sur la page "recherche avancee" I am on page "recherche-avancee" » Visited http://localhost:4200/recherche-avancee Emitted | step.passed (I am on page "recherche-avancee") Emitted | step.finish (I am on page "recherche-avancee") Emitted | hook.passed ([object Object]) Emitted | test.start ([object Object]) [1] Error | TypeError: Cannot read property 'bold' of undefined [1] Starting <teardown> session Emitted | test.failed ([object Object]) Emitted | test.finish ([object Object]) [1] <teardown> Stopping recording promises » <screenshotOnFail> Test failed, saving screenshot » Screenshot has been saved to C:\PISTARD\diffusion\dev\pistard-diffusion\codeceptjs-output\Les_champs_1557763592.failed.png × FAILED in 287ms [2] Starting recording promises Emitted | test.after ([object Object]) Emitted | suite.after ([object Object]) -- FAILURES: 1) Recherche par cote @PDIFF-56 Les champs @tabletest: Cannot read property 'bold' of undefined ypeError: Cannot read property 'bold' of undefined at Object.say (node_modules\codeceptjs\lib\output.js:139:53) at recorder.add (node_modules\codeceptjs\lib\actor.js:40:78) at process.internalTickCallback (internal/process/next_tick.js:77:7) FAIL | 0 passed, 1 failed // 5s Emitted | global.result ([object Object]) Emitted | global.after ([object Object]) » Stopped SeleniumStandaloneLauncher 。试试:

action

旁注:我可能会更改>>> a1.action.campaign 的名称,只是因为ActionMeta具有这样的特定含义,并且已经有一个Meta