feat: várias melhorias e evoluções no projeto
This commit is contained in:
0
diarios_oficiais_alems/users/__init__.py
Normal file
0
diarios_oficiais_alems/users/__init__.py
Normal file
48
diarios_oficiais_alems/users/adapters.py
Normal file
48
diarios_oficiais_alems/users/adapters.py
Normal file
@ -0,0 +1,48 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import typing
|
||||
|
||||
from allauth.account.adapter import DefaultAccountAdapter
|
||||
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
|
||||
from django.conf import settings
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from allauth.socialaccount.models import SocialLogin
|
||||
from django.http import HttpRequest
|
||||
|
||||
from diarios_oficiais_alems.users.models import User
|
||||
|
||||
|
||||
class AccountAdapter(DefaultAccountAdapter):
|
||||
def is_open_for_signup(self, request: HttpRequest) -> bool:
|
||||
return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True)
|
||||
|
||||
|
||||
class SocialAccountAdapter(DefaultSocialAccountAdapter):
|
||||
def is_open_for_signup(
|
||||
self,
|
||||
request: HttpRequest,
|
||||
sociallogin: SocialLogin,
|
||||
) -> bool:
|
||||
return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True)
|
||||
|
||||
def populate_user(
|
||||
self,
|
||||
request: HttpRequest,
|
||||
sociallogin: SocialLogin,
|
||||
data: dict[str, typing.Any],
|
||||
) -> User:
|
||||
"""
|
||||
Populates user information from social provider info.
|
||||
|
||||
See: https://docs.allauth.org/en/latest/socialaccount/advanced.html#creating-and-populating-user-instances
|
||||
"""
|
||||
user = super().populate_user(request, sociallogin, data)
|
||||
if not user.name:
|
||||
if name := data.get("name"):
|
||||
user.name = name
|
||||
elif first_name := data.get("first_name"):
|
||||
user.name = first_name
|
||||
if last_name := data.get("last_name"):
|
||||
user.name += f" {last_name}"
|
||||
return user
|
||||
40
diarios_oficiais_alems/users/admin.py
Normal file
40
diarios_oficiais_alems/users/admin.py
Normal file
@ -0,0 +1,40 @@
|
||||
from allauth.account.decorators import secure_admin_login
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth import admin as auth_admin
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .forms import UserAdminChangeForm
|
||||
from .forms import UserAdminCreationForm
|
||||
from .models import User
|
||||
|
||||
if settings.DJANGO_ADMIN_FORCE_ALLAUTH:
|
||||
# Force the `admin` sign in process to go through the `django-allauth` workflow:
|
||||
# https://docs.allauth.org/en/latest/common/admin.html#admin
|
||||
admin.autodiscover()
|
||||
admin.site.login = secure_admin_login(admin.site.login) # type: ignore[method-assign]
|
||||
|
||||
|
||||
@admin.register(User)
|
||||
class UserAdmin(auth_admin.UserAdmin):
|
||||
form = UserAdminChangeForm
|
||||
add_form = UserAdminCreationForm
|
||||
fieldsets = (
|
||||
(None, {"fields": ("username", "password")}),
|
||||
(_("Personal info"), {"fields": ("name", "email")}),
|
||||
(
|
||||
_("Permissions"),
|
||||
{
|
||||
"fields": (
|
||||
"is_active",
|
||||
"is_staff",
|
||||
"is_superuser",
|
||||
"groups",
|
||||
"user_permissions",
|
||||
),
|
||||
},
|
||||
),
|
||||
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
|
||||
)
|
||||
list_display = ["username", "name", "is_superuser"]
|
||||
search_fields = ["name"]
|
||||
13
diarios_oficiais_alems/users/apps.py
Normal file
13
diarios_oficiais_alems/users/apps.py
Normal file
@ -0,0 +1,13 @@
|
||||
import contextlib
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class UsersConfig(AppConfig):
|
||||
name = "diarios_oficiais_alems.users"
|
||||
verbose_name = _("Users")
|
||||
|
||||
def ready(self):
|
||||
with contextlib.suppress(ImportError):
|
||||
import diarios_oficiais_alems.users.signals # noqa: F401
|
||||
8
diarios_oficiais_alems/users/context_processors.py
Normal file
8
diarios_oficiais_alems/users/context_processors.py
Normal file
@ -0,0 +1,8 @@
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def allauth_settings(request):
|
||||
"""Expose some settings from django-allauth in templates."""
|
||||
return {
|
||||
"ACCOUNT_ALLOW_REGISTRATION": settings.ACCOUNT_ALLOW_REGISTRATION,
|
||||
}
|
||||
40
diarios_oficiais_alems/users/forms.py
Normal file
40
diarios_oficiais_alems/users/forms.py
Normal file
@ -0,0 +1,40 @@
|
||||
from allauth.account.forms import SignupForm
|
||||
from allauth.socialaccount.forms import SignupForm as SocialSignupForm
|
||||
from django.contrib.auth import forms as admin_forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .models import User
|
||||
|
||||
|
||||
class UserAdminChangeForm(admin_forms.UserChangeForm):
|
||||
class Meta(admin_forms.UserChangeForm.Meta): # type: ignore[name-defined]
|
||||
model = User
|
||||
|
||||
|
||||
class UserAdminCreationForm(admin_forms.UserCreationForm):
|
||||
"""
|
||||
Form for User Creation in the Admin Area.
|
||||
To change user signup, see UserSignupForm and UserSocialSignupForm.
|
||||
"""
|
||||
|
||||
class Meta(admin_forms.UserCreationForm.Meta): # type: ignore[name-defined]
|
||||
model = User
|
||||
error_messages = {
|
||||
"username": {"unique": _("This username has already been taken.")},
|
||||
}
|
||||
|
||||
|
||||
class UserSignupForm(SignupForm):
|
||||
"""
|
||||
Form that will be rendered on a user sign up section/screen.
|
||||
Default fields will be added automatically.
|
||||
Check UserSocialSignupForm for accounts created from social.
|
||||
"""
|
||||
|
||||
|
||||
class UserSocialSignupForm(SocialSignupForm):
|
||||
"""
|
||||
Renders the form when user has signed up using social accounts.
|
||||
Default fields will be added automatically.
|
||||
See UserSignupForm otherwise.
|
||||
"""
|
||||
132
diarios_oficiais_alems/users/migrations/0001_initial.py
Normal file
132
diarios_oficiais_alems/users/migrations/0001_initial.py
Normal file
@ -0,0 +1,132 @@
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
import django.utils.timezone
|
||||
from django.db import migrations
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("auth", "0012_alter_user_first_name_max_length"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="User",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("password", models.CharField(max_length=128, verbose_name="password")),
|
||||
(
|
||||
"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",
|
||||
),
|
||||
),
|
||||
(
|
||||
"username",
|
||||
models.CharField(
|
||||
error_messages={
|
||||
"unique": "A user with that username already exists."
|
||||
},
|
||||
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
|
||||
max_length=150,
|
||||
unique=True,
|
||||
validators=[
|
||||
django.contrib.auth.validators.UnicodeUsernameValidator()
|
||||
],
|
||||
verbose_name="username",
|
||||
),
|
||||
),
|
||||
(
|
||||
"email",
|
||||
models.EmailField(
|
||||
blank=True,
|
||||
max_length=254,
|
||||
verbose_name="email address",
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_staff",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Designates whether the user can log into this admin site.",
|
||||
verbose_name="staff status",
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_active",
|
||||
models.BooleanField(
|
||||
default=True,
|
||||
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
|
||||
verbose_name="active",
|
||||
),
|
||||
),
|
||||
(
|
||||
"date_joined",
|
||||
models.DateTimeField(
|
||||
default=django.utils.timezone.now,
|
||||
verbose_name="date joined",
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
max_length=255,
|
||||
verbose_name="Name of User",
|
||||
),
|
||||
),
|
||||
(
|
||||
"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",
|
||||
),
|
||||
),
|
||||
(
|
||||
"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={
|
||||
"verbose_name": "user",
|
||||
"verbose_name_plural": "users",
|
||||
"abstract": False,
|
||||
},
|
||||
managers=[
|
||||
("objects", django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
]
|
||||
0
diarios_oficiais_alems/users/migrations/__init__.py
Normal file
0
diarios_oficiais_alems/users/migrations/__init__.py
Normal file
26
diarios_oficiais_alems/users/models.py
Normal file
26
diarios_oficiais_alems/users/models.py
Normal file
@ -0,0 +1,26 @@
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db.models import CharField
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
"""
|
||||
Default custom user model for Diários Oficiais ALEMS.
|
||||
If adding fields that need to be filled at user signup,
|
||||
check forms.SignupForm and forms.SocialSignupForms accordingly.
|
||||
"""
|
||||
|
||||
# First and last name do not cover name patterns around the globe
|
||||
name = CharField(_("Name of User"), blank=True, max_length=255)
|
||||
first_name = None # type: ignore[assignment]
|
||||
last_name = None # type: ignore[assignment]
|
||||
|
||||
def get_absolute_url(self) -> str:
|
||||
"""Get URL for user's detail view.
|
||||
|
||||
Returns:
|
||||
str: URL for user detail.
|
||||
|
||||
"""
|
||||
return reverse("users:detail", kwargs={"username": self.username})
|
||||
0
diarios_oficiais_alems/users/tests/__init__.py
Normal file
0
diarios_oficiais_alems/users/tests/__init__.py
Normal file
43
diarios_oficiais_alems/users/tests/factories.py
Normal file
43
diarios_oficiais_alems/users/tests/factories.py
Normal file
@ -0,0 +1,43 @@
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from factory import Faker
|
||||
from factory import post_generation
|
||||
from factory.django import DjangoModelFactory
|
||||
|
||||
from diarios_oficiais_alems.users.models import User
|
||||
|
||||
|
||||
class UserFactory(DjangoModelFactory[User]):
|
||||
username = Faker("user_name")
|
||||
email = Faker("email")
|
||||
name = Faker("name")
|
||||
|
||||
@post_generation
|
||||
def password(
|
||||
self, create: bool, extracted: Sequence[Any], **kwargs
|
||||
): # noqa: FBT001
|
||||
password = (
|
||||
extracted
|
||||
if extracted
|
||||
else Faker(
|
||||
"password",
|
||||
length=42,
|
||||
special_chars=True,
|
||||
digits=True,
|
||||
upper_case=True,
|
||||
lower_case=True,
|
||||
).evaluate(None, None, extra={"locale": None})
|
||||
)
|
||||
self.set_password(password)
|
||||
|
||||
@classmethod
|
||||
def _after_postgeneration(cls, instance, create, results=None):
|
||||
"""Save again the instance if creating and at least one hook ran."""
|
||||
if create and results and not cls._meta.skip_postgeneration_save:
|
||||
# Some post-generation hooks ran, and may have modified us.
|
||||
instance.save()
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
django_get_or_create = ["username"]
|
||||
65
diarios_oficiais_alems/users/tests/test_admin.py
Normal file
65
diarios_oficiais_alems/users/tests/test_admin.py
Normal file
@ -0,0 +1,65 @@
|
||||
import contextlib
|
||||
from http import HTTPStatus
|
||||
from importlib import reload
|
||||
|
||||
import pytest
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.urls import reverse
|
||||
from pytest_django.asserts import assertRedirects
|
||||
|
||||
from diarios_oficiais_alems.users.models import User
|
||||
|
||||
|
||||
class TestUserAdmin:
|
||||
def test_changelist(self, admin_client):
|
||||
url = reverse("admin:users_user_changelist")
|
||||
response = admin_client.get(url)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
def test_search(self, admin_client):
|
||||
url = reverse("admin:users_user_changelist")
|
||||
response = admin_client.get(url, data={"q": "test"})
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
def test_add(self, admin_client):
|
||||
url = reverse("admin:users_user_add")
|
||||
response = admin_client.get(url)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
response = admin_client.post(
|
||||
url,
|
||||
data={
|
||||
"username": "test",
|
||||
"password1": "My_R@ndom-P@ssw0rd",
|
||||
"password2": "My_R@ndom-P@ssw0rd",
|
||||
},
|
||||
)
|
||||
assert response.status_code == HTTPStatus.FOUND
|
||||
assert User.objects.filter(username="test").exists()
|
||||
|
||||
def test_view_user(self, admin_client):
|
||||
user = User.objects.get(username="admin")
|
||||
url = reverse("admin:users_user_change", kwargs={"object_id": user.pk})
|
||||
response = admin_client.get(url)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
@pytest.fixture
|
||||
def _force_allauth(self, settings):
|
||||
settings.DJANGO_ADMIN_FORCE_ALLAUTH = True
|
||||
# Reload the admin module to apply the setting change
|
||||
import diarios_oficiais_alems.users.admin as users_admin
|
||||
|
||||
with contextlib.suppress(admin.sites.AlreadyRegistered): # type: ignore[attr-defined]
|
||||
reload(users_admin)
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.usefixtures("_force_allauth")
|
||||
def test_allauth_login(self, rf, settings):
|
||||
request = rf.get("/fake-url")
|
||||
request.user = AnonymousUser()
|
||||
response = admin.site.login(request)
|
||||
|
||||
# The `admin` login view should redirect to the `allauth` login view
|
||||
target_url = reverse(settings.LOGIN_URL) + "?next=" + request.path
|
||||
assertRedirects(response, target_url, fetch_redirect_response=False)
|
||||
35
diarios_oficiais_alems/users/tests/test_forms.py
Normal file
35
diarios_oficiais_alems/users/tests/test_forms.py
Normal file
@ -0,0 +1,35 @@
|
||||
"""Module for all Form Tests."""
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from diarios_oficiais_alems.users.forms import UserAdminCreationForm
|
||||
from diarios_oficiais_alems.users.models import User
|
||||
|
||||
|
||||
class TestUserAdminCreationForm:
|
||||
"""
|
||||
Test class for all tests related to the UserAdminCreationForm
|
||||
"""
|
||||
|
||||
def test_username_validation_error_msg(self, user: User):
|
||||
"""
|
||||
Tests UserAdminCreation Form's unique validator functions correctly by testing:
|
||||
1) A new user with an existing username cannot be added.
|
||||
2) Only 1 error is raised by the UserCreation Form
|
||||
3) The desired error message is raised
|
||||
"""
|
||||
|
||||
# The user already exists,
|
||||
# hence cannot be created.
|
||||
form = UserAdminCreationForm(
|
||||
{
|
||||
"username": user.username,
|
||||
"password1": user.password,
|
||||
"password2": user.password,
|
||||
},
|
||||
)
|
||||
|
||||
assert not form.is_valid()
|
||||
assert len(form.errors) == 1
|
||||
assert "username" in form.errors
|
||||
assert form.errors["username"][0] == _("This username has already been taken.")
|
||||
5
diarios_oficiais_alems/users/tests/test_models.py
Normal file
5
diarios_oficiais_alems/users/tests/test_models.py
Normal file
@ -0,0 +1,5 @@
|
||||
from diarios_oficiais_alems.users.models import User
|
||||
|
||||
|
||||
def test_user_get_absolute_url(user: User):
|
||||
assert user.get_absolute_url() == f"/users/{user.username}/"
|
||||
22
diarios_oficiais_alems/users/tests/test_urls.py
Normal file
22
diarios_oficiais_alems/users/tests/test_urls.py
Normal file
@ -0,0 +1,22 @@
|
||||
from django.urls import resolve
|
||||
from django.urls import reverse
|
||||
|
||||
from diarios_oficiais_alems.users.models import User
|
||||
|
||||
|
||||
def test_detail(user: User):
|
||||
assert (
|
||||
reverse("users:detail", kwargs={"username": user.username})
|
||||
== f"/users/{user.username}/"
|
||||
)
|
||||
assert resolve(f"/users/{user.username}/").view_name == "users:detail"
|
||||
|
||||
|
||||
def test_update():
|
||||
assert reverse("users:update") == "/users/~update/"
|
||||
assert resolve("/users/~update/").view_name == "users:update"
|
||||
|
||||
|
||||
def test_redirect():
|
||||
assert reverse("users:redirect") == "/users/~redirect/"
|
||||
assert resolve("/users/~redirect/").view_name == "users:redirect"
|
||||
101
diarios_oficiais_alems/users/tests/test_views.py
Normal file
101
diarios_oficiais_alems/users/tests/test_views.py
Normal file
@ -0,0 +1,101 @@
|
||||
from http import HTTPStatus
|
||||
|
||||
import pytest
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.contrib.messages.middleware import MessageMiddleware
|
||||
from django.contrib.sessions.middleware import SessionMiddleware
|
||||
from django.http import HttpRequest
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.test import RequestFactory
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from diarios_oficiais_alems.users.forms import UserAdminChangeForm
|
||||
from diarios_oficiais_alems.users.models import User
|
||||
from diarios_oficiais_alems.users.tests.factories import UserFactory
|
||||
from diarios_oficiais_alems.users.views import UserRedirectView
|
||||
from diarios_oficiais_alems.users.views import UserUpdateView
|
||||
from diarios_oficiais_alems.users.views import user_detail_view
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
class TestUserUpdateView:
|
||||
"""
|
||||
TODO:
|
||||
extracting view initialization code as class-scoped fixture
|
||||
would be great if only pytest-django supported non-function-scoped
|
||||
fixture db access -- this is a work-in-progress for now:
|
||||
https://github.com/pytest-dev/pytest-django/pull/258
|
||||
"""
|
||||
|
||||
def dummy_get_response(self, request: HttpRequest):
|
||||
return None
|
||||
|
||||
def test_get_success_url(self, user: User, rf: RequestFactory):
|
||||
view = UserUpdateView()
|
||||
request = rf.get("/fake-url/")
|
||||
request.user = user
|
||||
|
||||
view.request = request
|
||||
assert view.get_success_url() == f"/users/{user.username}/"
|
||||
|
||||
def test_get_object(self, user: User, rf: RequestFactory):
|
||||
view = UserUpdateView()
|
||||
request = rf.get("/fake-url/")
|
||||
request.user = user
|
||||
|
||||
view.request = request
|
||||
|
||||
assert view.get_object() == user
|
||||
|
||||
def test_form_valid(self, user: User, rf: RequestFactory):
|
||||
view = UserUpdateView()
|
||||
request = rf.get("/fake-url/")
|
||||
|
||||
# Add the session/message middleware to the request
|
||||
SessionMiddleware(self.dummy_get_response).process_request(request)
|
||||
MessageMiddleware(self.dummy_get_response).process_request(request)
|
||||
request.user = user
|
||||
|
||||
view.request = request
|
||||
|
||||
# Initialize the form
|
||||
form = UserAdminChangeForm()
|
||||
form.cleaned_data = {}
|
||||
form.instance = user
|
||||
view.form_valid(form)
|
||||
|
||||
messages_sent = [m.message for m in messages.get_messages(request)]
|
||||
assert messages_sent == [_("Information successfully updated")]
|
||||
|
||||
|
||||
class TestUserRedirectView:
|
||||
def test_get_redirect_url(self, user: User, rf: RequestFactory):
|
||||
view = UserRedirectView()
|
||||
request = rf.get("/fake-url")
|
||||
request.user = user
|
||||
|
||||
view.request = request
|
||||
assert view.get_redirect_url() == f"/users/{user.username}/"
|
||||
|
||||
|
||||
class TestUserDetailView:
|
||||
def test_authenticated(self, user: User, rf: RequestFactory):
|
||||
request = rf.get("/fake-url/")
|
||||
request.user = UserFactory()
|
||||
response = user_detail_view(request, username=user.username)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
|
||||
#def test_not_authenticated(self, user: User, rf: RequestFactory):
|
||||
# request = rf.get("/fake-url/")
|
||||
# request.user = AnonymousUser()
|
||||
# response = user_detail_view(request, username=user.username)
|
||||
# login_url = reverse(settings.LOGIN_URL)
|
||||
|
||||
# assert isinstance(response, HttpResponseRedirect)
|
||||
# assert response.status_code == HTTPStatus.FOUND
|
||||
# assert response.url == f"{login_url}?next=/fake-url/"
|
||||
12
diarios_oficiais_alems/users/urls.py
Normal file
12
diarios_oficiais_alems/users/urls.py
Normal file
@ -0,0 +1,12 @@
|
||||
from django.urls import path
|
||||
|
||||
from .views import user_detail_view
|
||||
from .views import user_redirect_view
|
||||
from .views import user_update_view
|
||||
|
||||
app_name = "users"
|
||||
urlpatterns = [
|
||||
path("~redirect/", view=user_redirect_view, name="redirect"),
|
||||
path("~update/", view=user_update_view, name="update"),
|
||||
path("<str:username>/", view=user_detail_view, name="detail"),
|
||||
]
|
||||
46
diarios_oficiais_alems/users/views.py
Normal file
46
diarios_oficiais_alems/users/views.py
Normal file
@ -0,0 +1,46 @@
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.db.models import QuerySet
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import DetailView
|
||||
from django.views.generic import RedirectView
|
||||
from django.views.generic import UpdateView
|
||||
|
||||
from diarios_oficiais_alems.users.models import User
|
||||
|
||||
|
||||
class UserDetailView(LoginRequiredMixin, DetailView):
|
||||
model = User
|
||||
slug_field = "username"
|
||||
slug_url_kwarg = "username"
|
||||
|
||||
|
||||
user_detail_view = UserDetailView.as_view()
|
||||
|
||||
|
||||
class UserUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
model = User
|
||||
fields = ["name"]
|
||||
success_message = _("Information successfully updated")
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
assert self.request.user.is_authenticated # type guard
|
||||
return self.request.user.get_absolute_url()
|
||||
|
||||
def get_object(self, queryset: QuerySet | None = None) -> User:
|
||||
assert self.request.user.is_authenticated # type guard
|
||||
return self.request.user
|
||||
|
||||
|
||||
user_update_view = UserUpdateView.as_view()
|
||||
|
||||
|
||||
class UserRedirectView(LoginRequiredMixin, RedirectView):
|
||||
permanent = False
|
||||
|
||||
def get_redirect_url(self) -> str:
|
||||
return reverse("users:detail", kwargs={"username": self.request.user.username})
|
||||
|
||||
|
||||
user_redirect_view = UserRedirectView.as_view()
|
||||
Reference in New Issue
Block a user