Models
CustomBaseModel
This is a common model. By default, CustomBaseModel
contains these fields:
created_at
updated_at
Almost a default models.Model
with two extra fields.
CustomBaseModelWithSoftDelete
This model inherits from CustomBaseModel
and provides fake deletion which is
probably called SOFT DELETE. This means, when you call model’s delete()
method or QuerySet’s delete()
method, it acts like delete action but never
deletes the data.
Just sets the deleted_at
field to NOW.
This works exactly like Django’s delete()
. Broadcasts pre_delete
and
post_delete
signals and returns the number of objects marked as deleted and
a dictionary with the number of deletion-marks per object type.
You can call hard_delete()
method to delete an instance or a queryset
actually.
This model uses CustomBaseModelWithSoftDeleteManager
as default manager.
How soft-delete works?
When you call .delete()
method of a model instance or queryset, model manager
sets deleted_at
attribute to NOW all the way down through related
ForeignKey
and ManyToMany
fields. This means, you still keep everything.
Nothing is actually deleted, therefore your database constraints are still
work fine. When you access deleted (inactive) object from admin site, you’ll
see "deleted" text prefix in your related form fields if your related objects
are CustomBaseModelWithSoftDelete
instances.
When you click recover button in the same page, all related and soft-deleted
objects’ deleted_at
value will set to NULL
and available again.
Please use .actives()
queryset method instead of .all()
. Why? .all()
method is untouched and works as default. When all()
called, returning
queryset set contains everything event if the deleted_at
is NULL or not...
Examples
>>> Post.objects.all() SELECT "blog_post"."id", "blog_post"."created_at", "blog_post"."updated_at", "blog_post"."deleted_at", "blog_post"."author_id", "blog_post"."category_id", "blog_post"."title", "blog_post"."body" FROM "blog_post" LIMIT 21 Execution time: 0.000950s [Database: default] <CustomBaseModelWithSoftDeleteQuerySet [ <Post: Python post 1>, <Post: Python post 2>, <Post: Python post 3>, <Post: Python post 4>, : : : <Post: Golang post 4> ]> >>> Category.objects.all() SELECT "blog_category"."id", "blog_category"."created_at", "blog_category"."updated_at", "blog_category"."deleted_at", "blog_category"."title" FROM "blog_category" LIMIT 21 Execution time: 0.000643s [Database: default] <CustomBaseModelWithSoftDeleteQuerySet [<Category: Python>, <Category: Ruby>, <Category: Bash>, <Category: Golang>]> >>> Tag.objects.all() SELECT "blog_tag"."id", "blog_tag"."created_at", "blog_tag"."updated_at", "blog_tag"."deleted_at", "blog_tag"."name" FROM "blog_tag" LIMIT 21 Execution time: 0.000519s [Database: default] <CustomBaseModelWithSoftDeleteQuerySet [<Tag: textmate>, <Tag: pyc>, <Tag: irb>, <Tag: ipython>, <Tag: lock>, <Tag: environment>]> >>> Category.objects.get(title='Bash').delete() (9, {'blog.Post_tags': 4, 'blog.Category': 1, 'blog.Post': 4}) >>> Category.objects.delete() (11, {'blog.Post_tags': 4, 'blog.Category': 3, 'blog.Post': 4}) >>> Category.objects.inactives() SELECT "blog_category"."id", "blog_category"."created_at", "blog_category"."updated_at", "blog_category"."deleted_at", "blog_category"."title" FROM "blog_category" WHERE "blog_category"."deleted_at" IS NOT NULL LIMIT 21 Execution time: 0.000337s [Database: default] <CustomBaseModelWithSoftDeleteQuerySet [<Category: Bash>]> >>> Post.objects.inactives() SELECT "blog_post"."id", "blog_post"."created_at", "blog_post"."updated_at", "blog_post"."deleted_at", "blog_post"."author_id", "blog_post"."category_id", "blog_post"."title", "blog_post"."body" FROM "blog_post" WHERE "blog_post"."deleted_at" IS NOT NULL LIMIT 21 Execution time: 0.000387s [Database: default] <CustomBaseModelWithSoftDeleteQuerySet [<Post: Bash post 1>, <Post: Bash post 2>, <Post: Bash post 3>, <Post: Bash post 4>]> >>> Category.objects.inactives().undelete() (9, {'blog.Post_tags': 4, 'blog.Category': 1, 'blog.Post': 4}) >>> Category.objects.inactives() <CustomBaseModelWithSoftDeleteQuerySet []> >>> Post.objects.inactives() <CustomBaseModelWithSoftDeleteQuerySet []>
CustomBaseModelWithSoftDeleteQuerySet
has these query options:
.actives()
: filters ifCustomBaseModelWithSoftDelete.deleted_at
is set toNULL
.inactives()
: filters ifCustomBaseModelWithSoftDelete.deleted_at
is not set toNULL
.delete()
: soft delete on given object/queryset..undelete()
: recover soft deleted on given object/queryset..hard_delete()
: this is real delete. this method erases given object/queryset and there is no turning back!.
When soft-delete enabled (during model creation), Django admin will
automatically use CustomBaseModelAdminWithSoftDelete
which is inherited from:
CustomBaseModelAdmin
<- admin.ModelAdmin
.