Compare commits
12 Commits
polymorphi
...
33c610dcbc
| Author | SHA1 | Date | |
|---|---|---|---|
| 33c610dcbc | |||
| 1c0b287666 | |||
| cc833c475f | |||
| 09584e22fd | |||
| 97b14ae1d7 | |||
| 18d2604121 | |||
| b32d0fd032 | |||
| bf9f43eed8 | |||
| 18953e06b7 | |||
| b79f0c318a | |||
| c91f603a50 | |||
| f7030e8da1 |
@@ -53,7 +53,6 @@ IMPORT_EXPORT_SKIP_ADMIN_LOG = True
|
|||||||
IMPORT_FORMATS = [CSV]
|
IMPORT_FORMATS = [CSV]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'flightslot.middleware.RedirectNonSuperuserFromAdminMiddleware', # custom middleware to show "user" page to non superuser
|
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
@@ -61,6 +60,7 @@ MIDDLEWARE = [
|
|||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
'flightslot.middleware.RedirectNonSuperuserFromAdminMiddleware', # custom middleware to show "user" page to non superuser
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = 'cntmanage.urls'
|
ROOT_URLCONF = 'cntmanage.urls'
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ INSTALLED_APPS = [
|
|||||||
'colorfield',
|
'colorfield',
|
||||||
'import_export',
|
'import_export',
|
||||||
'django_admin_action_forms',
|
'django_admin_action_forms',
|
||||||
|
'polymorphic'
|
||||||
]
|
]
|
||||||
|
|
||||||
# Import Export plugin settings
|
# Import Export plugin settings
|
||||||
@@ -56,7 +57,6 @@ IMPORT_EXPORT_SKIP_ADMIN_LOG = True
|
|||||||
IMPORT_FORMATS = [CSV]
|
IMPORT_FORMATS = [CSV]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'flightslot.middleware.RedirectNonSuperuserFromAdminMiddleware',
|
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
'whitenoise.middleware.WhiteNoiseMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
@@ -65,6 +65,7 @@ MIDDLEWARE = [
|
|||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
'flightslot.middleware.RedirectNonSuperuserFromAdminMiddleware', # custom middleware to show "user" page to non superuser
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = 'cntmanage.urls'
|
ROOT_URLCONF = 'cntmanage.urls'
|
||||||
|
|||||||
@@ -38,4 +38,4 @@ COPY ./docker/entrypoint.sh ./
|
|||||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||||
|
|
||||||
# Command to be executed after entry point
|
# Command to be executed after entry point
|
||||||
CMD ["gunicorn", "cntmanage.wsgi:application", "--bind", "0.0.0.0:8000"]
|
CMD ["gunicorn", "cntmanage.wsgi:application", "--bind", "0.0.0.0:8000", "--timeout", "600"]
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
|
|||||||
# Each of this iterations fills the table for a student
|
# Each of this iterations fills the table for a student
|
||||||
row: int = 2
|
row: int = 2
|
||||||
row_offset: int = 0
|
row_offset: int = 0
|
||||||
for i, q in enumerate(queryset.order_by("student__surname", "student__name", "student__course"), start=1):
|
for i, q in enumerate(queryset.order_by("week", "student__surname", "student__name", "student__course"), start=1):
|
||||||
student_data: List[str]
|
student_data: List[str]
|
||||||
student_phone: str = q.student.phone if q.student.phone else ""
|
student_phone: str = q.student.phone if q.student.phone else ""
|
||||||
student_email: str = q.student.email
|
student_email: str = q.student.email
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ from .models.weekpref import WeekPreference
|
|||||||
from .admins.course_adm import CourseAdmin
|
from .admins.course_adm import CourseAdmin
|
||||||
from .admins.student_adm import StudentAdmin
|
from .admins.student_adm import StudentAdmin
|
||||||
from .admins.mission_adm import MissionProfileAdmin
|
from .admins.mission_adm import MissionProfileAdmin
|
||||||
from .admins.weekpred_adm import WeekPreferenceAdmin
|
from .admins.weekpref_adm import WeekPreferenceAdmin
|
||||||
#from .admins.hourbuilding_adm import HourBuilding, HourBuildingInLine
|
|
||||||
|
|
||||||
from django.contrib.admin import AdminSite
|
from django.contrib.admin import AdminSite
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ class HourBuildingLegBaseInLine(nested_admin.NestedStackedPolymorphicInline):
|
|||||||
form = HourBuildingLegFlightForm
|
form = HourBuildingLegFlightForm
|
||||||
fk_name = "hourbuildinglegbase_ptr"
|
fk_name = "hourbuildinglegbase_ptr"
|
||||||
fields = ("departure", "time", "destination", "pax", )
|
fields = ("departure", "time", "destination", "pax", )
|
||||||
hide_title = True
|
|
||||||
|
|
||||||
class HourBuildingLegStopInLine(nested_admin.NestedStackedPolymorphicInline.Child):
|
class HourBuildingLegStopInLine(nested_admin.NestedStackedPolymorphicInline.Child):
|
||||||
model = HourBuildingLegStop
|
model = HourBuildingLegStop
|
||||||
@@ -61,18 +60,6 @@ class HourBuildingLegBaseInLine(nested_admin.NestedStackedPolymorphicInline):
|
|||||||
|
|
||||||
child_inlines = (HourBuildingLegFlightInLine, HourBuildingLegStopInLine, )
|
child_inlines = (HourBuildingLegFlightInLine, HourBuildingLegStopInLine, )
|
||||||
|
|
||||||
# If user is a student deny edit permission for week past the current one
|
|
||||||
def has_change_permission(self, request: HttpRequest, obj: HourBuilding | None = None):
|
|
||||||
if hasattr(request.user, "student") and obj:
|
|
||||||
current_week = date.today().isocalendar().week
|
|
||||||
if not obj.DoesNotExist and current_week > obj.weekpref.week:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def has_delete_permission(self, request: HttpRequest, obj: HourBuilding | None = None):
|
|
||||||
return self.has_change_permission(request=request, obj=obj)
|
|
||||||
|
|
||||||
|
|
||||||
class HourBuildingInLine(nested_admin.NestedTabularInline):
|
class HourBuildingInLine(nested_admin.NestedTabularInline):
|
||||||
model = HourBuilding
|
model = HourBuilding
|
||||||
inlines = (HourBuildingLegBaseInLine,)
|
inlines = (HourBuildingLegBaseInLine,)
|
||||||
@@ -85,3 +72,16 @@ class HourBuildingInLine(nested_admin.NestedTabularInline):
|
|||||||
models.TextField: {"widget": Textarea(attrs={"rows":4, "cols":35})},
|
models.TextField: {"widget": Textarea(attrs={"rows":4, "cols":35})},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# If user is a student deny edit permission for week past the current one
|
||||||
|
def has_change_permission(self, request: HttpRequest, obj: WeekPreference | None = None):
|
||||||
|
if hasattr(request.user, 'student') and obj:
|
||||||
|
current_week: int = date.today().isocalendar().week
|
||||||
|
if current_week > obj.week:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def has_delete_permission(self, request: HttpRequest, obj: WeekPreference | None = None):
|
||||||
|
return self.has_change_permission(request=request, obj=obj)
|
||||||
|
|
||||||
|
def has_add_permission(self, request: HttpRequest, obj: WeekPreference | None = None):
|
||||||
|
return self.has_change_permission(request=request, obj=obj)
|
||||||
|
|||||||
@@ -34,6 +34,6 @@ class MissionProfileResource(ModelResource):
|
|||||||
fields = ("mtype", "mnum", "duration")
|
fields = ("mtype", "mnum", "duration")
|
||||||
import_id_fields = ("mtype", "mnum")
|
import_id_fields = ("mtype", "mnum")
|
||||||
|
|
||||||
class MissionProfileAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin):
|
class MissionProfileAdmin(AdminActionFormsMixin, ImportMixin, admin.ModelAdmin):
|
||||||
list_display = ("mtype", "mnum", "notes")
|
list_display = ("mtype", "mnum", "notes")
|
||||||
list_filter = ("mtype",)
|
list_filter = ("mtype",)
|
||||||
|
|||||||
@@ -6,16 +6,25 @@ from django.utils.safestring import SafeText
|
|||||||
|
|
||||||
from import_export import fields
|
from import_export import fields
|
||||||
from import_export.admin import ImportMixin
|
from import_export.admin import ImportMixin
|
||||||
|
from import_export.tmp_storages import CacheStorage
|
||||||
from import_export.resources import ModelResource
|
from import_export.resources import ModelResource
|
||||||
from import_export.widgets import CharWidget
|
from import_export.forms import ConfirmImportForm, ImportForm
|
||||||
|
|
||||||
from django_admin_action_forms import AdminActionFormsMixin, AdminActionForm, action_with_form
|
from django_admin_action_forms import AdminActionFormsMixin, AdminActionForm, action_with_form
|
||||||
|
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
from ..models.courses import Course
|
from ..models.courses import Course
|
||||||
from ..models.students import Student
|
from ..models.students import Student
|
||||||
|
|
||||||
from ..custom.colortag import course_color
|
from ..custom.colortag import course_color
|
||||||
|
|
||||||
|
# Custom import form to select a course for student input
|
||||||
|
class StudentCustomConfirmImportForm(ConfirmImportForm):
|
||||||
|
course = forms.ModelChoiceField(
|
||||||
|
queryset=Course.objects.all(),
|
||||||
|
required=False)
|
||||||
|
|
||||||
# Resource Class for Student data import
|
# Resource Class for Student data import
|
||||||
class StudentResource(ModelResource):
|
class StudentResource(ModelResource):
|
||||||
surname = fields.Field(attribute="surname", column_name="surname")
|
surname = fields.Field(attribute="surname", column_name="surname")
|
||||||
@@ -24,29 +33,39 @@ class StudentResource(ModelResource):
|
|||||||
phone = fields.Field(attribute="phone", column_name="phone")
|
phone = fields.Field(attribute="phone", column_name="phone")
|
||||||
|
|
||||||
# Cleanup fields before entering
|
# Cleanup fields before entering
|
||||||
def before_import_row(self, row: dict[str, str], **kwargs) -> None:
|
def before_import_row(self, row: Dict[str, str], **kwargs) -> None:
|
||||||
row["name"] = SafeText(row["name"].capitalize().strip())
|
row["name"] = SafeText("-".join(c.capitalize() for c in row["name"].split(" ")).strip())
|
||||||
row["surname"] = SafeText(row["surname"].capitalize().strip())
|
row["surname"] = SafeText("-".join(c.capitalize() for c in row["surname"].split(" ")).strip())
|
||||||
row["phone"] = SafeText(row["phone"].replace(" ",""))
|
row["phone"] = SafeText(row["phone"].replace(" ",""))
|
||||||
row["email"] = SafeText(row["email"].lower().strip())
|
row["email"] = SafeText(row["email"].lower().strip())
|
||||||
return super().before_import_row(row, **kwargs)
|
return super().before_import_row(row, **kwargs)
|
||||||
|
|
||||||
|
# If course was addedd as a form kwasrg add it to the student after creation
|
||||||
|
def after_init_instance(self, instance: Student, new: bool, row: Dict[str, str], **kwargs: Dict[str, Any | Course]):
|
||||||
|
course = kwargs.get("course", None)
|
||||||
|
if course and isinstance(course, Course):
|
||||||
|
instance.course = course
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Student
|
model = Student
|
||||||
skip_unchanged = True
|
skip_unchanged = True
|
||||||
report_skipped = True
|
report_skipped = True
|
||||||
fields = ("surname", "name", "email", "phone")
|
fields = ("surname", "name", "email", "phone",)
|
||||||
import_id_fields = ("email", "phone")
|
import_id_fields = ("email", "phone",)
|
||||||
|
|
||||||
# Form Class for Student course change
|
# Form Class for Student course change
|
||||||
class ChangeCourseForm(AdminActionForm):
|
class ChangeCourseForm(AdminActionForm):
|
||||||
course = forms.ModelChoiceField(queryset=Course.objects.all())
|
course = forms.ModelChoiceField(queryset=Course.objects.all())
|
||||||
|
|
||||||
class StudentAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin):
|
class StudentAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin):
|
||||||
list_display = ("surname", "name", "course", "course_color", "email", "phone", "password", "active")
|
list_display = ("surname", "name", "course", "course_color", "email", "phone", "username", "password", "active",)
|
||||||
list_filter = ("course", "active")
|
list_filter = ("course", "active",)
|
||||||
actions = ("change_course", "disable_students")
|
search_fields = ("surname", "name", "phone", "email",)
|
||||||
|
actions = ("change_course", "disable_students",)
|
||||||
resource_classes = [StudentResource]
|
resource_classes = [StudentResource]
|
||||||
|
confirm_form_class = StudentCustomConfirmImportForm
|
||||||
|
tmp_storage_class = CacheStorage
|
||||||
|
skip_admin_log = True
|
||||||
|
|
||||||
@admin.display(description="Color")
|
@admin.display(description="Color")
|
||||||
def course_color(self, obj: Student) -> SafeText:
|
def course_color(self, obj: Student) -> SafeText:
|
||||||
@@ -58,6 +77,10 @@ class StudentAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin):
|
|||||||
def password(self, obj: Student) -> SafeText:
|
def password(self, obj: Student) -> SafeText:
|
||||||
return SafeText(obj.default_password())
|
return SafeText(obj.default_password())
|
||||||
|
|
||||||
|
@admin.display(description="Username")
|
||||||
|
def username(self, obj: Student) -> SafeText:
|
||||||
|
return SafeText(obj.default_username())
|
||||||
|
|
||||||
@admin.action(description="Deactivate Students")
|
@admin.action(description="Deactivate Students")
|
||||||
def disable_students(self, request: HttpRequest, queryset: QuerySet[Student]):
|
def disable_students(self, request: HttpRequest, queryset: QuerySet[Student]):
|
||||||
for q in queryset.all():
|
for q in queryset.all():
|
||||||
@@ -73,3 +96,18 @@ class StudentAdmin(ImportMixin, AdminActionFormsMixin, admin.ModelAdmin):
|
|||||||
course = data["course"]
|
course = data["course"]
|
||||||
count: int = queryset.update(course=course)
|
count: int = queryset.update(course=course)
|
||||||
messages.success(request, f"{count} students updated to {course}")
|
messages.success(request, f"{count} students updated to {course}")
|
||||||
|
|
||||||
|
# Return the initial form for import confirmations, request course to user
|
||||||
|
def get_confirm_form_initial(self, request: HttpRequest, import_form):
|
||||||
|
initial = super().get_confirm_form_initial(request, import_form)
|
||||||
|
if import_form and hasattr(import_form.cleaned_data, "course"):
|
||||||
|
course: Course = import_form.cleaned_data["course"]
|
||||||
|
initial["course"] = course.id
|
||||||
|
return initial
|
||||||
|
|
||||||
|
# Add course to import form kwargs to be used by resource to associate course with all imported students
|
||||||
|
def get_import_data_kwargs(self, request: HttpRequest, *args, **kwargs):
|
||||||
|
form: ImportForm | None = kwargs.get("form", None)
|
||||||
|
if form and hasattr(form, "cleaned_data"):
|
||||||
|
kwargs["course"] = form.cleaned_data.get("course", None)
|
||||||
|
return kwargs
|
||||||
|
|||||||
@@ -35,3 +35,6 @@ class TrainingInLIne(nested_admin.NestedTabularInline):
|
|||||||
|
|
||||||
def has_delete_permission(self, request: HttpRequest, obj: WeekPreference | None = None):
|
def has_delete_permission(self, request: HttpRequest, obj: WeekPreference | None = None):
|
||||||
return self.has_change_permission(request=request, obj=obj)
|
return self.has_change_permission(request=request, obj=obj)
|
||||||
|
|
||||||
|
def has_add_permission(self, request: HttpRequest, obj: WeekPreference | None = None):
|
||||||
|
return self.has_change_permission(request=request, obj=obj)
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ from datetime import date
|
|||||||
class WeekPreferenceAdmin(nested_admin.NestedPolymorphicModelAdmin):
|
class WeekPreferenceAdmin(nested_admin.NestedPolymorphicModelAdmin):
|
||||||
inlines = (TrainingInLIne, HourBuildingInLine, )
|
inlines = (TrainingInLIne, HourBuildingInLine, )
|
||||||
list_display = ("week", "student__surname","student__name", "student__course", "course_color", "student_brief_mix",)
|
list_display = ("week", "student__surname","student__name", "student__course", "course_color", "student_brief_mix",)
|
||||||
list_filter = ("week", "student__course", "student",)
|
list_filter = ("week", "student__course",)
|
||||||
|
search_fields = ("student__surname","student__name",)
|
||||||
actions = ("export",)
|
actions = ("export",)
|
||||||
|
|
||||||
@admin.action(description="Export Selected Preferences")
|
@admin.action(description="Export Selected Preferences")
|
||||||
@@ -8,11 +8,7 @@ class RedirectNonSuperuserFromAdminMiddleware:
|
|||||||
|
|
||||||
def __call__(self, request: HttpRequest):
|
def __call__(self, request: HttpRequest):
|
||||||
# Se l'utente è loggato, non è superuser e prova ad andare in /admin/...
|
# Se l'utente è loggato, non è superuser e prova ad andare in /admin/...
|
||||||
if hasattr(request,"user"):
|
if hasattr(request, "user") and not request.user.is_superuser:
|
||||||
if (
|
if "/admin/" in request.path:
|
||||||
request.path.startswith("/admin/") and
|
|
||||||
hasattr(request.user, 'student')
|
|
||||||
):
|
|
||||||
return redirect("/user/") # redirect automatico
|
return redirect("/user/") # redirect automatico
|
||||||
|
|
||||||
return self.get_response(request)
|
return self.get_response(request)
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# Generated by Django 5.2.8 on 2025-11-25 11:01
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('flightslot', '0019_remove_hourbuildinglegstop_location_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='hourbuildinglegbase',
|
||||||
|
options={'base_manager_name': 'objects', 'verbose_name': 'Flight Leg or Stop', 'verbose_name_plural': 'Flight Legs or Stops'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='hourbuildinglegflight',
|
||||||
|
options={'base_manager_name': 'objects', 'verbose_name': 'Flight leg', 'verbose_name_plural': 'Flight legs'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='hourbuildinglegstop',
|
||||||
|
options={'base_manager_name': 'objects', 'verbose_name': 'Stop', 'verbose_name_plural': 'Stops'},
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='hourbuildinglegflight',
|
||||||
|
name='pax',
|
||||||
|
field=models.CharField(blank=True, max_length=16, null=True, verbose_name='Pax (optional)'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='weekpreference',
|
||||||
|
name='week',
|
||||||
|
field=models.PositiveSmallIntegerField(auto_created=True, db_default=48, db_index=True, verbose_name='Week Number'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.2.8 on 2025-11-27 10:56
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('flightslot', '0020_alter_hourbuildinglegbase_options_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='hourbuildinglegstop',
|
||||||
|
name='refuel',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Stop for Refuelling'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -124,8 +124,10 @@ class HourBuildingLegFlight(HourBuildingLegBase):
|
|||||||
|
|
||||||
class HourBuildingLegStop(HourBuildingLegBase):
|
class HourBuildingLegStop(HourBuildingLegBase):
|
||||||
|
|
||||||
refuel = models.BooleanField(
|
refuel = models.BooleanField (
|
||||||
default=False
|
null=False,
|
||||||
|
default=False,
|
||||||
|
verbose_name="Stop for Refuelling"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Change displayed name in the inline form
|
# Change displayed name in the inline form
|
||||||
|
|||||||
@@ -46,8 +46,14 @@ class Student(models.Model):
|
|||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
def default_password(self) -> str:
|
def default_password(self) -> str: # Maximum 4 digits for passowrd
|
||||||
return f"{self.name.lower()[0]}{self.surname.lower()}{self.id}"
|
return f"{self.name.lower()[0]}{self.surname.lower()}{self.id % 10000}"
|
||||||
|
|
||||||
|
def default_username(self) -> str:
|
||||||
|
if self.pk and self.user:
|
||||||
|
return self.user.username
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
# Override save method to add user for login upon Student creation
|
# Override save method to add user for login upon Student creation
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|||||||
Reference in New Issue
Block a user