只有在存在外键的情况下,保存某些东西的标准方法是什么?

Dav*_*ave 9 python django foreign-keys django-models python-3.x

我正在使用Python 3.7和Django.我有以下型号,带有另一个型号的外键......

class ArticleStat(models.Model):
    objects = ArticleStatManager()
    article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='articlestats')
    ...

    def save(self, *args, **kwargs):
        if self.article.exists():
            try:
                article_stat = ArticleStat.objects.get(article=self.article, elapsed_time_in_seconds=self.elapsed_time_in_seconds)
                self.id = article_stat.id
                super().save(*args, **kwargs, update_fields=["hits"])
            except ObjectDoesNotExist:
                super().save(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

如果相关的外键存在,我只想保存它,否则,我注意到错误结果.做这样的事情的标准Django/Python方法是什么?我以为我读过我可以使用".exists()"(检查一个对象是否存在),但我得到一个错误

AttributeError: 'Article' object has no attribute 'exists'
Run Code Online (Sandbox Code Playgroud)

编辑:这是我必须检查的单元测试...

    id = 1
    article = Article.objects.get(pk=id)
    self.assertTrue(article, "A pre-condition of this test is that an article exist with id=" + str(id))
    articlestat = ArticleStat(article=article, elapsed_time_in_seconds=250, hits=25)
    # Delete the article
    article.delete()
    # Attempt to save ArticleStat
    articlestat.save()
Run Code Online (Sandbox Code Playgroud)

Pao*_*rre 7

如果你想确保文章中存在ArticleStatsave方法,你可以尝试从您的数据库,而不仅仅是测试得到它self.article.

引用Alex Martelli:

"...... Grace Murray Hopper着名的座右铭,"请求宽恕比允许更容易",有很多有用的应用 - 在Python中,......"

我认为使用try .. except .. else更pythonic,我会做这样的事情:

from django.db import models

class ArticleStat(models.Model):
    ...
    article = models.ForeignKey(
        Article, on_delete=models.CASCADE, related_name='articlestats'
    )

    def save(self, *args, **kwargs):
        try:
            article = Article.objects.get(pk=self.article_id)
        except Article.DoesNotExist:
            pass
        else:
            try:
                article_stat = ArticleStat.objects.get(
                    article=article,
                    elapsed_time_in_seconds=self.elapsed_time_in_seconds
                )
                self.id = article_stat.id
                super().save(*args, **kwargs, update_fields=["hits"])
            except ArticleStat.DoesNotExist:
                super().save(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)


sun*_*ara 5

如果您使用的是关系数据库,则会在迁移后自动添加外键约束.save方法可能不需要任何自定义.

class ArticleStat(models.Model):
    objects = ArticleStatManager()
    article = models.ForeignKey(
        Article, on_delete=models.CASCADE, related_name='articlestats'
    )
Run Code Online (Sandbox Code Playgroud)

使用以下代码创建ArticleStats

from django.db import IntegrityError
try:
  ArticleStats.objects.create(article=article, ...)
except IntegrityError:
  pass
Run Code Online (Sandbox Code Playgroud)

如果article_id有效,则会创建ArticleStats对象,否则会引发IntegrityError.

article = Article.objects.get(id=1)
article.delete()
try:
  ArticleStats.objects.create(article=article, ...)
  print("article stats is created")
except IntegrityError:
  print("article stats is not created")


# Output
article stats is not created
Run Code Online (Sandbox Code Playgroud)

注意:在MySQL v5.7,Django 1.11上测试过

  • 我写了一个单元测试来检查这个(现在包含在问题中),但是使用你的代码仍然会导致错误,"ValueError:save()被禁止防止由于未保存的相关对象"文章"而导致数据丢失." 也许taht是因为单元测试使用SqlLite? (2认同)

Yur*_*ots 5

ArticleStat模型上的文章字段不是可选的.没有ForeignKey to Article,您无法保存ArticleStat对象

这是一个类似的代码,item是Item模型的ForeignKey,它是必需的.

class Interaction(TimeStampedModel, models.Model):
    ...
    item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='interactions')
    type = models.IntegerField('Type', choices=TYPE_CHOICES)
    ...
Run Code Online (Sandbox Code Playgroud)

如果我尝试从shell中保存Interaction的对象而不选择ForeignKey到该项,我会收到IntegrityError.

~ interaction = Interaction()
~ interaction.save()
~ IntegrityError: null value in column "item_id" violates not-null constraint
Run Code Online (Sandbox Code Playgroud)

你不需要检查self.article.exists().Django和数据库将需要该字段,并且不允许您在没有它的情况下保存对象.

您应该阅读Django Docs中的 ForeignKey字段


Jon*_*hop 4

您可以只测试该字段的值article。如果没有设置,我相信它默认为None.

if self.article:  # Value is set
Run Code Online (Sandbox Code Playgroud)

如果您希望此外键字段是可选的(听起来像您所做的那样),则需要在该字段上设置blank=Truenull=True。这将允许该字段为空(在验证中),并且null当该字段不存在时将在该字段上设置。

正如下面的评论中提到的,您的数据库可能会强制执行该字段是必需的这一事实,并拒绝删除该article实例。