Initial commit - clean project structure
This commit is contained in:
0
statuslog/__init__.py
Normal file
0
statuslog/__init__.py
Normal file
10
statuslog/admin.py
Normal file
10
statuslog/admin.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import StatusChangeLog
|
||||
|
||||
|
||||
@admin.register(StatusChangeLog)
|
||||
class StatusChangeLogAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'label', 'object_id', 'old_status', 'new_status', 'event', 'user_id', 'created_at')
|
||||
list_filter = ('label', 'event', 'new_status')
|
||||
search_fields = ('object_id', 'user_id')
|
||||
9
statuslog/apps.py
Normal file
9
statuslog/apps.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class StatuslogConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'statuslog'
|
||||
|
||||
def ready(self):
|
||||
from . import receivers # noqa: F401
|
||||
30
statuslog/migrations/0001_initial.py
Normal file
30
statuslog/migrations/0001_initial.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Generated by Django 6.1 on 2026-08-18 02:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='StatusChangeLog',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('label', models.CharField(db_index=True, max_length=50)),
|
||||
('object_id', models.IntegerField(db_index=True)),
|
||||
('old_status', models.CharField(blank=True, max_length=30)),
|
||||
('new_status', models.CharField(max_length=30)),
|
||||
('event', models.CharField(max_length=50)),
|
||||
('user_id', models.IntegerField(blank=True, db_index=True, null=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-created_at'],
|
||||
},
|
||||
),
|
||||
]
|
||||
0
statuslog/migrations/__init__.py
Normal file
0
statuslog/migrations/__init__.py
Normal file
23
statuslog/models.py
Normal file
23
statuslog/models.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class StatusChangeLog(models.Model):
|
||||
"""
|
||||
لاگ عمومیِ تغییر Status هر مدلی در پروژه (Order، Subscription، UserOffer و...)
|
||||
از طریق سیگنال statuslog.signals.status_changed ساخته میشه، نه مستقیم.
|
||||
label بهجای FK واقعی، چون این اپ نباید به هیچ اپ دیگهای وابسته بشه
|
||||
(دقیقاً همون الگوی ارجاع سبک با id که در کل پروژه استفاده شده).
|
||||
"""
|
||||
label = models.CharField(max_length=50, db_index=True) # مثل 'payment.Order'
|
||||
object_id = models.IntegerField(db_index=True)
|
||||
old_status = models.CharField(max_length=30, blank=True)
|
||||
new_status = models.CharField(max_length=30)
|
||||
event = models.CharField(max_length=50) # چه رویدادی باعث تغییر شده
|
||||
user_id = models.IntegerField(null=True, blank=True, db_index=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-created_at']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.label}#{self.object_id}: {self.old_status}→{self.new_status} ({self.event})"
|
||||
16
statuslog/receivers.py
Normal file
16
statuslog/receivers.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from .models import StatusChangeLog
|
||||
from .signals import status_changed
|
||||
|
||||
|
||||
def _log_status_change(sender, **kwargs):
|
||||
StatusChangeLog.objects.create(
|
||||
label=sender,
|
||||
object_id=kwargs['object_id'],
|
||||
old_status=kwargs.get('old_status', ''),
|
||||
new_status=kwargs['new_status'],
|
||||
event=kwargs['event'],
|
||||
user_id=kwargs.get('user_id'),
|
||||
)
|
||||
|
||||
|
||||
status_changed.connect(_log_status_change, dispatch_uid='statuslog_log_status_change')
|
||||
27
statuslog/signals.py
Normal file
27
statuslog/signals.py
Normal file
@@ -0,0 +1,27 @@
|
||||
"""
|
||||
سیگنال عمومیِ تغییر Status، مستقل از هر اپ خاص (مثل الگوی notifications که
|
||||
هیچ اپی بهش رفرنس نمیده، ولی برعکسش: اینجا خودِ اپها چیزی از statuslog
|
||||
import نمیکنن جز همین سیگنال، و منطق ثبت لاگ کاملاً داخل statuslog میمونه).
|
||||
|
||||
هر اپی که یک فیلد status داره (Order، Subscription، UserOffer، در آینده
|
||||
پیشرفت درس/دوره و ...) موقع تغییر status باید این سیگنال رو send کنه:
|
||||
|
||||
from statuslog.signals import status_changed
|
||||
status_changed.send(
|
||||
sender='payment.Order', # رشتهی label مدل، نه خودِ کلاس
|
||||
object_id=order.id,
|
||||
old_status=old_status,
|
||||
new_status=order.status,
|
||||
event='payment_webhook_paid', # چه اتفاقی باعث تغییر شده
|
||||
user_id=order.user_id, # اختیاری
|
||||
)
|
||||
|
||||
چون send با sender رشتهای (نه کلاس مدل) صدا زده میشه، هیچ اپی لازم نیست
|
||||
مدل واقعیِ اپ دیگه رو import کنه؛ دقیقاً همون اصل «ارجاع سبک، بدون FK واقعی»
|
||||
که در کل پروژه رعایت شده.
|
||||
"""
|
||||
from django.dispatch import Signal
|
||||
|
||||
# kwargs: sender (str, مثل 'payment.Order')، object_id، old_status، new_status،
|
||||
# event، user_id (اختیاری)
|
||||
status_changed = Signal()
|
||||
Reference in New Issue
Block a user