Browse Source

translate enhancement

update models and admin files which had missing lazy text features

update the translation file.
master
Mohsen Taba 2 weeks ago
parent
commit
e661ad8525
  1. 1
      apps/account/admin/location.py
  2. 2
      apps/account/admin/notification.py
  3. 13
      apps/account/admin/professor.py
  4. 11
      apps/account/admin/student.py
  5. 12
      apps/account/admin/user.py
  6. 127
      apps/account/migrations/0003_alter_clientuser_options_and_more.py
  7. 21
      apps/account/models/groups.py
  8. 11
      apps/account/models/notification.py
  9. 81
      apps/account/models/user.py
  10. 36
      apps/certificate/migrations/0002_alter_certificate_course_and_more.py
  11. 8
      apps/certificate/models.py
  12. 15
      apps/course/admin/live_session.py
  13. 31
      apps/course/migrations/0006_alter_course_professor_alter_course_video_file_and_more.py
  14. 8
      apps/course/models/course.py
  15. 794
      locale/ru/LC_MESSAGES/django.po

1
apps/account/admin/location.py

@ -54,6 +54,7 @@ class LocationHistoryAdmin(ModelAdmin):
return instance.at_time.strftime("%Y-%m-%d %H:%M") if instance.at_time else "-"
# Register with both admin sites
project_admin_site.register(LocationHistory, LocationHistoryAdmin)
dovoodi_admin_site.register(LocationHistory, LocationHistoryAdmin)

2
apps/account/admin/notification.py

@ -1,3 +1,5 @@
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from ajaxdatatable.admin import AjaxDatatable
from apps.account.models import User, Notification

13
apps/account/admin/professor.py

@ -19,7 +19,7 @@ from apps.account.models import ProfessorUser
class ProfessorUserCreationForm(UserCreationForm):
phone_number = PhoneNumberField(
help_text="Enter the phone number in international format. Example: +989012023212",
help_text=_("Enter the phone number in international format. Example: +989012023212"),
required=False
)
@ -42,11 +42,11 @@ class ProfessorUserAdmin(UserAdmin, AjaxDatatable):
'classes': ('wide',),
'fields': ('fullname', 'email', 'phone_number',),
}),
('other', {
(_('other'), {
'classes': ('wide',),
'fields': ('avatar', 'info', 'skill'),
}),
('Password', {
(_('Password'), {
'classes': ('wide',),
'fields': ('password1', 'password2'),
}),
@ -72,7 +72,7 @@ class ProfessorUserAdmin(UserAdmin, AjaxDatatable):
if existing_user:
# If user exists and is already a professor, show error
if existing_user.user_type == User.UserType.PROFESSOR:
messages.error(request, f"A professor with the email {email} already exists.")
messages.error(request, _("A professor with the email {email} already exists.").format(email=email))
return
# اضافه کردن نقش professor بدون حذف نقش‌های قبلی
@ -93,7 +93,7 @@ class ProfessorUserAdmin(UserAdmin, AjaxDatatable):
existing_user.save()
# Show success message
messages.success(request, f"The user with email {email} has been converted to a professor.")
messages.success(request, _("The user with email {email} has been converted to a professor.").format(email=email))
# Set obj to None to prevent further processing
obj = None
@ -106,11 +106,12 @@ class ProfessorUserAdmin(UserAdmin, AjaxDatatable):
obj.add_role('professor')
super().save_model(request, obj, form, change)
@admin.display(description='Phone Number')
@admin.display(description=_('Phone Number'))
def _phone_number(self, obj):
return obj.phone_number
def get_readonly_fields(self, request, obj=None):
"""
Restrict the ability to modify groups to superusers only.

11
apps/account/admin/student.py

@ -1,7 +1,7 @@
from django.contrib import admin
from django.contrib.auth.forms import UserChangeForm, UsernameField
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_lazy as _, ngettext
from rest_framework.authtoken.models import TokenProxy
from ajaxdatatable.admin import AjaxDatatable
from unfold.admin import TabularInline, StackedInline
@ -29,11 +29,11 @@ class StudentUserAdmin(UserAdmin, AjaxDatatable):
'classes': ('wide',),
'fields': ('fullname', 'email', 'phone_number',),
}),
('other', {
(_('other'), {
'classes': ('wide',),
'fields': ('avatar', 'info'),
}),
('Password', {
(_('Password'), {
'classes': ('wide',),
'fields': ('password1', 'password2'),
}),
@ -48,7 +48,7 @@ class StudentUserAdmin(UserAdmin, AjaxDatatable):
}),
(_('Important dates'), {'fields': ('last_login', 'date_joined', 'fcm')}),
)
@admin.display(description='Phone Number')
@admin.display(description=_('Phone Number'))
def _phone_number(self, obj):
return obj.phone_number
@ -56,7 +56,8 @@ class StudentUserAdmin(UserAdmin, AjaxDatatable):
def enrolled_courses_count(self, obj):
"""نمایش تعداد دوره‌های شرکت کرده"""
count = obj.participated_courses.filter(is_active=True).count()
return f"{count} دوره"
return ngettext("{count} course", "{count} courses", count).format(count=count)
def get_queryset(self, request):

12
apps/account/admin/user.py

@ -5,7 +5,7 @@ from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
from django.contrib.auth.models import Group
from django.db import models
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_lazy as _, ngettext
from django.templatetags.static import static
from rest_framework.authtoken.models import TokenProxy
@ -278,7 +278,7 @@ class StudentUserAdmin(UserAdmin):
birthdate = instance.birthdate
age = today.year - birthdate.year - ((today.month, today.day) < (birthdate.month, birthdate.day))
formatted_date = birthdate.strftime("%Y-%m-%d")
return format_html('<span title="{}">{}</span>', f"Born on {formatted_date}", age)
return format_html('<span title="{}">{}</span>', _("Born on {date}").format(date=formatted_date), age)
@display(description=_("Courses"), dropdown=True)
def courses_count(self, instance: StudentUser):
@ -304,11 +304,12 @@ class StudentUserAdmin(UserAdmin):
return "-"
return {
"title": f"{total} {_('courses')}",
"title": ngettext("{total} course", "{total} courses", total).format(total=total),
"items": items,
"striped": True,
}
def get_queryset(self, request):
return super().get_queryset(request).prefetch_related(
"participated_courses",
@ -443,11 +444,12 @@ class ProfessorUserAdmin(UserAdmin):
return "-"
return {
"title": f"{total} {_('courses')}",
"title": ngettext("{total} course", "{total} courses", total).format(total=total),
"items": items,
"striped": True,
}
def get_queryset(self, request):
return super().get_queryset(request).prefetch_related("courses")
@ -466,7 +468,7 @@ class GroupAdmin(BaseGroupAdmin, ModelAdmin):
@display(description=_("Permissions"))
def permissions_count(self, obj):
count = obj.permissions.count()
return f"{count} {_('permissions')}" if count > 0 else "-"
return ngettext("{count} permission", "{count} permissions", count).format(count=count) if count > 0 else "-"
# =========================================================

127
apps/account/migrations/0003_alter_clientuser_options_and_more.py

@ -0,0 +1,127 @@
# Generated by Django 5.2.12 on 2026-05-04 12:53
import dj_language.field
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0002_alter_user_email_alter_user_username'),
('dj_language', '0002_auto_20220120_1344'),
]
operations = [
migrations.AlterModelOptions(
name='clientuser',
options={'ordering': ('-id',), 'verbose_name': 'User', 'verbose_name_plural': 'Users'},
),
migrations.AlterModelOptions(
name='locationhistory',
options={'verbose_name': 'Location History', 'verbose_name_plural': 'Location History'},
),
migrations.AlterModelOptions(
name='loginhistory',
options={'verbose_name': 'Login History', 'verbose_name_plural': 'Login Histories'},
),
migrations.AlterModelOptions(
name='notification',
options={'verbose_name': 'Notification', 'verbose_name_plural': 'Notifications'},
),
migrations.AlterModelOptions(
name='user',
options={'ordering': ('-id',), 'verbose_name': 'User', 'verbose_name_plural': 'Users'},
),
migrations.AlterField(
model_name='locationhistory',
name='at_time',
field=models.DateTimeField(auto_now_add=True, verbose_name='at time'),
),
migrations.AlterField(
model_name='locationhistory',
name='ip',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='ip'),
),
migrations.AlterField(
model_name='locationhistory',
name='selected_manually',
field=models.BooleanField(blank=True, null=True, verbose_name='selected manually'),
),
migrations.AlterField(
model_name='locationhistory',
name='timezone',
field=models.CharField(blank=True, max_length=60, null=True, verbose_name='timezone'),
),
migrations.AlterField(
model_name='locationhistory',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='location_history', to=settings.AUTH_USER_MODEL, verbose_name='user'),
),
migrations.AlterField(
model_name='loginhistory',
name='at_time',
field=models.DateTimeField(auto_now_add=True, verbose_name='at time'),
),
migrations.AlterField(
model_name='loginhistory',
name='device_os',
field=models.CharField(blank=True, max_length=16, null=True, verbose_name='Device os'),
),
migrations.AlterField(
model_name='loginhistory',
name='ip',
field=models.CharField(max_length=255, null=True, verbose_name='ip'),
),
migrations.AlterField(
model_name='loginhistory',
name='timezone',
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='timezone'),
),
migrations.AlterField(
model_name='loginhistory',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='login_history', to=settings.AUTH_USER_MODEL, verbose_name='user'),
),
migrations.AlterField(
model_name='user',
name='avatar',
field=models.ImageField(blank=True, max_length=512, null=True, upload_to='users/avatars/%Y/%m/', verbose_name='Avatar'),
),
migrations.AlterField(
model_name='user',
name='deleted_at',
field=models.DateTimeField(blank=True, null=True, verbose_name='deleted at'),
),
migrations.AlterField(
model_name='user',
name='device_os',
field=models.CharField(choices=[('android', 'android'), ('apple', 'apple'), ('web', 'web')], max_length=16, null=True, verbose_name='device os'),
),
migrations.AlterField(
model_name='user',
name='fcm',
field=models.CharField(blank=True, max_length=512, null=True, verbose_name='fcm'),
),
migrations.AlterField(
model_name='user',
name='is_staff',
field=models.BooleanField(default=False, verbose_name='is staff'),
),
migrations.AlterField(
model_name='user',
name='language',
field=dj_language.field.LanguageField(default=69, limit_choices_to={'status': True}, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='dj_language.language', verbose_name='Language'),
),
migrations.AlterField(
model_name='user',
name='skill',
field=models.CharField(blank=True, max_length=512, null=True, verbose_name='skill'),
),
migrations.AlterField(
model_name='user',
name='slug',
field=models.SlugField(blank=True, max_length=255, null=True, unique=True, verbose_name='slug'),
),
]

21
apps/account/models/groups.py

@ -1,3 +1,4 @@
from django.utils.translation import gettext_lazy as _
from apps.account.models import User
from apps.account.manager import *
@ -19,8 +20,8 @@ class ProfessorUser(User):
class Meta:
proxy = True
verbose_name = "Professor User"
verbose_name_plural = "Professor Users"
verbose_name = _("Professor User")
verbose_name_plural = _("Professor Users")
@ -39,8 +40,8 @@ class ClientUser(User):
class Meta:
proxy = True
verbose_name = 'user'
verbose_name_plural = 'users'
verbose_name = _('User')
verbose_name_plural = _('Users')
ordering = ('-id',)
@ -57,8 +58,8 @@ class AdminUser(User):
class Meta:
proxy = True
verbose_name = "Admin User"
verbose_name_plural = "Admin Users"
verbose_name = _("Admin User")
verbose_name_plural = _("Admin Users")
@ -74,8 +75,8 @@ class SuperAdminUser(User):
class Meta:
proxy = True
verbose_name = "Super Admin User"
verbose_name_plural = "Super Admin Users"
verbose_name = _("Super Admin User")
verbose_name_plural = _("Super Admin Users")
@ -91,5 +92,5 @@ class StudentUser(User):
class Meta:
proxy = True
verbose_name = "Student User"
verbose_name_plural = "Student Users"
verbose_name = _("Student User")
verbose_name_plural = _("Student Users")

11
apps/account/models/notification.py

@ -1,11 +1,11 @@
from django.db import models
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy as _
class Notification(models.Model):
class ServiceChoices(models.TextChoices):
IMAM_JAVAD = 'imam-javad', 'Imam Javad'
DOBOODI = 'doboodi', 'Doboodi'
IMAM_JAVAD = 'imam-javad', _('Imam Javad')
DOBOODI = 'doboodi', _('Doboodi')
title = models.CharField(max_length=255, verbose_name=_('title'))
message = models.TextField(max_length=512, verbose_name=_('message'))
@ -20,6 +20,11 @@ class Notification(models.Model):
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at'), null=True)
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at'), null=True)
class Meta:
verbose_name = _('Notification')
verbose_name_plural = _('Notifications')
def __str__(self):
return self.title

81
apps/account/models/user.py

@ -14,20 +14,20 @@ from apps.account.manager import UserManager
class User(AbstractUser):
class DeviceOs(models.TextChoices):
android = 'android', 'android'
apple = 'apple', 'apple'
web = 'web', 'web'
android = 'android', _('android')
apple = 'apple', _('apple')
web = 'web', _('web')
class UserType(models.TextChoices):
PROFESSOR = 'professor', 'Professor'
CLIENT = 'client', 'Client'
STUDENT = 'student', "Student"
ADMIN = 'admin', 'Admin'
SUPER_ADMIN = 'super_admin', 'Super Admin'
PROFESSOR = 'professor', _('Professor')
CLIENT = 'client', _('Client')
STUDENT = 'student', _("Student")
ADMIN = 'admin', _('Admin')
SUPER_ADMIN = 'super_admin', _('Super Admin')
class GenderChoices(models.TextChoices):
MALE = 'male', 'Male'
FEMALE = 'female', 'Female'
MALE = 'male', _('Male')
FEMALE = 'female', _('Female')
last_name = None
first_name = None
@ -43,36 +43,36 @@ class User(AbstractUser):
null=True, blank=True)
birthdate = models.DateField(verbose_name=_('birthdate'), null=True, blank=True)
avatar = models.ImageField(max_length=512, null=True, blank=True, upload_to='users/avatars/%Y/%m/')
avatar = models.ImageField(max_length=512, null=True, blank=True, upload_to='users/avatars/%Y/%m/', verbose_name=_('Avatar'))
phone_number = PhoneNumberField(
validators=[validate_possible_number],
null=True,
blank=True,
verbose_name=_('Phone Number'),
help_text="e.g., +49 151 12345678"
help_text=_("e.g., +49 151 12345678")
)
language = LanguageField(null=True)
language = LanguageField(null=True, verbose_name=_('Language'))
gender = models.CharField(max_length=20, choices=GenderChoices.choices, null=True, blank=True, verbose_name=_('Gender'), help_text="Select the user's gender.")
user_type = models.CharField(max_length=20, choices=UserType.choices, default=UserType.CLIENT, verbose_name="User Type", help_text="Type of the user.")
date_joined = models.DateTimeField(auto_now_add=True, verbose_name="Date Joined", help_text="The date and time the user registered.")
gender = models.CharField(max_length=20, choices=GenderChoices.choices, null=True, blank=True, verbose_name=_('Gender'), help_text=_("Select the user's gender."))
user_type = models.CharField(max_length=20, choices=UserType.choices, default=UserType.CLIENT, verbose_name=_("User Type"), help_text=_("Type of the user."))
date_joined = models.DateTimeField(auto_now_add=True, verbose_name=_("Date Joined"), help_text=_("The date and time the user registered."))
city = models.CharField(verbose_name=_('City'), max_length=255, null=True, blank=True)
country = models.CharField(max_length=255, verbose_name=_('country'), null=True, blank=True)
device_id = models.CharField(verbose_name=_('device id'), max_length=255, null=True, blank=True)
device_os = models.CharField(choices=DeviceOs.choices, null=True, max_length=16)
device_os = models.CharField(choices=DeviceOs.choices, null=True, max_length=16, verbose_name=_('device os'))
user_agent = models.TextField(verbose_name=_('user agent'), null=True, blank=True)
client_ip = models.TextField(verbose_name=_('client ip'), null=True, blank=True)
fcm = models.CharField(max_length=512, null=True, blank=True)
slug = models.SlugField(max_length=255, unique=True, null=True, blank=True)
fcm = models.CharField(max_length=512, null=True, blank=True, verbose_name=_('fcm'))
slug = models.SlugField(max_length=255, unique=True, null=True, blank=True, verbose_name=_('slug'))
experience_years = models.PositiveIntegerField(default=0, verbose_name=_('Experience years'))
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True, verbose_name="Active", help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.")
deleted_at = models.DateTimeField(null=True, blank=True)
info = models.TextField(verbose_name="Info", null=True, blank=True)
skill = models.CharField(max_length=512, null=True, blank=True)
is_staff = models.BooleanField(default=False, verbose_name=_('is staff'))
is_active = models.BooleanField(default=True, verbose_name=_("Active"), help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts."))
deleted_at = models.DateTimeField(null=True, blank=True, verbose_name=_('deleted at'))
info = models.TextField(verbose_name=_("Info"), null=True, blank=True)
skill = models.CharField(max_length=512, null=True, blank=True, verbose_name=_('skill'))
objects = UserManager()
@ -286,8 +286,8 @@ class User(AbstractUser):
class Meta:
ordering = ("-id",)
verbose_name = "All Users"
verbose_name_plural = "All Users"
verbose_name = _("User")
verbose_name_plural = _("Users")
unique_together = (
'email',
)
@ -295,26 +295,35 @@ class User(AbstractUser):
class LoginHistory(models.Model):
user = models.ForeignKey("account.User", on_delete=models.CASCADE, related_name='login_history')
user = models.ForeignKey("account.User", on_delete=models.CASCADE, related_name='login_history', verbose_name=_('user'))
lat = models.FloatField(verbose_name=_('lat'), null=True, blank=True)
lon = models.FloatField(verbose_name=_('lon'), null=True, blank=True)
country = models.CharField(max_length=255, verbose_name=_('country'), null=True, blank=True)
city = models.CharField(max_length=255, verbose_name=_('city'), null=True, blank=True)
ip = models.CharField(max_length=255, null=True)
timezone = models.CharField(max_length=100, null=True, blank=True)
ip = models.CharField(max_length=255, null=True, verbose_name=_('ip'))
timezone = models.CharField(max_length=100, null=True, blank=True, verbose_name=_('timezone'))
user_agent = models.TextField(verbose_name=_('user agent'), null=True, blank=True)
device_os = models.CharField(max_length=16, null=True, blank=True)
at_time = models.DateTimeField(auto_now_add=True)
device_os = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Device os'))
at_time = models.DateTimeField(auto_now_add=True, verbose_name=_('at time'))
class Meta:
verbose_name = _('Login History')
verbose_name_plural = _('Login Histories')
class LocationHistory(models.Model):
user = models.ForeignKey("account.User", on_delete=models.CASCADE, related_name='location_history')
user = models.ForeignKey("account.User", on_delete=models.CASCADE, related_name='location_history', verbose_name=_('user'))
lat = models.FloatField(verbose_name=_('lat'))
lon = models.FloatField(verbose_name=_('lon'))
country = models.CharField(max_length=255, verbose_name=_('country'), null=True, blank=True)
city = models.CharField(max_length=255, verbose_name=_('city'), null=True, blank=True)
selected_manually = models.BooleanField(null=True, blank=True)
ip = models.CharField(max_length=255, null=True, blank=True)
timezone = models.CharField(null=True, blank=True, max_length=60)
at_time = models.DateTimeField(auto_now_add=True)
selected_manually = models.BooleanField(null=True, blank=True, verbose_name=_('selected manually'))
ip = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('ip'))
timezone = models.CharField(null=True, blank=True, max_length=60, verbose_name=_('timezone'))
at_time = models.DateTimeField(auto_now_add=True, verbose_name=_('at time'))
class Meta:
verbose_name = _('Location History')
verbose_name_plural = _('Location History')

36
apps/certificate/migrations/0002_alter_certificate_course_and_more.py

@ -0,0 +1,36 @@
# Generated by Django 5.2.12 on 2026-05-04 12:53
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0003_alter_clientuser_options_and_more'),
('certificate', '0001_initial'),
('course', '0006_alter_course_professor_alter_course_video_file_and_more'),
]
operations = [
migrations.AlterField(
model_name='certificate',
name='course',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='course_certificates', to='course.course', verbose_name='Course'),
),
migrations.AlterField(
model_name='certificate',
name='created_at',
field=models.DateTimeField(auto_now_add=True, verbose_name='Created at'),
),
migrations.AlterField(
model_name='certificate',
name='status',
field=models.CharField(choices=[('pending', 'pending'), ('approved', 'approved'), ('canceled', 'canceled')], default='pending', max_length=10, verbose_name='Status'),
),
migrations.AlterField(
model_name='certificate',
name='student',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='certificates', to='account.studentuser', verbose_name='Student'),
),
]

8
apps/certificate/models.py

@ -14,12 +14,12 @@ class Certificate(models.Model):
('canceled', _('canceled')),
]
student = models.ForeignKey(StudentUser, on_delete=models.CASCADE, related_name='certificates')
course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='course_certificates')
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='pending')
student = models.ForeignKey(StudentUser, on_delete=models.CASCADE, related_name='certificates', verbose_name=_('Student'))
course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='course_certificates', verbose_name=_('Course'))
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='pending', verbose_name=_('Status'))
certificate_file = models.FileField(upload_to='certificates/', null=True, blank=True, verbose_name=_('certificate_file'))
created_at = models.DateTimeField(auto_now_add=True)
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('Created at'))
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):

15
apps/course/admin/live_session.py

@ -22,6 +22,15 @@ from django.contrib.auth import get_user_model
User = get_user_model()
# 🔔 CUSTOM FILTER: Only show active, non-guest users in the right sidebar filter
class ActiveUserDropdownFilter(MultipleRelatedDropdownFilter):
def field_choices(self, field, request, model_admin):
return field.get_choices(
include_blank=False,
limit_choices_to={'is_active': True, 'email__isnull': False}
)
# --- WIDTH ENFORCEMENT & PLACEHOLDER TEXT FOR DROPDOWNS ---
class MinWidthInlineForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
@ -62,7 +71,7 @@ class LiveSessionUserInline(StackedInline):
verbose_name = _("Session User")
verbose_name_plural = _("Session Users")
# 🔔 FILTER THE USER DROPDOWN TO ONLY SHOW ACTIVE & NON-GUEST USERS
# 🔔 FILTER THE USER DROPDOWN IN THE FORM ITSELF
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "user":
kwargs["queryset"] = User.objects.filter(is_active=True, email__isnull=False)
@ -113,7 +122,7 @@ class LiveSessionUserAdmin(ModelAdmin):
list_display = ("user", "session", "role", "is_online", "entered_at", "exited_at")
list_filter = (
("session", MultipleRelatedDropdownFilter),
("user", MultipleRelatedDropdownFilter),
("user", ActiveUserDropdownFilter), # 🔔 APPLIED CUSTOM FILTER HERE!
("role", ChoicesDropdownFilter),
("entered_at", RangeDateFilter),
("is_online", admin.BooleanFieldListFilter),
@ -134,7 +143,7 @@ class LiveSessionUserAdmin(ModelAdmin):
def get_role_choices(self, request):
return USER_ROLE_CHOICES
# 🔔 FILTER THE STANDALONE ADMIN FORM AS WELL JUST IN CASE
# FILTER THE STANDALONE ADMIN FORM AS WELL JUST IN CASE
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "user":
kwargs["queryset"] = User.objects.filter(is_active=True, email__isnull=False)

31
apps/course/migrations/0006_alter_course_professor_alter_course_video_file_and_more.py

@ -0,0 +1,31 @@
# Generated by Django 5.2.12 on 2026-05-04 12:53
import apps.course.models.course
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0003_alter_clientuser_options_and_more'),
('course', '0005_alter_course_discount_percentage'),
]
operations = [
migrations.AlterField(
model_name='course',
name='professor',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='courses', to='account.professoruser', verbose_name='Professor'),
),
migrations.AlterField(
model_name='course',
name='video_file',
field=models.FileField(blank=True, null=True, upload_to=apps.course.models.course.course_file_upload_to, verbose_name='Video File'),
),
migrations.AlterField(
model_name='course',
name='video_link',
field=models.CharField(blank=True, max_length=500, null=True, verbose_name='Video Link'),
),
]

8
apps/course/models/course.py

@ -69,7 +69,8 @@ class Course(models.Model):
professor = models.ForeignKey(
ProfessorUser,
on_delete=models.CASCADE,
related_name="courses"
related_name="courses",
verbose_name=_("Professor")
)
thumbnail = models.ImageField(upload_to="courses/thumbnails/", verbose_name=_('Thumbnail'))
@ -81,9 +82,10 @@ class Course(models.Model):
video_file = models.FileField(
upload_to=course_file_upload_to,
null=True,
blank=True
blank=True,
verbose_name=_("Video File")
)
video_link = models.CharField(max_length=500, null=True, blank=True)
video_link = models.CharField(max_length=500, null=True, blank=True, verbose_name=_("Video Link"))
is_online = models.BooleanField(default=False, verbose_name=_('Is Online Course'))
online_link = models.CharField(max_length=500, null=True, blank=True, verbose_name=_('Online Class Link'))

794
locale/ru/LC_MESSAGES/django.po
File diff suppressed because it is too large
View File

Loading…
Cancel
Save