~/icsd.ir — bash
SYSTEM_ONLINE

🔐 فصل ۷: احراز هویت

جنگو یه سیستم احراز هویت کامل و آماده داره - فقط کافیه استفاده کنی!

جنگو یه سیستم احراز هویت کامل و آماده داره – فقط کافیه استفاده کنی!

👤 مدل User جنگو

جنگو یه مدل User آماده داره با این فیلدها:

  • username – نام کاربری (یکتا)
  • email – ایمیل
  • password – رمز عبور (هش‌شده)
  • first_name و last_name
  • is_active – فعال بودن حساب
  • is_staff – دسترسی به ادمین
  • is_superuser – مدیر کل
  • date_joined – تاریخ عضویت
  • last_login – آخرین ورود
Django Shell
from django.contrib.auth.models import User

# ساخت کاربر معمولی
user = User.objects.create_user(
    username='ali',
    email='ali@example.com',
    password='SecurePass123'
)

# ساخت ادمین
admin = User.objects.create_superuser(
    username='admin',
    email='admin@site.com',
    password='AdminPass123'
)

# بررسی رمز عبور
user.check_password('SecurePass123')  # True یا False

# تغییر رمز عبور
user.set_password('NewPass456')
user.save()
🔒

هرگز رمز رو مستقیم ست نکن!

اگه user.password = '123' بنویسی، رمز هش نمی‌شه. حتماً از set_password یا create_user استفاده کن.

📝 ثبت‌نام کاربر

accounts/forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class RegisterForm(UserCreationForm):
    email = forms.EmailField(required=True)
    first_name = forms.CharField(max_length=30, required=True, label='نام')
    last_name = forms.CharField(max_length=30, required=True, label='نام خانوادگی')
    
    class Meta:
        model = User
        fields = ['username', 'first_name', 'last_name', 'email', 'password1', 'password2']
    
    def clean_email(self):
        email = self.cleaned_data['email']
        if User.objects.filter(email=email).exists():
            raise forms.ValidationError('این ایمیل قبلاً ثبت شده')
        return email
accounts/views.py
from django.shortcuts import render, redirect
from django.contrib.auth import login
from django.contrib import messages
from .forms import RegisterForm

def register(request):
    if request.method == 'POST':
        form = RegisterForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)  # ← ورود خودکار بعد از ثبت‌نام
            messages.success(request, f'حساب کاربری {user.username} ساخته شد!')
            return redirect('home')
    else:
        form = RegisterForm()
    
    return render(request, 'accounts/register.html', {'form': form})

🚪 ورود و خروج

روش ۱: استفاده از ویوهای آماده جنگو

myproject/urls.py
from django.urls import path, include

urlpatterns = [
    # ...
    path('accounts/', include('django.contrib.auth.urls')),
]

این به‌طور خودکار این URLها رو اضافه می‌کنه:

  • accounts/login/
  • accounts/logout/
  • accounts/password_change/
  • accounts/password_reset/
  • و چند تای دیگه…

فقط باید قالب‌هاش رو بسازی (templates/registration/login.html و…)

روش ۲: ساخت دستی

accounts/views.py
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from django.contrib import messages

def user_login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        
        user = authenticate(request, username=username, password=password)
        
        if user is not None:
            login(request, user)
            messages.success(request, f'خوش آمدی {user.username}!')
            
            # هدایت به صفحه قبلی یا خانه
            next_url = request.GET.get('next', 'home')
            return redirect(next_url)
        else:
            messages.error(request, 'نام کاربری یا رمز عبور اشتباه است')
    
    return render(request, 'accounts/login.html')

def user_logout(request):
    logout(request)
    messages.info(request, 'با موفقیت خارج شدید')
    return redirect('home')

قالب لاگین

accounts/login.html
<form method="POST" class="login-form">
    {% csrf_token %}
    
    <div class="mb-3">
        <label class="form-label">نام کاربری</label>
        <input type="text" name="username" class="form-control" required>
    </div>
    
    <div class="mb-3">
        <label class="form-label">رمز عبور</label>
        <input type="password" name="password" class="form-control" required>
    </div>
    
    <input type="hidden" name="next" value="{{ request.GET.next }}">
    
    <button type="submit" class="btn btn-primary">ورود</button>
</form>

🛡️ محدود کردن دسترسی

۱. login_required – فقط کاربر وارد شده

views.py
from django.contrib.auth.decorators import login_required

@login_required
def dashboard(request):
    return render(request, 'dashboard.html')

# هدایت به صفحه دلخواه برای ورود
@login_required(login_url='/login/')
def profile(request):
    return render(request, 'profile.html')

۲. permission_required – دسترسی خاص

views.py
from django.contrib.auth.decorators import permission_required

@permission_required('products.add_product')
def add_product(request):
    # فقط کسی که اجازه add_product داره
    pass

@permission_required(['products.add_product', 'products.change_product'])
def manage_products(request):
    # نیاز به هر دو دسترسی
    pass

۳. user_passes_test – شرط دلخواه

views.py
from django.contrib.auth.decorators import user_passes_test

def is_admin(user):
    return user.is_authenticated and user.is_staff

@user_passes_test(is_admin)
def admin_panel(request):
    return render(request, 'admin_panel.html')

۴. در CBV – استفاده از Mixin

views.py
from django.contrib.auth.mixins import (
    LoginRequiredMixin,
    PermissionRequiredMixin,
    UserPassesTestMixin
)
from django.views.generic import ListView

class DashboardView(LoginRequiredMixin, ListView):
    model = Order
    template_name = 'dashboard.html'
    login_url = '/login/'

class AdminView(UserPassesTestMixin, ListView):
    model = User
    
    def test_func(self):
        return self.request.user.is_staff

تنظیمات احراز هویت

myproject/settings.py
# آدرس صفحه ورود (پیش‌فرض)
LOGIN_URL = '/accounts/login/'

# آدرس بعد از ورود
LOGIN_REDIRECT_URL = '/'

# آدرس بعد از خروج
LOGOUT_REDIRECT_URL = '/'

👥 گروه‌ها و دسترسی‌ها

جنگو سیستم پیچیده‌ای برای مدیریت دسترسی داره:

Django Shell
from django.contrib.auth.models import User, Group, Permission
from django.contrib.contenttypes.models import ContentType
from products.models import Product

# ساخت گروه
editors = Group.objects.create(name='ویراستارها')

# اضافه کردن کاربر به گروه
user = User.objects.get(username='ali')
user.groups.add(editors)

# گرفتن دسترسی محصول
content_type = ContentType.objects.get_for_model(Product)
permission = Permission.objects.get(
    codename='change_product',
    content_type=content_type
)

# اضافه کردن دسترسی به گروه
editors.permissions.add(permission)

# اضافه کردن دسترسی مستقیم به کاربر
user.user_permissions.add(permission)

# بررسی دسترسی
user.has_perm('products.change_product')  # True یا False

دسترسی‌های پیش‌فرض

برای هر مدل، جنگو ۴ دسترسی می‌سازه:

  • add_modelname – افزودن
  • change_modelname – ویرایش
  • delete_modelname – حذف
  • view_modelname – مشاهده

در قالب چک کنی

قالب
{% if user.is_authenticated %}
    سلام {{ user.username }}
{% else %}
    <a href="{% url 'login' %}">ورود</a>
{% endif %}

{% if perms.products.add_product %}
    <a href="{% url 'add_product' %}">افزودن محصول</a>
{% endif %}

{% if user.is_staff %}
    <a href="/admin/">پنل ادمین</a>
{% endif %}

🎨 مدل کاربر سفارشی

اگه می‌خوای فیلدهای بیشتری به User اضافه کنی، دو روش داری:

روش ۱: Profile جدا (ساده)

accounts/models.py
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    phone = models.CharField(max_length=11, blank=True)
    bio = models.TextField(blank=True)
    avatar = models.ImageField(upload_to='avatars/', blank=True)
    birth_date = models.DateField(null=True, blank=True)
    
    def __str__(self):
        return f'پروفایل {self.user.username}'

# سیگنال - وقتی کاربر ساخته شد، پروفایل هم ساخته بشه
@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)

روش ۲: AbstractUser (حرفه‌ای)

این روش بهتره ولی باید از اول پروژه باشه:

accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    phone = models.CharField(max_length=11, blank=True)
    avatar = models.ImageField(upload_to='avatars/', blank=True)
    birth_date = models.DateField(null=True, blank=True)
    
    def __str__(self):
        return self.username
myproject/settings.py
# مدل کاربر سفارشی
AUTH_USER_MODEL = 'accounts.CustomUser'
⚠️

خیلی مهم!

AbstractUser رو فقط در شروع پروژه قبل از اولین migrate استفاده کن. در غیر این صورت کلی دردسر داره.

🔑 بازیابی رمز عبور

جنگو سیستم آماده‌ای داره! فقط کافیه تنظیمات ایمیل رو انجام بدی:

myproject/settings.py
# تنظیمات ایمیل
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'your-email@gmail.com'
EMAIL_HOST_PASSWORD = 'your-password'

# برای تست در حالت توسعه - ایمیل در کنسول چاپ بشه
# EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

حالا URLهای آماده رو اضافه کن (اگه قبلاً نکردی) و قالب‌هاش رو بساز.

🎯 مثال‌های کاربردی

آسان

مثال ۱: navbar با وضعیت لاگین

قالب
<nav>
    {% if user.is_authenticated %}
        <span>سلام {{ user.first_name|default:user.username }}</span>
        <a href="{% url 'profile' %}">پروفایل</a>
        <a href="{% url 'logout' %}">خروج</a>
    {% else %}
        <a href="{% url 'login' %}">ورود</a>
        <a href="{% url 'register' %}">ثبت‌نام</a>
    {% endif %}
</nav>

متوسط

مثال ۲: محدود کردن دسترسی به سفارشات کاربر

orders/views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseForbidden
from .models import Order

@login_required
def my_orders(request):
    # فقط سفارشات کاربر فعلی
    orders = Order.objects.filter(user=request.user).order_by('-created_at')
    return render(request, 'orders/list.html', {'orders': orders})

@login_required
def order_detail(request, order_id):
    order = get_object_or_404(Order, id=order_id)
    
    # کاربر فقط سفارش خودش رو ببینه
    if order.user != request.user and not request.user.is_staff:
        return HttpResponseForbidden('شما به این سفارش دسترسی ندارید')
    
    return render(request, 'orders/detail.html', {'order': order})

پیشرفته

مثال ۳: ورود با موبایل و OTP

accounts/views.py
import random
from django.shortcuts import render, redirect
from django.contrib.auth import login
from django.contrib.auth.models import User
from django.core.cache import cache

def request_otp(request):
    if request.method == 'POST':
        phone = request.POST.get('phone')
        
        # ساخت کد ۶ رقمی
        otp_code = random.randint(100000, 999999)
        
        # ذخیره در کش با انقضای ۲ دقیقه
        cache.set(f'otp_{phone}', otp_code, 120)
        
        # ارسال SMS (اینجا فقط چاپ می‌کنیم)
        print(f'کد ارسال‌شده به {phone}: {otp_code}')
        # send_sms(phone, f'کد ورود: {otp_code}')
        
        request.session['otp_phone'] = phone
        return redirect('verify_otp')
    
    return render(request, 'accounts/request_otp.html')

def verify_otp(request):
    phone = request.session.get('otp_phone')
    if not phone:
        return redirect('request_otp')
    
    if request.method == 'POST':
        entered_code = request.POST.get('code')
        cached_code = cache.get(f'otp_{phone}')
        
        if cached_code and str(cached_code) == entered_code:
            # یافتن یا ساخت کاربر
            user, created = User.objects.get_or_create(
                username=phone,
                defaults={'first_name': 'کاربر'}
            )
            
            # ورود
            login(request, user)
            cache.delete(f'otp_{phone}')
            del request.session['otp_phone']
            
            return redirect('home')
        else:
            return render(request, 'accounts/verify_otp.html', {
                'error': 'کد اشتباه است یا منقضی شده'
            })
    
    return render(request, 'accounts/verify_otp.html')

📝 تمرین‌ها

آسان

تمرین ۱

یه ویو بساز که فقط کاربران لاگین‌شده بتونن ببیننش. اگه کاربر لاگین نباشه، به صفحه ورود هدایت بشه.

متوسط

تمرین ۲

یه فرم تغییر رمز عبور بساز که کاربر رمز فعلی رو وارد کنه و رمز جدید رو تأیید کنه.

پیشرفته

تمرین ۳

یه سیستم احراز هویت دو مرحله‌ای (2FA) با ایمیل بساز. بعد از ورود نام کاربری و رمز، یه کد به ایمیل ارسال بشه.

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.