Стартавая модель авторизации в приложении

This commit is contained in:
adm
2024-02-22 15:16:15 +03:00
parent bbe3fd7892
commit 645bddb07f
22 changed files with 744 additions and 1 deletions

62
system/accounts/admin.py Normal file
View File

@@ -0,0 +1,62 @@
# Системные приложения
import logging
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.admin.models import LogEntry, DELETION
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.urls import reverse
# Внутриние приложения
from system.accounts.models import User
logger = logging.getLogger(__name__)
class ProfileInline(admin.StackedInline):
model = User
can_delete = False
max_num = 1
verbose_name = 'Profile'
verbose_name_plural = 'Profile'
fk_name = 'user'
@admin.register(User)
class UserAdmin(UserAdmin):
ordering = ('email', )
list_display = (
'id', 'email', 'is_staff', 'is_superuser', 'is_active', 'date_joined',
'last_updated', 'last_login'
#'verified_email', 'accepted_terms', 'read_terms',
)
search_fields = ('email',)
list_filter = (
'is_staff', 'is_superuser', 'is_active',
#'verified_email'
)
readonly_fields = (
'last_login', 'last_updated', 'date_joined'
#'email_token',
)
list_display_links = ('id', 'email')
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Permissions', {'fields': ('groups', 'user_permissions')}),
('Roles', {'fields': ('is_staff', 'is_superuser', 'is_active')}),
#('Additional', {'fields': (
#'verified_email', 'email_token', 'accepted_terms', 'read_terms'
#)}),
('Profile', {'fields': (
'first_name','last_name', 'gender', 'bio', 'image',
'address', 'followers'
)}),
('Dates', {'fields': ('last_login', 'last_updated', 'date_joined')})
)
add_fieldsets = (
(None, {
'classes': ('wide', ),
'fields': (
'email', 'password1', 'password2'
)
}),
)
#inlines = (ProfileInline, )

7
system/accounts/apps.py Normal file
View File

@@ -0,0 +1,7 @@
from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'system.accounts'
verbose_name = 'Управление пользователями'

68
system/accounts/models.py Normal file
View File

@@ -0,0 +1,68 @@
from __future__ import annotations
from django.contrib.auth.models import AbstractUser, BaseUserManager
from django.db import models
# Управление учётными записями
class UserManager(BaseUserManager):
# Создание пользователя
def create_user(
self, email: str, password: str | None = None, **other_fields
) -> User:
user = User(email=email, **other_fields)
if password:
user.set_password(password)
else:
user.set_unusable_password()
user.save()
return user
# Создание супер пользователя
def create_superuser(self, email: str, password: str | None = None, **other_fields) -> User:
other_fields.setdefault("is_staff", True)
other_fields.setdefault("is_superuser", True)
other_fields.setdefault("is_active", True)
if other_fields.get("is_staff") is not True:
raise ValueError("Superuser must be assigned to is_staff=True.")
if other_fields.get("is_superuser") is not True:
raise ValueError("Superuser must be assigned to is_superuser=True.")
return self.create_user(email, password, **other_fields)
# Модель учётной записи
class User(AbstractUser):
GENDER_CHOICES = (
('n', 'Не указано'),
('m', 'Мужчина'),
('f', 'Женщина'),
)
first_name: str = models.CharField(max_length=60, null=True, blank=True, verbose_name='Имя')
last_name: str = models.CharField(max_length=60, null=True, blank=True, verbose_name='Фамилия')
gender: str = models.CharField(verbose_name='Пол', max_length=10, choices=GENDER_CHOICES, default='n')
email: str = models.EmailField(verbose_name='Email Address', unique=True)
username: str = models.CharField(max_length=60, verbose_name='Ник')
bio: str = models.TextField(blank=True, verbose_name='О себе')
image: str | None = models.URLField(null=True, blank=True, verbose_name='Аватар')
followers = models.ManyToManyField("self", blank=True, symmetrical=False, verbose_name='Подписчики')
address: str = models.CharField(verbose_name='Адрес', max_length=255, blank=True, null=True)
#avatar = models.ImageField(upload_to=avatar_upload_path, null=True, blank=True)
#country = models.ForeignKey('chatroom.Country', on_delete=models.SET_NULL, null=True)
#state = models.ForeignKey('chatroom.State', on_delete=models.SET_NULL, null=True)
date_joined = models.DateTimeField(verbose_name='Дата регистрации', blank=True, null=True, auto_now_add=True)
last_updated = models.DateTimeField(verbose_name='Последнее обновление', blank=True, null=True, auto_now=True)
last_login = models.DateTimeField(verbose_name='Последняя авторизация', blank=True, null=True, auto_now=True)
EMAIL_FIELD = "email"
USERNAME_FIELD = "email"
REQUIRED_FIELDS: list[str] = []
objects = UserManager()
def get_full_name(self) -> str:
if self.first_name and self.last_name:
return f"{self.first_name} {self.last_name}"
else:
return self.username
def get_short_name(self) -> str:
if self.first_name and self.last_name:
return f"{self.first_name[0]}{self.last_name}"
else:
return self.username

View File

@@ -0,0 +1,40 @@
from rest_framework import serializers
from system.accounts.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'email', 'first_name', 'last_name', 'password', 'bio', 'image')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
password = validated_data.pop('password')
user = User(
**validated_data
)
user.set_password(password)
user.save()
return user
def update(self, instance, validated_data):
for key, value in validated_data.items():
if key == 'password':
instance.set_password(value)
else:
setattr(instance, key, value)
instance.save()
return instance
class ProfileSerializer(serializers.ModelSerializer):
following = serializers.SerializerMethodField()
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'bio', 'image', 'following')
def get_following(self, obj):
user = self.context.get('request').user
if user.is_authenticated:
return obj.followers.filter(pk=user.id).exists()
return False

2
system/accounts/tests.py Normal file
View File

@@ -0,0 +1,2 @@
from django.test import TestCase
# Создайте первуый тест

18
system/accounts/urls.py Normal file
View File

@@ -0,0 +1,18 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from django.contrib.auth.views import LogoutView
from django.contrib.auth import views
from django.urls import path
from system.accounts import views
profile_router = DefaultRouter(trailing_slash=False)
profile_router.register('profiles', views.ProfileDetailView)
urlpatterns = [
path('users/login', views.account_login, name='account-login'),
path('users', views.account_registration, name="account-registration"),
path('user', views.UserView.as_view(), name='user-account'),
path('signout/', views.signout, name='signout'),
path('logout/', views.signout, name='logout'),
path('', include(profile_router.urls))
]

135
system/accounts/views.py Normal file
View File

@@ -0,0 +1,135 @@
from rest_framework.decorators import api_view, action
from rest_framework.response import Response
from rest_framework import status, views, viewsets
from django.contrib.auth import authenticate
from django.shortcuts import redirect
from django.contrib.auth import logout
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
from system.accounts.models import User
from system.accounts.serializers import UserSerializer, ProfileSerializer
@api_view(['POST', ])
def account_registration(request):
try:
user_data = request.data.get('user')
serializer = UserSerializer(data=user_data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response({"user": serializer.data}, status=status.HTTP_201_CREATED)
except Exception:
return Response(status=status.HTTP_400_BAD_REQUEST)
@api_view(['POST', ])
def account_login(request):
try:
user_data = request.data.get('user')
user = authenticate(email=user_data['email'], password=user_data['password'])
serializer = UserSerializer(user)
jwt_token = RefreshToken.for_user(user)
serializer_data = serializer.data
serializer_data['token'] = str(jwt_token.access_token)
response_data = {
"user": serializer_data,
}
return Response(response_data, status=status.HTTP_202_ACCEPTED)
except Exception:
return Response(status=status.HTTP_400_BAD_REQUEST)
class UserView(views.APIView):
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
user = self.request.user
serializer = UserSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK)
def put(self, request, format=None, pk=None):
user = self.request.user
user_data = request.data.get('user')
user.email = user_data['email']
user.bio = user_data['bio']
user.image = user_data['image']
user.save()
serializer = UserSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK)
class ProfileDetailView(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = ProfileSerializer
permission_classes = [IsAuthenticated]
lookup_field = 'username'
http_method_names = ['get', 'post', 'delete']
def get_permissions(self):
if self.action == 'list':
return [IsAuthenticatedOrReadOnly(), ]
return super().get_permissions()
def list(self, request, username=None, *args, **kwargs):
try:
profile = User.objects.get(username=username)
serializer = self.get_serializer(profile)
return Response({"profile": serializer.data})
except Exception:
return Response({"errors": {
"body": [
"Invalid User"
]
}})
@action(detail=True, methods=['post', 'delete'])
def follow(self, request, username=None, *args, **kwargs):
if request.method == 'POST':
profile = self.get_object()
follower = request.user
if profile == follower:
return Response({"errors": {
"body": [
"Invalid follow Request"
]
}}, status=status.HTTP_400_BAD_REQUEST)
profile.followers.add(follower)
serializer = self.get_serializer(profile)
return Response({"profile": serializer.data})
elif request.method == 'DELETE':
profile = self.get_object()
follower = request.user
if profile == follower:
return Response({"errors": {
"body": [
"Invalid follow Request"
]
}}, status=status.HTTP_400_BAD_REQUEST)
if not profile.followers.filter(pk=follower.id).exists():
return Response({"errors": {
"body": [
"Invalid follow Request"
]
}}, status=status.HTTP_400_BAD_REQUEST)
profile.followers.remove(follower)
serializer = self.get_serializer(profile)
return Response({"profile": serializer.data})
def signout(request):
logout(request)
return redirect("main")