Python Django多对多关系不保存

Python Django多对多关系不保存,python,django,postgresql,manytomanyfield,Python,Django,Postgresql,Manytomanyfield,更新: 对于任何好奇的人,我想出了什么、为什么以及如何修复它。 在我看来,我有: fields=['html','tags','title','text','take\u date','image'] 我在模板中使用{form.as_p}}。显然,一旦它从表单中发布,它就真的,真的不希望任何其他内容涉及表单中还没有的表单字段。 所以我从我的视图中取出“tags”字段,它就工作了 感谢所有回应的人 原始问题: 使用Django 2.0.1和PostgreSQL 9.2.18 我正在编写一个简单的照

更新

对于任何好奇的人,我想出了什么、为什么以及如何修复它。 在我看来,我有:
fields=['html','tags','title','text','take\u date','image']
我在模板中使用{form.as_p}}。显然,一旦它从表单中发布,它就真的,真的不希望任何其他内容涉及表单中还没有的表单字段。 所以我从我的视图中取出“tags”字段,它就工作了

感谢所有回应的人

原始问题

使用Django 2.0.1和PostgreSQL 9.2.18

我正在编写一个简单的照片库应用程序。其中我有一个照片对象和照片标签对象。照片可以有许多标签,并且标签可以与许多照片相关联,因此它需要是多个字段

保存提交的照片后,post_save接收器调用函数生成缩略图(工作正常),并调用函数更新标签

照片保存良好,更新标签调用良好,标签从照片读取良好,标签保存到照片标签良好。但是,将这两个行绑定在一起的多个表并没有插入新行。除非在update_tags函数或post_save receiver函数期间代码异常退出,否则将调用update_tags之后的thumbs

我甚至尝试使用connection.cursor直接写入m2m表,它也有相同的行为

如果我再次尝试调用Photo对象上的save(),由于post_save信号,我将进入一个无限循环

我不知道发生了什么事。有什么线索吗

# models.py

def update_tags(instance):
    tags = get_tags(instance.image)

    # Set initial values
    pt = []
    tagid = ''
    photoid = instance.id

    # Loop through tag list and insert into PhotoTag and m2m relation
    for x in range(0, len(tags)):
        # Make sure this tag doesn't already exist
        if PhotoTag.objects.filter(tag_text=tags[x]).count() == 0:
            pt = PhotoTag.objects.create(tag_text=tags[x])
            tagid = PhotoTag.objects.latest('id').id
            instance.tags.add(pt)
        else:
            # Only working with new tags right now
            pass

    return


class Photo(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL,
                               on_delete=models.CASCADE)
    title = models.CharField(max_length=200, null=True, blank=True)
    text = models.TextField(null=True, blank=True)
    html = models.BooleanField(default=False)
    filename = models.CharField(default='', max_length=100, blank=True,
                                null=True)
    image = models.ImageField(upload_to=upload_path)
    location = models.CharField(max_length=100, blank=True, null=True)
    entry_date = models.DateTimeField(default=timezone.now)
    taken_date = models.DateTimeField(blank=True, null=True)

    tags = models.ManyToManyField(PhotoTag, blank=True)


@receiver(post_save, sender=Photo)
def thumbs(sender, instance, **kwargs):
    """
    Upon photo save, create thumbnails and then
    update PhotoTag and m2m with any Exif/XMP tags
    in the photo.
    """

    mk_thumb(instance.image, 'mid')
    mk_thumb(instance.image, 'th')
    mk_thumb(instance.image, 'sm')

    update_tags(instance)

    return

-------------
From views.py
-------------

class PhotoCreate(LoginRequiredMixin, CreateView):
    model = Photo
    template_name = 'photogallery/photo_edit.html'
    fields = ['html', 'tags', 'title', 'text', 'taken_date', 'image']

    def get_initial(self):
        self.initial = {'entry_date': timezone.now()}
        return self.initial

    def form_valid(self, form):
        form.instance.author = self.request.user

        return super(PhotoCreate, self).form_valid(form)
更新:

def save(self, mkthumb='', *args, **kwargs):
      super(Photo, self).save(*args, **kwargs)
      if mkthumb != "thumbs":
          self.mk_thumb(self.image, 'mid')
          self.mk_thumb(self.image, 'th')
          self.mk_thumb(self.image, 'sm')

          self.update_tags()

          mkthumb = "thumbs"

      return

重写保存方法,如

def save(self, *args, **kwargs):
    tags = get_tags(self.image)

    # Set initial values
    pt = None

    # Loop through tag list and insert into PhotoTag and m2m relation
    for x in range(0, len(tags)):
        # Make sure this tag doesn't already exist
        if PhotoTag.objects.filter(tag_text=tags[x]).count() == 0:
            pt = PhotoTag.objects.create(tag_text=tags[x])
            self.tags.add(pt)
        else:
            # Only working with new tags right now
            pass
      super(Photo, self).save(*args, **kwargs)
在保存用户实例时,我尝试添加一个组

为什么会发生这种情况的答案是,更明确地说(使用代码)

保存
ModelForm()
(在管理中点击save)时,首先保存对象的一个实例,然后触发其所有信号等。第三步是使用
ModelForm()保存所有m2m关系。清除数据
。如果
ModelForm()

  • 黑客解决方案是使用
    post_save
    信号,该信号将在现有事务(包括保存所有m2m关系的过程)提交到数据库后执行相关代码

    def on_transaction_commit(func):
        ''' Create the decorator '''
        def inner(*args, **kwargs):
            transaction.on_commit(lambda: func(*args, **kwargs))
    
        return inner
    
    
    @receiver(post_save, sender=Photo)
    @on_transaction_commit
    def tags(instance, raw, **kwargs):
        """
        Create the relevant tags after the transaction 
        of instance is committed, unless the database is 
        populated with fixtures. 
        """
        if not raw:
            update_tags(instance)
    
  • 如果您的多对多关系没有
    blank=True
    ,一个更合理的解决方案是使用一个信号,如前所述

  • 最好的方法是在使用
    ModelForm()
    的情况下使用并重写
    ModelForm().clean()
    方法,在直接保存模型的情况下也重写
    Model().save()
    方法

    ModelForm().instance.my_标志
    将非常有用,因此您可以在
    Model().save()中检查现有的
    Model().my_标志
    ,以避免访问两次数据库


可能与主题无关,但是否有理由使用信号而不是覆盖
save()
model方法?这对我来说更容易、更清晰,并且可能有助于调试此类问题。我已经考虑过了,但我不确定我是否能够正确订购,因为制作缩略图和读取标签都需要在照片保存到db和磁盘后进行。也许我会看看重写模型的save()方法可以做些什么。当重写
save
方法时,您可以选择是在
save
之前还是之后执行操作,只需在
super()
之前或之后编写代码即可。在那之后,阅读和调试会容易得多。我敢肯定,在那之后你会自己发现问题的。不,是同样的行为。关系正在建立中,在update_tags()的末尾,我可以放入
print(instance.tags.all())
print(self.tags.all())
,具体取决于实现情况,它会输出正确的标记。您能用
保存
版本的代码编辑您的帖子吗?