Skip to content

Commit cdfb090

Browse files
author
Daniel Pinto
committed
improves README.md, updates tests, removes django-fake-model
1 parent b7d708d commit cdfb090

File tree

12 files changed

+137
-137
lines changed

12 files changed

+137
-137
lines changed

README.md

Lines changed: 54 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,39 @@
22

33
Timestamps and Soft Delete Patterns in Django Models.
44

5+
## ✅ Summary
56

6-
## How to install
7+
- Add timestamps and soft delete to any model with a single line of code.
8+
- Manage deleted objects effortlessly with built-in managers and custom querysets.
9+
- Seamlessly integrate into existing models without breaking changes or refactoring your app/project
10+
- ⭐ no need to modify the default objects manager or queryset ⭐
11+
- Hook into lifecycle events with signals.
12+
- Get full CRUD support in DRF, including restore endpoints.
13+
- Configure safety features such as bulk hard delete and bulk responses.
14+
15+
## 🚀 Installation
16+
17+
### Without DRF
718

8-
### Option a)
919
````bash
1020
$ pip install django-timestampable
1121
````
1222

23+
### With DRF support
1324

14-
### Option b)
1525
To install django-timestampable with [Django Rest Framework](https://www.django-rest-framework.org/) included:
26+
1627
````bash
1728
$ pip install "django-timestampable[drf]"
1829
````
30+
1931
*You can use the first option if you have Django Rest Framework already installed.*
2032

2133
 
2234

23-
#### And add "timestamps" to your INSTALLED_APPS settings
35+
## ⚙️ Configuration
36+
37+
Add `timestamps` to your `INSTALLED_APPS` in your Django settings:
2438

2539
```python
2640
INSTALLED_APPS = [
@@ -29,7 +43,8 @@ INSTALLED_APPS = [
2943
]
3044
```
3145

32-
#### Or, if you installed with [Django Rest Framework](https://www.django-rest-framework.org/):
46+
### Or, if you installed with [Django Rest Framework](https://www.django-rest-framework.org/):
47+
3348
```python
3449
INSTALLED_APPS = [
3550
# ...
@@ -38,63 +53,60 @@ INSTALLED_APPS = [
3853
]
3954
```
4055

41-
## Usage
56+
## 🛠 Usage
4257

43-
a) For models you want timestamps, just inherit Timestampable:
58+
a) For Timestamps
4459

4560
```python
4661
from timestamps.models import models, Timestampable
4762

48-
4963
class YourModel(Timestampable):
5064
# your fields here ...
5165

5266
```
5367

54-
b) For models you want soft-delete, just inherit SoftDeletes:
68+
b) For Soft Deletes
5569

5670
```python
5771
from timestamps.models import models, SoftDeletes
5872

59-
6073
class YourModel(SoftDeletes):
6174
# your fields here ...
6275

6376
```
6477

65-
c) If you want both, you can also inherit from Model for shorter convenience:
78+
c) Combine both Timestamps and Soft Deletes
6679

6780
```python
6881
# to this:
69-
from timestamps.models import models, Model # explicit import Model (which contains timestamps)
70-
71-
# instead of:
72-
# from django.db import models
73-
74-
# Explicitly import of "Model" is required
75-
# because models.Model is the original from Django models module
76-
82+
from timestamps.models import models, Model # shortcut including both
7783

7884
class YourModel(Model):
7985
# your fields here ...
8086

8187
```
8288

89+
**Note**: Always import Model from timestamps.models explicitly. models.Model from Django is untouched.
90+
91+
## 🧹 Soft Deleting
8392

84-
### Soft Deleting
93+
### Query managers
8594

86-
- To get all objects without the deleted ones:
95+
When you use SoftDeletes or Model, you have 3 query managers available:
96+
97+
- Active objects only (without soft deleted objects):
8798

8899
```queryset = YourModel.objects```
89100

90-
- To get only deleted objects:
101+
- Deleted objects only:
91102

92103
```queryset = YourModel.objects_deleted```
93104

94-
- To get all the objects, including deleted ones:
105+
- All objects (active + deleted):
95106

96107
```queryset = YourModel.objects_with_deleted```
97108

109+
### Operations
98110

99111
#### To soft delete an instance
100112

@@ -138,15 +150,9 @@ qs = MyModel.objects_deleted # ... bulk restore a subset: qs = MyModel.objects_
138150
qs.restore() # or qs.delete(hard=False)
139151
```
140152

141-
 
142-
143-
---
144-
145-
 
146-
147-
### Signals for Soft Deleting and Restoring
153+
## 📡 Signals
148154

149-
You have 4 signals available that you can listen in your project:
155+
Four signals are available:
150156

151157
- pre_soft_delete
152158
- post_soft_delete
@@ -155,7 +161,7 @@ You have 4 signals available that you can listen in your project:
155161

156162
To use them, just import the signals and register listeners for them. Eg:
157163

158-
#### Pre Soft Delete
164+
### Pre Soft Delete
159165

160166
```python3
161167
from timestamps.signals import pre_soft_delete
@@ -166,7 +172,7 @@ def on_pre_soft_delete(sender, instance, **kwargs):
166172
print(f"Model {sender} with id {instance.pk} will be deleted!")
167173
```
168174

169-
#### Post Soft Delete
175+
### Post Soft Delete
170176

171177
```python3
172178
from timestamps.signals import post_soft_delete
@@ -177,7 +183,7 @@ def on_post_soft_delete(sender, instance, **kwargs):
177183
print(f"Model {sender} with id {instance.pk} was deleted at {instance.deleted_at}!")
178184
```
179185

180-
#### Pre Restore
186+
### Pre Restore
181187

182188
```python3
183189
from timestamps.signals import pre_restore
@@ -188,7 +194,7 @@ def on_pre_restore(sender, instance, **kwargs):
188194
print(f"Model {sender} with id {instance.pk} deleted at {instance.deleted_at} will be restored!")
189195
```
190196

191-
#### Post Restore
197+
### Post Restore
192198

193199
```python3
194200
from timestamps.signals import post_restore
@@ -199,13 +205,7 @@ def on_post_restore(sender, instance, **kwargs):
199205
print(f"Model {sender} with id {instance.pk} restored!")
200206
```
201207

202-
 
203-
204-
---
205-
206-
 
207-
208-
### If you're using DRF
208+
## 🌐 Using with DRF
209209

210210
You can use the SoftDeleteModelViewSet along with DefaultRouter present in this package
211211
and you will have access to a complete CRUD on soft deleted objects as well.
@@ -258,7 +258,7 @@ falsely_options = [
258258
]
259259
```
260260

261-
#### How to expose all CRUD operations
261+
### How to expose all CRUD operations
262262

263263
```python
264264
# dummy/views.py
@@ -287,7 +287,9 @@ urlpatterns = router.urls
287287

288288
````
289289

290-
#### Note A
290+
## ⚠️ Notes & Settings
291+
292+
### Note A - About Bulk Hard Delete
291293

292294
For security reasons, by default, if you pass to the query parameter "?permanent=true" on a bulk destroy,
293295
the view will not let you hard-delete, raising a PermissionDenied.
@@ -304,7 +306,7 @@ In production, you can set this flag to True and manage hard-deleting using DRF
304306

305307
 
306308

307-
#### NOTE B
309+
### Note B - About Bulk Response
308310

309311
Bulk actions of restoring and deleting returns no content (status code 204) by default.
310312
If you want to return a response with the number of deleted/restored objects, just add this setting:
@@ -317,15 +319,15 @@ Example of returned response: ```{"count": 3 }```
317319

318320
 
319321

320-
#### Note C
322+
### Note C - Selective Routes
321323

322324
If you don't want to expose all the crud operations, be free to register as:
323325

324326
```python
325327
router.register(r'dummy', DummyModelViewSet.as_view({'get': 'list_with_deleted'})) # e.g.
326328
```
327329

328-
And you can always use the mixins instead and create your APIViews:
330+
Or use DRF mixins instead:
329331

330332
````python
331333
from rest_framework import generic
@@ -350,6 +352,10 @@ to the correct queryset the view needs.
350352
If you don't inherit from generic.GenericAPIView, you must be aware that, for this type of scenarios,
351353
you need to override the method get_queryset() to return the objects that matches your needs.
352354

355+
 
356+
353357
---
354358

355-
Thank you for reading!
359+
 
360+
361+
Thanks for using `django-timestampable`! 🎉

requirements.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
setuptools>=50.0
12
Django>=3.1
2-
djangorestframework>=3.12
33
aspectlib>=1.5
4-
coverage>=5.5
4+
djangorestframework>=3.12
55
django-fake-model>=0.1.4
66
django-extensions>=3.1
7+
coverage>=5.5
78
twine>=3.4

setup.cfg

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = django-timestampable
3-
version = 1.1.4
3+
version = 1.1.5
44
description = Timestamps and Soft Delete Patterns in Django Models
55
long_description = file: README.md
66
long_description_content_type = text/markdown
@@ -17,6 +17,8 @@ classifiers =
1717
Framework :: Django :: 4.1
1818
Framework :: Django :: 4.2
1919
Framework :: Django :: 5.0
20+
Framework :: Django :: 5.1
21+
Framework :: Django :: 5.2
2022
Intended Audience :: Developers
2123
License :: OSI Approved :: MIT License
2224
Operating System :: OS Independent
@@ -30,6 +32,7 @@ classifiers =
3032
Programming Language :: Python :: 3.10
3133
Programming Language :: Python :: 3.11
3234
Programming Language :: Python :: 3.12
35+
Programming Language :: Python :: 3.13
3336
Topic :: Software Development :: Libraries
3437
Topic :: Software Development :: Libraries :: Application Frameworks
3538

tests/models.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
11
import uuid
2-
from django_fake_model import models as f
3-
from timestamps.models import models, SoftDeletes, Timestampable, Model
4-
5-
6-
class FooTimestamps(f.FakeModel, Timestampable):
7-
...
8-
9-
10-
class FooSoftDeletes(f.FakeModel, SoftDeletes):
11-
...
2+
from timestamps.models import models, Model
123

134

145
class Foo(Model):

tests/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
DATABASES = {
2222
'default': {
2323
'ENGINE': 'django.db.backends.sqlite3',
24+
'NAME': ':memory:',
2425
}
2526
}
2627

0 commit comments

Comments
 (0)