Docs: начальная структура проекта
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
0
app/users/__init__.py
Normal file
0
app/users/__init__.py
Normal file
7
app/users/admin.py
Normal file
7
app/users/admin.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.contrib import admin
|
||||
from .models import UserProfile
|
||||
|
||||
|
||||
@admin.register(UserProfile)
|
||||
class UserProfileAdmin(admin.ModelAdmin):
|
||||
list_display = ("user", "employee")
|
||||
7
app/users/apps.py
Normal file
7
app/users/apps.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class UsersConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "users"
|
||||
verbose_name = "Пользователи"
|
||||
30
app/users/migrations/0001_initial.py
Normal file
30
app/users/migrations/0001_initial.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Generated by Django 5.2.11 on 2026-02-25 14:58
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('references', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='UserProfile',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('employee', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user_profiles', to='references.employee', verbose_name='Сотрудник (автор в документах)')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL, verbose_name='Пользователь')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Профиль пользователя',
|
||||
'verbose_name_plural': 'Профили пользователей',
|
||||
},
|
||||
),
|
||||
]
|
||||
0
app/users/migrations/__init__.py
Normal file
0
app/users/migrations/__init__.py
Normal file
31
app/users/models.py
Normal file
31
app/users/models.py
Normal file
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
Профиль пользователя: связь User → Сотрудник для поля «Автор» в документах.
|
||||
"""
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from references.models import Employee
|
||||
|
||||
|
||||
class UserProfile(models.Model):
|
||||
"""Профиль пользователя: привязка к справочнику Сотрудники."""
|
||||
user = models.OneToOneField(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="profile",
|
||||
verbose_name="Пользователь",
|
||||
)
|
||||
employee = models.ForeignKey(
|
||||
Employee,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="user_profiles",
|
||||
verbose_name="Сотрудник (автор в документах)",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Профиль пользователя"
|
||||
verbose_name_plural = "Профили пользователей"
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.user.username} → {self.employee or '—'}"
|
||||
10
app/users/urls.py
Normal file
10
app/users/urls.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
app_name = "users"
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.HomeView.as_view(), name="home"),
|
||||
path("login/", views.ErpLoginView.as_view(), name="login"),
|
||||
path("logout/", views.ErpLogoutView.as_view(), name="logout"),
|
||||
]
|
||||
10
app/users/utils.py
Normal file
10
app/users/utils.py
Normal file
@@ -0,0 +1,10 @@
|
||||
"""Хелперы для пользователей."""
|
||||
def get_author_employee(user):
|
||||
"""Возвращает сотрудника (автора) для текущего пользователя, если привязан профиль."""
|
||||
if not user or not user.is_authenticated:
|
||||
return None
|
||||
try:
|
||||
profile = user.profile
|
||||
return profile.employee if profile else None
|
||||
except Exception:
|
||||
return None
|
||||
35
app/users/views.py
Normal file
35
app/users/views.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
Вход, выход и главная страница.
|
||||
"""
|
||||
import logging
|
||||
from django.contrib.auth.views import LoginView, LogoutView
|
||||
from django.views.generic import TemplateView
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HomeView(LoginRequiredMixin, TemplateView):
|
||||
"""Главная страница после входа: меню справочников и документов."""
|
||||
template_name = "home.html"
|
||||
login_url = "users:login"
|
||||
|
||||
|
||||
class ErpLoginView(LoginView):
|
||||
"""Страница входа."""
|
||||
template_name = "registration/login.html"
|
||||
redirect_authenticated_user = True
|
||||
|
||||
def form_valid(self, form):
|
||||
logger.info("Вход пользователя: %s", form.get_user().username)
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class ErpLogoutView(LogoutView):
|
||||
"""Выход."""
|
||||
next_page = "users:login"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if request.user.is_authenticated:
|
||||
logger.info("Выход пользователя: %s", request.user.username)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
Reference in New Issue
Block a user