This commit is contained in:
Mikan
2025-05-18 21:29:04 +03:00
parent 63228923a7
commit d70c5f73f5
50 changed files with 1344 additions and 52 deletions

17
.idea/dataSources.xml generated Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="db" uuid="018e0f6c-5916-4f25-8264-dc6301fd54fd">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:C:\Users\Mikan\PycharmProjects\tisbiProFi\db.sqlite3</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
<libraries>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.43.0/org/xerial/sqlite-jdbc/3.43.0.0/sqlite-jdbc-3.43.0.0.jar</url>
</library>
</libraries>
</data-source>
</component>
</project>

View File

@@ -1,15 +0,0 @@
from django.db import models
class ContactMixin(models.Model):
first_name = models.CharField(max_length=100, verbose_name="Имя")
last_name = models.CharField(max_length=100, verbose_name="Фамилия")
middle_name = models.CharField(max_length=100, blank=True, null=True, verbose_name="Отчество")
phone = models.CharField(max_length=20, blank=True, null=True, verbose_name="Телефон")
email = models.EmailField(blank=True, null=True, verbose_name="Почта")
def __str__(self):
return f"{self.last_name} {self.first_name}"
class Meta:
abstract = True

View File

@@ -0,0 +1,15 @@
from django.db import models
class ContactMixin(models.Model):
first_name = models.CharField(max_length=100, verbose_name="Имя")
last_name = models.CharField(max_length=100, verbose_name="Фамилия", blank=True, null=True)
middle_name = models.CharField(max_length=100, blank=True, null=True, verbose_name="Отчество")
phone = models.CharField(max_length=20, blank=True, null=True, verbose_name="Телефон")
email = models.EmailField(blank=True, null=True, verbose_name="Почта")
def __str__(self):
return f"{self.last_name} {self.first_name}"
class Meta:
abstract = True

0
education/__init__.py Normal file
View File

3
education/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
education/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class EducationConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'education'

View File

@@ -0,0 +1,84 @@
# Generated by Django 4.2.21 on 2025-05-18 18:14
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='AcademicYear',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('year', models.CharField(max_length=9, verbose_name='Год')),
],
options={
'verbose_name': 'Учебный год',
'verbose_name_plural': 'Учебные годы',
},
),
migrations.CreateModel(
name='Department',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Название отделения')),
],
options={
'verbose_name': 'Учебное отделение',
'verbose_name_plural': 'Учебные отделения',
},
),
migrations.CreateModel(
name='DepartmentHead',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
options={
'verbose_name': 'Руководитель отделения',
'verbose_name_plural': 'Руководители отделений',
},
),
migrations.CreateModel(
name='Group',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=20, verbose_name='Название группы')),
],
options={
'verbose_name': 'Группа',
'verbose_name_plural': 'Группы',
},
),
migrations.CreateModel(
name='Specialty',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Название специальности')),
('code', models.CharField(max_length=20, verbose_name='Код специальности')),
],
options={
'verbose_name': 'Специальность',
'verbose_name_plural': 'Специальности',
},
),
migrations.CreateModel(
name='Student',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=100, verbose_name='Имя')),
('last_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Фамилия')),
('middle_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Отчество')),
('phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='Телефон')),
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Почта')),
],
options={
'verbose_name': 'Студент',
'verbose_name_plural': 'Студенты',
},
),
]

View File

@@ -0,0 +1,58 @@
# Generated by Django 4.2.21 on 2025-05-18 18:14
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('employees', '0001_initial'),
('education', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='student',
name='account',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Аккаунт'),
),
migrations.AddField(
model_name='student',
name='group',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='education.group', verbose_name='Группа'),
),
migrations.AddField(
model_name='specialty',
name='department',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='education.department', verbose_name='Учебное отделение'),
),
migrations.AddField(
model_name='group',
name='curator',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='employees.employee', verbose_name='Куратор'),
),
migrations.AddField(
model_name='group',
name='specialty',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='education.specialty', verbose_name='Специальность'),
),
migrations.AddField(
model_name='departmenthead',
name='department',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='education.department', verbose_name='Учебное отделение'),
),
migrations.AddField(
model_name='departmenthead',
name='head',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='employees.employee', verbose_name='Руководитель'),
),
migrations.AlterUniqueTogether(
name='departmenthead',
unique_together={('department', 'head')},
),
]

View File

93
education/models.py Normal file
View File

@@ -0,0 +1,93 @@
from django.db import models
from core.models import ContactMixin # Предполагается, что ContactMixin находится в приложении core
from employees.models import Employee # Предполагается, что модель Employee находится в приложении employees
from users.models import Account # Предполагается, что модель Account находится в приложении users
## Учебный процесс (название приложения: education)
class AcademicYear(models.Model):
year = models.CharField(max_length=9, verbose_name="Год") # Например, "2023-2024"
def __str__(self):
return self.year
class Meta:
verbose_name = "Учебный год"
verbose_name_plural = "Учебные годы"
class Department(models.Model):
name = models.CharField(max_length=100, verbose_name="Название отделения")
def __str__(self):
return self.name
class Meta:
verbose_name = "Учебное отделение"
verbose_name_plural = "Учебные отделения"
class DepartmentHead(models.Model):
department = models.ForeignKey(
Department, on_delete=models.CASCADE, verbose_name="Учебное отделение"
)
head = models.OneToOneField(
Employee, on_delete=models.CASCADE, verbose_name="Руководитель"
)
def __str__(self):
return f"{self.head} - {self.department}"
class Meta:
verbose_name = "Руководитель отделения"
verbose_name_plural = "Руководители отделений"
unique_together = ("department", "head")
class Specialty(models.Model):
name = models.CharField(max_length=200, verbose_name="Название специальности")
code = models.CharField(max_length=20, verbose_name="Код специальности")
department = models.ForeignKey(
Department, on_delete=models.CASCADE, verbose_name="Учебное отделение"
)
def __str__(self):
return f"{self.code} - {self.name}"
class Meta:
verbose_name = "Специальность"
verbose_name_plural = "Специальности"
class Group(models.Model):
name = models.CharField(max_length=20, verbose_name="Название группы")
curator = models.ForeignKey(
Employee, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Куратор"
)
specialty = models.ForeignKey(
Specialty, on_delete=models.CASCADE, verbose_name="Специальность"
)
def __str__(self):
return self.name
class Meta:
verbose_name = "Группа"
verbose_name_plural = "Группы"
class Student(ContactMixin):
group = models.ForeignKey(
Group, on_delete=models.CASCADE, verbose_name="Группа"
)
account = models.OneToOneField(
Account, on_delete=models.CASCADE, unique=True, verbose_name="Аккаунт"
)
def __str__(self):
return f"{self.last_name} {self.first_name} - {self.group}"
class Meta:
verbose_name = "Студент"
verbose_name_plural = "Студенты"

3
education/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
education/views.py Normal file
View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

0
employees/__init__.py Normal file
View File

3
employees/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
employees/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class EmployeesConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'employees'

View File

@@ -0,0 +1,51 @@
# Generated by Django 4.2.21 on 2025-05-18 18:14
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Department',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Название подразделения')),
],
options={
'verbose_name': 'Подразделение',
'verbose_name_plural': 'Подразделения',
},
),
migrations.CreateModel(
name='Employee',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=100, verbose_name='Имя')),
('last_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Фамилия')),
('middle_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Отчество')),
('phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='Телефон')),
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Почта')),
],
options={
'verbose_name': 'Сотрудник',
'verbose_name_plural': 'Сотрудники',
},
),
migrations.CreateModel(
name='Position',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Название должности')),
],
options={
'verbose_name': 'Должность',
'verbose_name_plural': 'Должности',
},
),
]

View File

@@ -0,0 +1,33 @@
# Generated by Django 4.2.21 on 2025-05-18 18:14
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('employees', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='employee',
name='account',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Аккаунт'),
),
migrations.AddField(
model_name='employee',
name='department',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='employees.department', verbose_name='Подразделение'),
),
migrations.AddField(
model_name='employee',
name='position',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='employees.position', verbose_name='Должность'),
),
]

View File

45
employees/models.py Normal file
View File

@@ -0,0 +1,45 @@
from django.db import models
from core.models import ContactMixin # Предполагается, что ContactMixin находится в приложении core
## Учет сотрудников (название приложения: employees)
class Position(models.Model):
name = models.CharField(max_length=100, verbose_name="Название должности")
def __str__(self):
return self.name
class Meta:
verbose_name = "Должность"
verbose_name_plural = "Должности"
class Department(models.Model):
name = models.CharField(max_length=100, verbose_name="Название подразделения")
def __str__(self):
return self.name
class Meta:
verbose_name = "Подразделение"
verbose_name_plural = "Подразделения"
class Employee(ContactMixin):
department = models.ForeignKey(
Department, on_delete=models.CASCADE, verbose_name="Подразделение"
)
position = models.ForeignKey(
Position, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Должность"
)
account = models.OneToOneField(
"users.Account", on_delete=models.CASCADE, unique=True, verbose_name="Аккаунт"
)
def __str__(self):
return f"{self.last_name} {self.first_name} - {self.position}"
class Meta:
verbose_name = "Сотрудник"
verbose_name_plural = "Сотрудники"

3
employees/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
employees/views.py Normal file
View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

0
events/__init__.py Normal file
View File

3
events/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
events/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class EventsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'events'

View File

@@ -0,0 +1,125 @@
# Generated by Django 4.2.21 on 2025-05-18 18:14
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('employees', '0001_initial'),
('education', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='ActivityType',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Название вида деятельности')),
],
options={
'verbose_name': 'Деятельность мероприятия',
'verbose_name_plural': 'Деятельности мероприятий',
},
),
migrations.CreateModel(
name='Event',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Название')),
('location', models.CharField(blank=True, max_length=255, null=True, verbose_name='Место проведения')),
('date', models.DateField(verbose_name='Дата проведения')),
('description', models.TextField(blank=True, null=True, verbose_name='Описание')),
('academic_year', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='education.academicyear', verbose_name='Учебный год')),
],
options={
'verbose_name': 'Мероприятие',
'verbose_name_plural': 'Мероприятия',
},
),
migrations.CreateModel(
name='EventStatus',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='Название статуса')),
],
options={
'verbose_name': 'Статус проведения',
'verbose_name_plural': 'Статусы проведения',
},
),
migrations.CreateModel(
name='MethodicalFile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Название')),
('file_path', models.FileField(max_length=500, upload_to='', verbose_name='Файл')),
('upload_date', models.DateField(auto_now_add=True, verbose_name='Дата добавления')),
('employee', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='employees.employee', verbose_name='Сотрудник')),
('specialty', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='education.specialty', verbose_name='Специальность')),
],
options={
'verbose_name': 'Методический файл',
'verbose_name_plural': 'Методические файлы',
},
),
migrations.CreateModel(
name='RelevantMethodicalFile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('activity_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.activitytype', verbose_name='Вид деятельности')),
('methodical_file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.methodicalfile', verbose_name='Методический файл')),
],
options={
'verbose_name': 'Релевантный методический файл',
'verbose_name_plural': 'Релевантные методические файлы',
},
),
migrations.CreateModel(
name='EventParticipant',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('points', models.PositiveIntegerField(blank=True, default=0, verbose_name='Баллы за участие')),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.event', verbose_name='Мероприятие')),
('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='education.student', verbose_name='Студент')),
],
options={
'verbose_name': 'Участник мероприятия',
'verbose_name_plural': 'Участники мероприятий',
},
),
migrations.AddField(
model_name='event',
name='status',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='events.eventstatus', verbose_name='Статус мероприятия'),
),
migrations.CreateModel(
name='EventOrganizer',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('employee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='employees.employee', verbose_name='Сотрудник-организатор')),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.event', verbose_name='Мероприятие')),
],
options={
'verbose_name': 'Организатор мероприятия',
'verbose_name_plural': 'Организаторы мероприятий',
'unique_together': {('employee', 'event')},
},
),
migrations.CreateModel(
name='EventDetail',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('activity_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.activitytype', verbose_name='Вид деятельности')),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.event', verbose_name='Мероприятие')),
],
options={
'verbose_name': 'Деталь мероприятия',
'verbose_name_plural': 'Детали мероприятий',
'unique_together': {('activity_type', 'event')},
},
),
]

View File

133
events/models.py Normal file
View File

@@ -0,0 +1,133 @@
from django.db import models
from education.models import AcademicYear, Specialty, Student # Предполагается, что модели находятся в приложении education
from employees.models import Employee # Предполагается, что модель Employee находится в приложении employees
## Учет мероприятий (название приложения: events)
class EventStatus(models.Model):
name = models.CharField(max_length=100, verbose_name="Название статуса")
def __str__(self):
return self.name
class Meta:
verbose_name = "Статус проведения"
verbose_name_plural = "Статусы проведения"
class ActivityType(models.Model):
name = models.CharField(max_length=100, verbose_name="Название вида деятельности")
def __str__(self):
return self.name
class Meta:
verbose_name = "Деятельность мероприятия"
verbose_name_plural = "Деятельности мероприятий"
class Event(models.Model):
academic_year = models.ForeignKey(
AcademicYear, on_delete=models.CASCADE, verbose_name="Учебный год"
)
status = models.ForeignKey(
EventStatus, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Статус мероприятия"
)
name = models.CharField(max_length=200, verbose_name="Название")
location = models.CharField(max_length=255, blank=True, null=True, verbose_name="Место проведения")
date = models.DateField(verbose_name="Дата проведения")
description = models.TextField(blank=True, null=True, verbose_name="Описание")
def __str__(self):
return f"{self.name} ({self.date})"
class Meta:
verbose_name = "Мероприятие"
verbose_name_plural = "Мероприятия"
class EventOrganizer(models.Model):
employee = models.ForeignKey(
Employee, on_delete=models.CASCADE, verbose_name="Сотрудник-организатор"
)
event = models.ForeignKey(
Event, on_delete=models.CASCADE, verbose_name="Мероприятие"
)
def __str__(self):
return f"{self.employee} - {self.event}"
class Meta:
verbose_name = "Организатор мероприятия"
verbose_name_plural = "Организаторы мероприятий"
unique_together = ("employee", "event")
class EventDetail(models.Model):
activity_type = models.ForeignKey(
ActivityType, on_delete=models.CASCADE, verbose_name="Вид деятельности"
)
event = models.ForeignKey(
Event, on_delete=models.CASCADE, verbose_name="Мероприятие"
)
def __str__(self):
return f"{self.activity_type} - {self.event}"
class Meta:
verbose_name = "Деталь мероприятия"
verbose_name_plural = "Детали мероприятий"
unique_together = ("activity_type", "event")
class EventParticipant(models.Model):
student = models.ForeignKey(
Student, on_delete=models.CASCADE, verbose_name="Студент"
)
event = models.ForeignKey(
Event, on_delete=models.CASCADE, verbose_name="Мероприятие"
)
points = models.PositiveIntegerField(blank=True, default=0, verbose_name="Баллы за участие")
def __str__(self):
return f"{self.student} - {self.event}"
class Meta:
verbose_name = "Участник мероприятия"
verbose_name_plural = "Участники мероприятий"
class MethodicalFile(models.Model):
specialty = models.ForeignKey(
Specialty, on_delete=models.CASCADE, verbose_name="Специальность"
)
name = models.CharField(max_length=200, verbose_name="Название")
file_path = models.FileField(max_length=500, verbose_name="Файл")
upload_date = models.DateField(auto_now_add=True, verbose_name="Дата добавления")
employee = models.ForeignKey(
Employee, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Сотрудник"
)
def __str__(self):
return f"{self.name} ({self.specialty})"
class Meta:
verbose_name = "Методический файл"
verbose_name_plural = "Методические файлы"
class RelevantMethodicalFile(models.Model):
methodical_file = models.ForeignKey(
MethodicalFile, on_delete=models.CASCADE, verbose_name="Методический файл"
)
activity_type = models.ForeignKey(
ActivityType, on_delete=models.CASCADE, verbose_name="Вид деятельности"
)
def __str__(self):
return f"{self.methodical_file} - {self.activity_type}"
class Meta:
verbose_name = "Релевантный методический файл"
verbose_name_plural = "Релевантные методические файлы"

3
events/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
events/views.py Normal file
View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

0
outreach/__init__.py Normal file
View File

3
outreach/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
outreach/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class OutreachConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'outreach'

View File

@@ -0,0 +1,96 @@
# Generated by Django 4.2.21 on 2025-05-18 18:14
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('employees', '0001_initial'),
('education', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='InstitutionType',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50, verbose_name='Название вида учебного заведения')),
],
options={
'verbose_name': 'Вид учебного заведения',
'verbose_name_plural': 'Виды учебных заведений',
},
),
migrations.CreateModel(
name='Partner',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Название')),
('description', models.TextField(blank=True, null=True, verbose_name='Описание')),
],
options={
'verbose_name': 'Партнер',
'verbose_name_plural': 'Партнеры',
},
),
migrations.CreateModel(
name='Representative',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=100, verbose_name='Имя')),
('last_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Фамилия')),
('middle_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Отчество')),
('phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='Телефон')),
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Почта')),
('position', models.CharField(blank=True, max_length=100, null=True, verbose_name='Должность')),
],
options={
'verbose_name': 'Представитель',
'verbose_name_plural': 'Представители',
},
),
migrations.CreateModel(
name='School',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Название')),
('address', models.CharField(max_length=300, verbose_name='Адрес')),
('director', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='outreach.representative', verbose_name='Директор')),
('institution_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='outreach.institutiontype', verbose_name='Вид учебного заведения')),
],
options={
'verbose_name': 'Школа',
'verbose_name_plural': 'Школы',
},
),
migrations.CreateModel(
name='SchoolResponsibility',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('academic_year', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='education.academicyear', verbose_name='Учебный год')),
('responsible', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='employees.employee', verbose_name='Ответственный сотрудник')),
('school', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='outreach.school', verbose_name='Школа')),
],
options={
'verbose_name': 'Ответственный за школу',
'verbose_name_plural': 'Ответственные за школы',
},
),
migrations.CreateModel(
name='PartnerRepresentative',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('partner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='outreach.partner', verbose_name='Партнер')),
('representative', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='outreach.representative', verbose_name='Представитель')),
],
options={
'verbose_name': 'Представитель партнера',
'verbose_name_plural': 'Представители партнеров',
'unique_together': {('representative', 'partner')},
},
),
]

View File

94
outreach/models.py Normal file
View File

@@ -0,0 +1,94 @@
from django.db import models
from core.models import ContactMixin # Предполагается, что ContactMixin находится в приложении core
from education.models import AcademicYear # Предполагается, что AcademicYear находится в приложении education
from employees.models import Employee # Предполагается, что модель Employee находится в приложении employees
## Профориентационная деятельность (название приложения: outreach)
class InstitutionType(models.Model):
name = models.CharField(max_length=50, verbose_name="Название вида учебного заведения")
def __str__(self):
return self.name
class Meta:
verbose_name = "Вид учебного заведения"
verbose_name_plural = "Виды учебных заведений"
class Representative(ContactMixin):
position = models.CharField(max_length=100, blank=True, null=True, verbose_name="Должность")
def __str__(self):
return f"{self.last_name} {self.first_name} - {self.position}"
class Meta:
verbose_name = "Представитель"
verbose_name_plural = "Представители"
class School(models.Model):
director = models.ForeignKey(
Representative, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Директор"
)
institution_type = models.ForeignKey(
InstitutionType, on_delete=models.CASCADE, verbose_name="Вид учебного заведения"
)
name = models.CharField(max_length=200, verbose_name="Название")
address = models.CharField(max_length=300, verbose_name="Адрес")
def __str__(self):
return f"{self.name} ({self.institution_type})"
class Meta:
verbose_name = "Школа"
verbose_name_plural = "Школы"
class Partner(models.Model):
name = models.CharField(max_length=200, verbose_name="Название")
description = models.TextField(blank=True, null=True, verbose_name="Описание")
def __str__(self):
return self.name
class Meta:
verbose_name = "Партнер"
verbose_name_plural = "Партнеры"
class PartnerRepresentative(models.Model):
representative = models.OneToOneField(
Representative, on_delete=models.CASCADE, verbose_name="Представитель"
)
partner = models.ForeignKey(
Partner, on_delete=models.CASCADE, verbose_name="Партнер"
)
def __str__(self):
return f"{self.representative} - {self.partner}"
class Meta:
verbose_name = "Представитель партнера"
verbose_name_plural = "Представители партнеров"
unique_together = ("representative", "partner")
class SchoolResponsibility(models.Model):
academic_year = models.ForeignKey(
AcademicYear, on_delete=models.CASCADE, verbose_name="Учебный год"
)
responsible = models.ForeignKey(
Employee, on_delete=models.CASCADE, verbose_name="Ответственный сотрудник"
)
school = models.ForeignKey(
School, on_delete=models.CASCADE, verbose_name="Школа"
)
def __str__(self):
return f"{self.responsible} - {self.school} ({self.academic_year})"
class Meta:
verbose_name = "Ответственный за школу"
verbose_name_plural = "Ответственные за школы"

3
outreach/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
outreach/views.py Normal file
View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

0
surveys/__init__.py Normal file
View File

3
surveys/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
surveys/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class SurveysConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'surveys'

View File

@@ -0,0 +1,143 @@
# Generated by Django 4.2.21 on 2025-05-18 18:14
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='EducationalInstitution',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=256, verbose_name='Название учреждения')),
],
options={
'verbose_name': 'Образовательное учреждение',
'verbose_name_plural': 'Образовательные учреждения',
},
),
migrations.CreateModel(
name='Profession',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=256, verbose_name='Название профессии')),
('description', models.TextField(blank=True, null=True, verbose_name='Описание')),
],
options={
'verbose_name': 'Профессия',
'verbose_name_plural': 'Профессии',
},
),
migrations.CreateModel(
name='ScoreVariable',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=30, verbose_name='Имя переменной')),
('title', models.CharField(max_length=70, verbose_name='Название переменной')),
('description', models.TextField(verbose_name='Описание')),
],
options={
'verbose_name': 'Переменная оценки',
'verbose_name_plural': 'Переменные оценки',
},
),
migrations.CreateModel(
name='Survey',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=70, verbose_name='Название опроса')),
],
options={
'verbose_name': 'Опрос',
'verbose_name_plural': 'Опросы',
},
),
migrations.CreateModel(
name='Statement',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('statement', models.CharField(max_length=256, verbose_name='Утверждение')),
('score_variable', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='surveys.scorevariable', verbose_name='Переменная оценки')),
],
options={
'verbose_name': 'Утверждение',
'verbose_name_plural': 'Утверждения',
},
),
migrations.CreateModel(
name='Specialty',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=256, verbose_name='Название специальности')),
('link', models.URLField(blank=True, null=True, verbose_name='Ссылка')),
('institution', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='surveys.educationalinstitution', verbose_name='Образовательное учреждение')),
],
options={
'verbose_name': 'Специальность',
'verbose_name_plural': 'Специальности',
},
),
migrations.AddField(
model_name='scorevariable',
name='survey',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='surveys.survey', verbose_name='Опрос'),
),
migrations.CreateModel(
name='QuestionPair',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('statement_a', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='pair_a', to='surveys.statement', verbose_name='Утверждение A')),
('statement_b', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='pair_b', to='surveys.statement', verbose_name='Утверждение B')),
('survey', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='surveys.survey', verbose_name='Опрос')),
],
options={
'verbose_name': 'Пара вопросов',
'verbose_name_plural': 'Пары вопросов',
},
),
migrations.CreateModel(
name='ProfessionTestResult',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('profession', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='surveys.profession', verbose_name='Профессия')),
('survey_score_variable', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='surveys.scorevariable', verbose_name='Переменная оценки')),
],
options={
'verbose_name': 'Результат теста по профессии',
'verbose_name_plural': 'Результаты тестов по профессиям',
},
),
migrations.CreateModel(
name='AgreementQuestion',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('question', models.CharField(max_length=256, verbose_name='Вопрос')),
('score_variable', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='surveys.scorevariable', verbose_name='Переменная оценки')),
('survey', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='surveys.survey', verbose_name='Опрос')),
],
options={
'verbose_name': 'Вопрос на согласие',
'verbose_name_plural': 'Вопросы на согласие',
},
),
migrations.CreateModel(
name='ProfessionSpecialtyLink',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('profession', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='surveys.profession', verbose_name='Профессия')),
('specialty', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='surveys.specialty', verbose_name='Специальность')),
],
options={
'verbose_name': 'Связь между профессией и специальностью',
'verbose_name_plural': 'Связи между профессиями и специальностями',
'unique_together': {('profession', 'specialty')},
},
),
]

View File

151
surveys/models.py Normal file
View File

@@ -0,0 +1,151 @@
from django.db import models
## Модуль Анкетирование (название приложения: surveys)
class Survey(models.Model):
title = models.CharField(max_length=70, verbose_name="Название опроса")
def __str__(self):
return self.title
class Meta:
verbose_name = "Опрос"
verbose_name_plural = "Опросы"
class ScoreVariable(models.Model):
name = models.CharField(max_length=30, verbose_name="Имя переменной")
title = models.CharField(max_length=70, verbose_name="Название переменной")
description = models.TextField(verbose_name="Описание")
survey = models.ForeignKey(
Survey, on_delete=models.CASCADE, verbose_name="Опрос"
)
def __str__(self):
return f"{self.title} ({self.survey})"
class Meta:
verbose_name = "Переменная оценки"
verbose_name_plural = "Переменные оценки"
class Statement(models.Model):
statement = models.CharField(max_length=256, verbose_name="Утверждение")
score_variable = models.ForeignKey(
ScoreVariable, on_delete=models.SET_NULL, blank=True, null=True, verbose_name="Переменная оценки"
)
def __str__(self):
return self.statement
class Meta:
verbose_name = "Утверждение"
verbose_name_plural = "Утверждения"
class QuestionPair(models.Model):
survey = models.ForeignKey(
Survey, on_delete=models.CASCADE, verbose_name="Опрос"
)
statement_a = models.OneToOneField(
Statement, on_delete=models.CASCADE, related_name="pair_a", verbose_name="Утверждение A"
)
statement_b = models.OneToOneField(
Statement, on_delete=models.CASCADE, related_name="pair_b", verbose_name="Утверждение B"
)
def __str__(self):
return f"Пара: {self.statement_a} - {self.statement_b}"
class Meta:
verbose_name = "Пара вопросов"
verbose_name_plural = "Пары вопросов"
class AgreementQuestion(models.Model):
survey = models.ForeignKey(
Survey, on_delete=models.CASCADE, verbose_name="Опрос"
)
question = models.CharField(max_length=256, verbose_name="Вопрос")
score_variable = models.ForeignKey(
ScoreVariable, on_delete=models.CASCADE, verbose_name="Переменная оценки"
)
def __str__(self):
return self.question
class Meta:
verbose_name = "Вопрос на согласие"
verbose_name_plural = "Вопросы на согласие"
class EducationalInstitution(models.Model):
name = models.CharField(max_length=256, verbose_name="Название учреждения")
def __str__(self):
return self.name
class Meta:
verbose_name = "Образовательное учреждение"
verbose_name_plural = "Образовательные учреждения"
class Profession(models.Model):
name = models.CharField(max_length=256, verbose_name="Название профессии")
description = models.TextField(blank=True, null=True, verbose_name="Описание")
def __str__(self):
return self.name
class Meta:
verbose_name = "Профессия"
verbose_name_plural = "Профессии"
class Specialty(models.Model):
name = models.CharField(max_length=256, verbose_name="Название специальности")
institution = models.ForeignKey(
EducationalInstitution, on_delete=models.CASCADE, verbose_name="Образовательное учреждение"
)
link = models.URLField(verbose_name="Ссылка", null=True, blank=True)
def __str__(self):
return f"{self.name} ({self.institution})"
class Meta:
verbose_name = "Специальность"
verbose_name_plural = "Специальности"
class ProfessionSpecialtyLink(models.Model):
profession = models.ForeignKey(
Profession, on_delete=models.CASCADE, verbose_name="Профессия"
)
specialty = models.ForeignKey(
Specialty, on_delete=models.CASCADE, verbose_name="Специальность"
)
def __str__(self):
return f"{self.profession} - {self.specialty}"
class Meta:
verbose_name = "Связь между профессией и специальностью"
verbose_name_plural = "Связи между профессиями и специальностями"
unique_together = ("profession", "specialty")
class ProfessionTestResult(models.Model):
profession = models.ForeignKey(
Profession, on_delete=models.CASCADE, verbose_name="Профессия"
)
survey_score_variable = models.ForeignKey(
ScoreVariable, on_delete=models.CASCADE, verbose_name="Переменная оценки"
)
def __str__(self):
return f"{self.profession} - {self.survey_score_variable}"
class Meta:
verbose_name = "Результат теста по профессии"
verbose_name_plural = "Результаты тестов по профессиям"

3
surveys/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
surveys/views.py Normal file
View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

View File

@@ -41,6 +41,11 @@ INSTALLED_APPS = [
'django.contrib.staticfiles', 'django.contrib.staticfiles',
"core.apps.CoreConfig", "core.apps.CoreConfig",
'users.apps.UsersConfig', 'users.apps.UsersConfig',
"employees.apps.EmployeesConfig",
"education.apps.EducationConfig",
"events.apps.EventsConfig",
"outreach.apps.OutreachConfig",
"surveys.apps.SurveysConfig"
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@@ -143,4 +148,4 @@ MEDIA_ROOT = BASE_DIR / 'media'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
AUTH_USER_MODEL = "users.UserAccount" AUTH_USER_MODEL = "users.Account"

View File

@@ -1,3 +1,85 @@
from django import forms
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.utils.translation import gettext_lazy as _
# Register your models here. from .models import Account, Role
class AccountChangeForm(forms.ModelForm):
password = forms.CharField(
label=_("Password"),
widget=forms.PasswordInput,
required=False,
help_text=_("Raw passwords are not stored, so there is no way to see this user's password.")
)
class Meta:
model = Account
fields = '__all__'
def save(self, commit=True):
password = self.cleaned_data.get('password')
if password:
self.instance.set_password(password)
return super().save(commit=commit)
class AccountCreationForm(forms.ModelForm):
password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
password2 = forms.CharField(label=_("Password confirmation"), widget=forms.PasswordInput)
class Meta:
model = Account
fields = ('login',)
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError(_("Passwords don't match"))
return password2
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
@admin.register(Account)
class AccountAdmin(BaseUserAdmin):
form = AccountChangeForm
add_form = AccountCreationForm
list_display = ('login', 'full_name', 'role', 'is_staff', 'is_active')
list_filter = ('is_staff', 'is_active', 'role')
search_fields = ('login', 'first_name', 'last_name', 'middle_name', 'phone', 'email')
ordering = ('login',)
filter_horizontal = ('groups', 'user_permissions')
fieldsets = (
(None, {'fields': ('login', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'middle_name', 'phone', 'email')}),
(_('Permissions'), {
'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions'),
}),
(_('Role'), {'fields': ('role',)}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('login', 'password1', 'password2'),
}),
)
@admin.display(description=_('Full name'))
def full_name(self, obj):
return str(obj)
@admin.register(Role)
class RoleAdmin(admin.ModelAdmin):
list_display = ('name',)
search_fields = ('name',)

View File

@@ -4,3 +4,4 @@ from django.apps import AppConfig
class UsersConfig(AppConfig): class UsersConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'users' name = 'users'
verbose_name = "Пользователи"

View File

@@ -1,4 +1,4 @@
# Generated by Django 4.2.21 on 2025-05-14 16:31 # Generated by Django 4.2.21 on 2025-05-18 18:21
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
@@ -17,35 +17,35 @@ class Migration(migrations.Migration):
name='Role', name='Role',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=20, unique=True, verbose_name='Название роли')), ('name', models.CharField(max_length=100, verbose_name='Название роли')),
], ],
options={ options={
'verbose_name': 'Роль', 'verbose_name': 'Роль пользователя',
'verbose_name_plural': 'Роли', 'verbose_name_plural': 'Роли пользователей',
}, },
), ),
migrations.CreateModel( migrations.CreateModel(
name='UserAccount', name='Account',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')), ('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('first_name', models.CharField(max_length=100, verbose_name='Имя')), ('first_name', models.CharField(max_length=100, verbose_name='Имя')),
('last_name', models.CharField(max_length=100, verbose_name='Фамилия')), ('last_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Фамилия')),
('middle_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Отчество')), ('middle_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='Отчество')),
('phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='Телефон')), ('phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='Телефон')),
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Почта')), ('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Почта')),
('username', models.CharField(max_length=150, unique=True, verbose_name='Имя пользователя')), ('login', models.CharField(max_length=150, unique=True, verbose_name='Логин')),
('is_active', models.BooleanField(default=True, verbose_name='Активен')),
('is_staff', models.BooleanField(default=False, verbose_name='Сотрудник')), ('is_staff', models.BooleanField(default=False, verbose_name='Сотрудник')),
('is_active', models.BooleanField(default=True, verbose_name='Активен')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.role', verbose_name='Роль')), ('role', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.role', verbose_name='Роль пользователя')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
], ],
options={ options={
'verbose_name': 'Пользователь', 'verbose_name': 'Аккаунт',
'verbose_name_plural': 'Пользователи', 'verbose_name_plural': 'Аккаунты',
}, },
), ),
] ]

View File

@@ -1,48 +1,54 @@
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models from django.db import models
from core.mixins import ContactMixin from core.models import ContactMixin
## Пользователи
class Role(models.Model): class Role(models.Model):
name = models.CharField(max_length=20, unique=True, verbose_name='Название роли') name = models.CharField(max_length=100, verbose_name="Название роли")
def __str__(self): def __str__(self):
return self.name return self.name
class Meta: class Meta:
verbose_name = 'Роль' verbose_name = "Роль пользователя"
verbose_name_plural = 'Роли' verbose_name_plural = "Роли пользователей"
class UserAccountManager(BaseUserManager):
def create_user(self, username, password=None, role=None, **extra_fields): class AccountManager(BaseUserManager):
if not username: def create_user(self, login, password=None, **extra_fields):
raise ValueError('Имя пользователя должно быть указано') if not login:
user = self.model(username=username, role=role, **extra_fields) raise ValueError("Login must be set")
user = self.model(login=login, **extra_fields)
user.set_password(password) user.set_password(password)
user.save(using=self._db) user.save(using=self._db)
return user return user
def create_superuser(self, username, password=None, **extra_fields): def create_superuser(self, login, password=None, **extra_fields):
admin_role, _ = Role.objects.get_or_create(name='admin') extra_fields.setdefault("is_staff", True)
extra_fields.setdefault('is_staff', True) extra_fields.setdefault("is_superuser", True)
extra_fields.setdefault('is_superuser', True) return self.create_user(login, password, **extra_fields)
return self.create_user(username, password, role=admin_role, **extra_fields)
class UserAccount(ContactMixin, AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=150, unique=True, verbose_name='Имя пользователя')
role = models.ForeignKey(Role, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='Роль')
is_active = models.BooleanField(default=True, verbose_name='Активен')
is_staff = models.BooleanField(default=False, verbose_name='Сотрудник')
objects = UserAccountManager() class Account(ContactMixin, AbstractBaseUser, PermissionsMixin):
login = models.CharField(max_length=150, unique=True, verbose_name="Логин")
role = models.ForeignKey(
Role, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Роль пользователя"
)
is_staff = models.BooleanField(default=False, verbose_name="Сотрудник")
is_active = models.BooleanField(default=True, verbose_name="Активен")
USERNAME_FIELD = 'username' USERNAME_FIELD = "login"
REQUIRED_FIELDS = [] REQUIRED_FIELDS = []
objects = AccountManager()
def __str__(self): def __str__(self):
return self.username+" ("+ContactMixin.__str__(self)+")" return self.login
class Meta: class Meta:
verbose_name = 'Пользователь' verbose_name = "Аккаунт"
verbose_name_plural = 'Пользователи' verbose_name_plural = "Аккаунты"