Postgres 不尊重 models.py 中 Django 的“on_delete = models.CASCADE”。为什么?

问题描述 投票:0回答:1

我刚刚从 SQLite3 迁移到 Postgres 12(使用 pgloader)。

我无法删除某些模型对象,因为还有其他模型对象引用它。

但是,我非常确定我的模型对象在引用模型对象中设置了“on_delete = models.CASCADE”(在 models.py 中的 Django 代码中)。

所以我得到了 Postgres 生成的错误:

...
django.db.utils.IntegrityError: update or delete on table "app_reply" violates foreign key constraint "app_replyimage_reply_id_fkey" on table "app_replyimage"
DETAIL:  Key (id)=(SRB6Nf98) is still referenced from table "app_replyimage".

有没有办法(希望不用在 Postgres 中手动编辑表模式)来解决这个问题?

编辑:在下面添加一些代码...

模型.py

class ReplyImage(models.Model):
    id = models.CharField(default = make_id(), unique = True, primary_key = True, max_length = 8)
    
    file = models.ImageField(upload_to = customer_images_directory_path)
    
    reply = models.ForeignKey(Reply, on_delete = models.CASCADE, related_name = 'reply_images')
    
    #Meta
    created = models.DateTimeField(auto_now_add = True)
    updated = models.DateTimeField(auto_now = True)
    created_by = models.ForeignKey(User, on_delete = models.CASCADE, related_name = '+', null = True)
    updated_by = models.ForeignKey(User, on_delete = models.CASCADE, related_name = '+', null = True)
    
    def __str__(self):
        return str(self.id)
    
    def delete(self, *args, **kwargs):
        self.file.delete()
        super(ReplyImage, self).delete(*args, **kwargs)

@receiver(post_delete, sender = ReplyImage)
def reply_image_delete(sender, instance, **kwargs):
    instance.file.delete()

这是参考型号,回复:

class Reply(models.Model):
    id = models.CharField(default = make_id(), unique = True, primary_key = True, max_length = 8)
    
    #Content
    content = models.CharField(max_length = 560, blank = True, null = True)
    replies = GenericRelation('self')
    link = models.ForeignKey(Link, on_delete = models.SET_NULL, related_name = 'reply', blank = True, null = True)
    
    #Drip
    drip_interval_quantity = models.IntegerField(default = 0, blank = True, null = True) #Custom quantity
    drip_interval_calendar_unit = models.CharField(default = 'minute', max_length = 6, choices = DRIP_INTERVAL_CALENDAR_UNIT_CHOICES, blank = True, null = True) #Custom calendar unit
    
    post_datetime = models.DateTimeField(blank = True, null = True) #Post datetime
    
    #Twitter API
    self_status_id_str = models.CharField(max_length = 19, null = True, blank = True) #status_id_str of self
    in_reply_to_status_id_str = models.CharField(max_length = 19, null = True, blank = True) #status_id_str of tweet replied to
    
    #Meta
    created = models.DateTimeField(auto_now_add = True)
    updated = models.DateTimeField(auto_now = True)
    created_by = models.ForeignKey(User, on_delete = models.CASCADE, related_name = '+', null = True)
    updated_by = models.ForeignKey(User, on_delete = models.CASCADE, related_name = '+', null = True)
    
    #Mandatory fields for generic relation
    content_type = models.ForeignKey(ContentType, on_delete = models.CASCADE)
    object_id = models.CharField(default = make_id(), unique = True, max_length = 8)
    content_object = GenericForeignKey()
    
    customer = models.ForeignKey(Customer, on_delete = models.CASCADE, related_name = 'postreplies')
    
    def delete(self, *args, **kwargs):
        if self.reply_images.all():
            for i in self.reply_images.all():
                i.delete()
        
        if self.link:
            self.link.delete()
        
        super(Reply, self).delete(*args, **kwargs)
    
    def __str__(self):
        return self.content

@receiver(post_save, sender = Reply)
def reply_model_save(sender, instance, **kwargs):
    if kwargs['raw']:
        return
    
    recursive_reply_save_mock_post(instance)

@receiver(post_delete, sender = Reply)
def reply_model_delete(sender, instance, **kwargs):
    if instance.reply_images.all():
        for i in instance.reply_images.all():
            i.delete()

views.py

...
post.replies.all().delete()
...

(我重新定义了delete()方法并添加了post_delete()信号,但我认为它们不会影响任何东西,因为它与SQLite完美配合。)

出了什么问题?

编辑2:

Postgres 表架构

                       Table "public.app_replyimage"
    Column     |           Type           | Collation | Nullable | Default
---------------+--------------------------+-----------+----------+---------
 file          | text                     |           |          |
 created       | timestamp with time zone |           |          |
 updated       | timestamp with time zone |           |          |
 created_by_id | bigint                   |           |          |
 reply_id      | text                     |           |          |
 updated_by_id | bigint                   |           |          |
 id            | text                     |           | not null |
Indexes:
    "idx_85696_sqlite_autoindex_app_replyimage_1" PRIMARY KEY, btree (id)
    "idx_85696_app_replyimage_created_by_id_a5974d1f" btree (created_by_id)
    "idx_85696_app_replyimage_reply_id_7a8aff2c" btree (reply_id)
    "idx_85696_app_replyimage_updated_by_id_d73f7446" btree (updated_by_id)
Foreign-key constraints:
    "app_replyimage_created_by_id_fkey" FOREIGN KEY (created_by_id) REFERENCES auth_user(id)
    "app_replyimage_reply_id_fkey" FOREIGN KEY (reply_id) REFERENCES app_reply(id)
    "app_replyimage_updated_by_id_fkey" FOREIGN KEY (updated_by_id) REFERENCES auth_user(id)
django postgresql sqlite django-models pgloader
1个回答
6
投票

简而言之:Postgres 处理 ON DELETE 触发器。 Django 本身 进行级联处理。

确实,

models.CASCADE
是一个callable,它被调用并收集其他需要删除的元素。例如,我们可以查看 源代码[GitHub]

def CASCADE(collector, field, sub_objs, using):
    collector.collect(
        sub_objs, source=field.remote_field.model, source_attr=field.name,
        nullable=field.null, fail_on_restricted=False,
    )
    if field.null and not connections[using].features.can_defer_constraint_checks:
        collector.add_field_update(field, None, sub_objs)

这里的

collector
是一个集合,收集需要删除的对象。

所以Django会在你删除一个对象时,确定哪些其他对象需要删除、更新等,然后以正确的顺序进行查询。它不使用数据库中的任何触发器。

事实上,您可以通过实现具有相同参数并运行不同逻辑的可调用来实现自己的删除策略,尽管我建议保持简单。

如果你想让 Django 做同样的事情,你应该更改该字段,使其看起来像这样:

ALTER TABLE app_replyimage ALTER COLUMN reply_id
    integer REFERENCES orders ON DELETE CASCADE;
© www.soinside.com 2019 - 2024. All rights reserved.