Django

چگونه مدل کاربر جنگو را گسترش دهیم

چگونه مدل کاربر جنگو را گسترش دهیم

مدل کاربر جنگو چیست؟گسترش مدل کاربر جنگو چیست؟ سیستم احراز هویت داخلی جنگو عالیه ولی تو بیشتر موارد می‌تونیم خارج از قاعده مدل کاربری جنگو هم از سیستم استفاده کنیم.این سیستم تو اکثر موارد استفاده، مناسب و بسیار ایمن هست. اما گاهی اوقات ما نیاز داریم تا تنظیمات دیگری رو انجام بدیم تا متناسب با برنامه وب ما باشه.

معمولاً ما می خوایم چند داده دیگه مربوط به کاربر خودمون رو ذخیره کنیم. اگر برنامه وب شما کابری اجتماعی داره، ممکنه بخواید یک بیوگرافی کوتاه، مکان کاربر و موارد دیگه ای مثل این رو ذخیره کنید.

تو این آموزش، استراتژی‌هایی رو که می‌تونید برای گسترش مدل کاربر جنگو که پیش فرض وجود داره استفاده کنید، ارائه می‌دم، پس نیازی به پیاده‌سازی همه چیز از ابتدا نداریم.

چطوری مدل کاربر جنگو رو گسترش بدیم؟راه هایی که برای گسترش مدل کاربر جنگو استفاده می شود چیست؟

به طور کلی، چهار راه مختلف برای گسترش مدل کاربر موجود وجود داره. دلیل و زمان استفاده از اون ها رو در ادامه توضیح میدم.

گزینه 1: استفاده از مدل پروکسی(Proxy Model)

مدل پروکسی (Proxy Model) چیست؟

این یک مدل ارثی بدون ایجاد جدول جدید تو پایگاه داده ست. برای تغییر رفتار یک مدل موجود (مثلاً سفارش پیش‌فرض، افزودن روش‌های جدید و غیره) بدون تأثیر بر طرح‌واره پایگاه داده موجود استفاده می‌شه.

چه زمانی باید از مدل پروکسی استفاده کنم؟

هنگامی که نیازی به ذخیره اطلاعات اضافی در پایگاه داده ندارید، باید از یک مدل پراکسی(Proxy Model) برای گسترش مدل کاربر موجود استفاده کنید، بلکه به سادگی روش‌های اضافی را اضافه کنید یا مدیریت جستجوی مدل را تغییر دهید.

گزینه 2: استفاده از پیوند یک به یک با مدل کاربر (One-To-One)

لینک یک به یک چیست؟

این یک مدل جنگو معمولیه که جدول پایگاه داده خودش رو دارد و از طریق OneToOneField یک رابطه یک به یک با مدل کاربر موجود برقرار می کنه.

چه زمانی باید از پیوند یک به یک استفاده کنم؟

زمانی که نیاز به ذخیره اطلاعات اضافی در مورد مدل کاربر موجود داریم که به فرآیند احراز هویت مرتبط نیست، باید از پیوند یک به یک استفاده کنیم. ما معمولاً اون رو نمایه کاربر (Profile) می نامیم.

گزینه 3: ایجاد یک مدل کاربر سفارشی با AbstractBaseUser

مدل کاربر سفارشی AbstractBaseUser چیست؟

یک مدل کاربر کاملاً جدیده که از AbstractBaseUser به ارث می رسه.و نیاز به بروزرسانی برخی از مراجع از طریق settings.py داره. تو حالت ایده آل این کار باید در ابتدای پروژه انجام بشه، چون روی طرح واره پایگاه داده تاثیر میزاره و احتمالا اطلاعات موجود در پایگاه داده کلا از دست میره.

چه زمانی باید از یک مدل کاربر سفارشی AbstractBaseUser استفاده کنم؟

زمانی که برنامه دارای الزامات خاصی در رابطه با فرآیند احراز هویت هست، باید از یک مدل کاربر سفارشی استفاده کنیم. به عنوان مثال، در برخی موارد استفاده از آدرس ایمیل به عنوان رمز شناسایی یا به جای نام کاربری منطقی تره.

گزینه 4: ایجاد یک مدل کاربر سفارشی با AbstractUser

مدل کاربر سفارشی AbstractUser چیست؟

این یک مدل کاربر جدید است که از AbstractUser به ارث می رسه.و نیاز به بروزرسانی برخی از مراجع از طریق settings.py داره. تو حالت ایده آل این کار باید در ابتدای پروژه انجام بشه، چون روی طرح واره پایگاه داده تاثیر میزاره و احتمالا اطلاعات موجود در پایگاه داده کلا از دست میره.

چه زمانی باید از یک مدل کاربر سفارشی AbstractUser استفاده کنم؟

وقتی از این گزینه استفاده می کنیم که از نحوه مدیریت جنگو با فرآیند احراز هویت کاملاً راضی هستیم و چیزی رو تو اون تغییر نمیدیم. ولی میخوایم بدون نیاز به ایجاد کلاس اضافی (مثل گزینه 2) مقداری اطلاعات اضافی رو مستقیماً تو مدل کاربر اضافه کنیم.

توسعه مدل کاربر با استفاده از یک مدل پروکسی (Proxy Model)

این روش کمترین زحمت رو برای گسترش مدل کاربر موجود داره این استراتژی هیچ اشکالی نداره ولی از بسیاری جهات بسیار محدوده.

نحوه انجام

from django.contrib.auth.models import User
from .managers import PersonManager

class Person(User):
    objects = PersonManager()

    class Meta:
        proxy = True
        ordering = ('first_name', )

    def do_something(self):
        ...

تو مثال بالا یک مدل پراکسی با نام Person تعریف کردیم. با اضافه کردن ویژگی زیر تو کلاس متا به جنگو میگیم که این یک مدل پراکسی هست: proxy = True.

تو این مورد، من ترتیب پیش‌فرض رو دوباره تعریف کردم، یک مدیر سفارشی به مدل اختصاص دادم، و همچنین یک روش جدید do_something را تعریف کرده‌ام.

دقت کنید که User.objects.all() و Person.objects.all() جدول پایگاه داده رو واکشی می کنن.فقط فرقش تو رفتاریه که ما برای مدل پروکسی تعریف می کنیم.

گسترش مدل کاربر با استفاده از پیوند یک به یک

ما یک مدل جنگو جدید ایجاد میکنیم تا اطلاعات اضافی مربوط به مدل کاربر رو ذخیره کنه.دقت کنید که استفاده از این استراتژی منجر به واکشی یا پیوندهای اضافی برای بازیابی داده های مرتبط می شه. اساساً همه زمانی که به یک داده مرتبط دسترسی پیدا می کنید، جنگو یک درخواست اضافی را ارسال می کنه. اما در اکثر موارد میشه از این کار جلوگیری کرد توضیح خواهم داد.

من معمولا مدل جنگو را به عنوان Profile میسازم:

from django.db import models
from django.contrib.auth.models import User

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(max_length=500, blank=True)
    location = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True)

الان سیگنال‌هایی را تعریف می‌کنیم تا مدل profile ما به‌طور خودکار ایجاد/به‌روزرسانی بشه که نمونه‌های کاربر رو ایجاد/به‌روزرسانی کنیم.

from django.db import models
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)
    bio = models.TextField(max_length=500, blank=True)
    location = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True)

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.profile.save()

اصولا هر وقت که یک رویداد ذخیره رخ بده، ما متدهای create_user_profile و save_user_profile رو به مدل User متصل می کنیم. این نوع سیگنال post_save نامیده می شه.

این هم مثال استفاده در قالب جنگو، بررسی کنید:

<h2>{{ user.get_full_name }}</h2>
<ul>
  <li>Username: {{ user.username }}</li>
  <li>Location: {{ user.profile.location }}</li>
  <li>Birth Date: {{ user.profile.birth_date }}</li>
</ul>

نحوه استفاده در View:

def update_profile(request, user_id):
    user = User.objects.get(pk=user_id)
    user.profile.bio = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit...'
    user.save()

به طور کلی، مجبور نمیشید که روش ذخیره پروفایل رو فراخوانی کنید. همه چیز از طریق مدل User انجام می شه.اگر نیازی به تغییری در فرم جنگو ندارید.

اگر بخوام از Django Forms استفاده کنم چی؟

forms.py

class UserForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ('first_name', 'last_name', 'email')

class ProfileForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ('url', 'location', 'company')

views.py

@login_required
@transaction.atomic
def update_profile(request):
    if request.method == 'POST':
        user_form = UserForm(request.POST, instance=request.user)
        profile_form = ProfileForm(request.POST, instance=request.user.profile)
        if user_form.is_valid() and profile_form.is_valid():
            user_form.save()
            profile_form.save()
            messages.success(request, _('Your profile was successfully updated!'))
            return redirect('settings:profile')
        else:
            messages.error(request, _('Please correct the error below.'))
    else:
        user_form = UserForm(instance=request.user)
        profile_form = ProfileForm(instance=request.user.profile)
    return render(request, 'profiles/profile.html', {
        'user_form': user_form,
        'profile_form': profile_form
    })

profile.html

<form method="post">
  {% csrf_token %}
  {{ user_form.as_p }}
  {{ profile_form.as_p }}
  <button type="submit">Save changes</button>
</form>

و درخواست های اضافی از پایگاه داده که در مورد آنها صحبت می کردید چه شد؟

توضیحات کوتاه: روابط جنگو تنبل هستن. یعنی جنگو فقط در صورت دسترسی به یکی از ویژگی های مرتبط، پایگاه داده را واکشی می کنه که گاهی اوقات باعث ایجاد برخی اثرات نامطلوب می شه، مثل واکشی صدها یا هزاران درخواست. این مشکل را میشه با استفاده از روش select_related کاهش داد.

با دونستن اینکه از قبل باید به یک داده مرتبط دسترسی داشته باشید، می تونید اون رو تو یک کوئری پایگاه داده واحد از قبل واکشی کنید:

users = User.objects.all().select_related('profile')

برای مطالعه مقاله رگرسیون لجستیک چیست؟ بر روی لینک کلیک کنید

امیرحسین باقری

امیرحسین باقری هستم عاشق برنامه نویسی مخصوصا با python و همینطور طراح قالب و متخصص فرانت اند؛ از دانشی که تو این مدت بدست آوردم میشه به HTML, CSS, Bootstrap, Flexbox, SASS, Python, Django, DRF و هوش مصنوعی اشاره کرد.
دکمه بازگشت به بالا