【问题标题】:Post save, model id nonexistent保存后,模型ID不存在
【发布时间】:2011-12-18 10:25:15
【问题描述】:

我有多个与单个模型相关的模型。在保存这些模型时,我重写了 save 以检索主模型的 id,以便将文件放在操作系统上由主模型的 pk 键入的目录中。

例如,以具有许多房间的建筑物为例。房间的任何图像都将保存在由建筑物 ID 键入的目录中(房间没有子目录)。

只要保存房间时建筑物存在,我的覆盖保存方法就可以正常工作。但是,如果该建筑物尚未保存,并且我正在通过 django 管理员向该建筑物添加一个房间,则该图像仍保留在上传目录中,因为该建筑物的 pk 尚不存在。

我最初尝试在 Building 上覆盖 save 并将任何 Room 图像移动到新创建的 Building 目录(再次关闭 building 的 pk)。尽管 super(Building, self).save(*args, **kwargs) 首先没有设置 Building 的 id。

然后我决定 post_save 信号可能更干净,并且这样做了。不幸的是,该 id 似乎也不存在于帖子保存中。我可以尝试打印 ID,但在触发后保存时看不到任何值,直到我第二次保存模型。

有人能给我指出一个方向,可以解释为什么 id 没有像this other SO answer 中接受的预期输出那样设置吗?

谢谢。

编辑:

这是评论中要求的一些代码。因为我简化了最初的问题,所以我在这里包含更多内容。这里有 3 个级别,一个带有房间的建筑物列表。该清单是我尝试通过print kwargs['instance'] 行简单打印的内容。在底部,我包含了两次背靠背保存后的输出。请注意在第一次保存后完全没有实例存在。这些实际上是背靠背的,中间没有任何动作。对 Building_Room 之类的东西的引用是通过表来实现的。除了数据字段之外,RoomImage、BuildingImage 和 ListingImage 都相似,因此我只包含了一个。

class Listing(models.Model):
    ...
    buildings = models.ManyToManyField('Building', null=True, blank=True, through = 'Building_Listing')
    addresses = models.ManyToManyField(Address, null=True, blank=True)
    def __unicode__(self):
        return '  &  '.join([a.__unicode__() for a in self.addresses.all()])

class Building(models.Model):
    ...
    rooms = models.ManyToManyField('Room', null=True, through="Building_Room")
    def __unicode__(self):
        return self.description

class Room(models.Model):
    ...
    def __unicode__(self):
        return str(self.room_type)

class RoomImage(models.Model):
    room = models.ForeignKey(Room)
    room_photo = FileBrowseField("Image", max_length=200, blank=True, null=True)

    def save(self, *args, **kwargs):
        try:
            listing = Building_Listing.objects.get(building=Building_Room.objects.get(room=self.room).building).listing
            self.room_photo = moveFileBeforeSave(listing, self.room_photo)
        except Building_Listing.DoesNotExist:
            pass
        except Building_Room.DoesNotExist:
            pass
        super(RoomImage, self).save(*args, **kwargs)

@receiver(post_save, sender=Listing, weak=False)
def save_images_on_listing_create(sender, **kwargs):
    #if kwargs['created']:
    listing = kwargs['instance']
    print kwargs['instance']
    listing_image_list = ListingImage.objects.filter(listing = listing)
    listing_buildings = Building_Listing.objects.filter(listing = listing).values_list('building', flat=True)
    building_image_list = BuildingImage.objects.filter(building__in = listing_buildings)
    building_rooms = Building_Room.objects.filter(building__in = listing_buildings).values_list('room', flat=True)
    room_image_list = RoomImage.objects.filter(room__in = building_rooms)
    for image in listing_image_list:
        image.save()
    for image in building_image_list:
        image.save()
    for image in room_image_list:
        image.save()

@receiver(post_save, sender=Building, weak=False)
def save_images_in_building_create(sender, **kwargs):
    #if kwargs['created']:
    print str(kwargs['instance'])+" : building save trigger"
    building = kwargs['instance']
    building_image_list = BuildingImage.objects.filter(building = building)
    building_rooms = Building_Room.objects.filter(building = building).values_list('room', flat=True)
    room_image_list = RoomImage.objects.filter(room__in = building_rooms)
    for image in building_image_list:
        image.save()
    for image in room_image_list:
        image.save()

一些输出:

[30/Oct/2011 19:52:05] "POST /admin/mls/building/add/?_popup=1 HTTP/1.1" 200 97
# This is the print of the instance kwarg after the first save (ie nothing)
[30/Oct/2011 19:52:10] "POST /admin/mls/listing/add/ HTTP/1.1" 302 0
[30/Oct/2011 19:52:10] "GET /admin/mls/listing/8/ HTTP/1.1" 200 72495
[30/Oct/2011 19:52:10] "GET /admin/jsi18n/ HTTP/1.1" 200 2158
1 Ben Blvd sometown, MN #this is the print of the instance kwarg after the second save
[30/Oct/2011 19:52:12] "POST /admin/mls/listing/8/ HTTP/1.1" 302 0
[30/Oct/2011 19:52:13] "GET /admin/mls/listing/8/ HTTP/1.1" 200 72497
[30/Oct/2011 19:52:13] "GET /admin/jsi18n/ HTTP/1.1" 200 2158

【问题讨论】:

  • 你确定 post_save 信号是附加到建筑物的保存,而不是房间?你能发布你的信号以及你在哪里注册信号吗?如果信号在建筑物保存后触发,那么您没有理由看不到 id(除非您使用 commit=False 保存,在这种情况下,我不确定信号是否会触发?)
  • 我很确定后保存已附加到正确的模型,因为我设置了发件人,并且在第一次保存后立即保存第二次后也获得了预期的输出。信号/注册装饰器和相关模型代码已包含在编辑中。没有提交=假。正如所包含的输出所引用的那样,信号正在触发。感谢您的帮助,我的头脑很困惑……希望我正在做一些显而易见的事情。

标签: django django-models django-admin django-signals


【解决方案1】:

首先,pastylegs 是对的,他的答案更好。但是如果由于某种原因(就像我现在一样)由于某种原因无法更改代码并发现自己在 post_save 模型实例中不存在像我们在过去几天中发现的那样的问题,那么以下想法可能会有所帮助。

在我们的例子中,多对多通表对于 post_save 来说已经足够了。通过将 post save 信号附加到 through 表本身,我们基本上能够捕捉到我们需要为其执行 post_saving 的所有情况,并且作为两个连接表的 id 以用于通过表中存在的多对多关系,这足以获得完成的工作。因此,如果您发现自己出于类似原因在这里,您可以将 post_save 附加到一个直通表吗?

再说一次,pastylegs 是对的,但如果您出于某种原因不能这样做,我希望这对您有所帮助。

【讨论】:

    【解决方案2】:

    好的,所以问题看起来是因为您使用的多对多关系。看看这些帖子:

    save with many-to-many relationship in django problem

    Issue with ManyToMany Relationships not updating inmediatly after save

    我会考虑重构您的代码以更改建筑物与其房间之间的关系。目前,您说的是“有很多房间,而建筑物可以将自己与其中的一些房间联系起来”。此外,两个建筑物可以与同一个房间相关联。这真的没有意义。实际上,一个房间应该与一个且只有一个建筑物相关联,即

    class Building(models.Model):
        name = models.CharField(...)
        ...
    
    class Room(models.Model):
        building = models.ForeignKey(Building, unique=True)
        ....
    

    这意味着任何房间只能链接到一栋特定的建筑物。

    【讨论】:

    • 谢谢。完全与您一起使用外键而不是多对多的想法。我认为促使我们使用多线程的原因是我们使用了直通/多线程模型。现在我们有直通表的内联(即 Building_Room)。我们这样做是为了解决缺少嵌套内联的问题。单击 building_inline 上的“编辑”会显示该引用建筑物的建筑物管理显示。现在,直到我们可以根据您的建议找到更好的解决方案,我们可能会在直通表本身上尝试 post_save。很快就尝试了。感谢您的帮助。
    • 我感受到了你的困境!你应该小心不要(像我一样)陷入基于 django-admin 的功能设计数据库和业务逻辑的陷阱 - 这些限制通常是由于前端代码和弯曲你的后端设计来创造生活更容易有时会导致问题。
    • 是的,正是我一直在想的。不过,这里有点落后(设计不佳的模型-> 管理员建立在这些模型上-> 现在无法轻松更改)。最初我们认为出于某种原因我们确实想要 m2m(不知道为什么),然后我们构建了管理员。因此,我们的管理员目前过于依赖我们设计错误的数据库来修复它。再次感谢您的帮助和建议。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-14
    • 1970-01-01
    • 2014-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多