چه کسی اجازهی چه کاری را دارد؟
احراز هویت میگوید «تو کیستی»؛ مجوز میگوید «اجازهی چه کاری داری». جنگو برای هر مدل چهار مجوز خودکار میسازد: add، change، delete و view. نام کامل هر مجوز به شکل app_label.codename است؛ مثلاً orders.change_order. مجوزها به کاربر یا به Group داده میشوند و کاربر مجوزهای همهی گروههایش را به ارث میبرد.
مجوز سفارشی
class Order(models.Model):
...
class Meta:
permissions = [
("approve_order", "میتواند سفارش را تأیید کند"),
("export_orders", "میتواند خروجی اکسل سفارشها را بگیرد"),
]
بعد از makemigrations و migrate، این مجوزها در ادمین قابل تخصیصاند.
ساخت گروهها با کد، نه با کلیک
# apps/accounts/management/commands/setup_roles.py
from django.contrib.auth.models import Group, Permission
from django.core.management.base import BaseCommand
ROLES = {
"فروش": ["orders.view_order", "orders.change_order", "orders.approve_order"],
"انبار": ["catalog.view_carpet", "catalog.change_carpet"],
"حسابداری": ["orders.view_order", "orders.export_orders"],
}
class Command(BaseCommand):
def handle(self, *args, **opts):
for name, perms in ROLES.items():
group, _ = Group.objects.get_or_create(name=name)
objs = []
for p in perms:
app_label, codename = p.split(".")
objs.append(Permission.objects.get(content_type__app_label=app_label, codename=codename))
group.permissions.set(objs)
self.stdout.write(f"گروه {name}: {len(objs)} مجوز")
بررسی مجوز در View و قالب
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin
@permission_required("orders.approve_order", raise_exception=True)
def approve(request, pk): ...
class ExportView(PermissionRequiredMixin, View):
permission_required = "orders.export_orders"
if request.user.has_perm("orders.approve_order"): ...
{% if perms.orders.approve_order %}
<button>تأیید سفارش</button>
{% endif %}
دسترسی سطح شیء
مجوزهای جنگو «سطح مدل» هستند: یا میتوانید همهی سفارشها را تأیید کنید یا هیچکدام را. برای «نمایندهی فروش فقط سفارشهای شهر خودش را» دو راه دارید: ساده و رایج، محدود کردن queryset در get_queryset؛ یا نوشتن یک authentication backend که has_perm(user_obj, perm, obj=None) را با منطق شیء پیاده کند (یا کتابخانههایی مثل django-rules و django-guardian).
نکتههایی که کمتر کسی میداند
- مجوزها روی شیء user کش میشوند؛ اگر در همان درخواست مجوزی اضافه کنید،
has_permهنوز False میدهد تا کاربر را دوباره از پایگاه داده بخوانید. is_superuserهمهی بررسیهایhas_permرا دور میزند، اماis_active=Falseحتی برای سوپریوزر همه را False میکند.- مجوزها در سیگنال
post_migrateساخته میشوند؛ در data migration ممکن است هنوز وجود نداشته باشند. به همین دلیل ساخت گروهها با دستور مدیریتی بعد از migrate امنتر است. raise_exception=Trueبرای کاربر لاگینشدهی بدون مجوز 403 برمیگرداند؛ بدون آن به صفحهی ورود ریدایرکت میشود و کاربر در حلقهی «وارد شدم ولی باز صفحهی ورود» گیر میکند.is_staffفقط اجازهی ورود به ادمین است، نه هیچ مجوز دیگری؛ کاربر staff بدون مجوز در ادمین صفحهای خالی میبیند.