maquinaweb-shared-auth 0.2.60__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- maquinaweb_shared_auth-0.2.60.dist-info/METADATA +1003 -0
- maquinaweb_shared_auth-0.2.60.dist-info/RECORD +28 -0
- maquinaweb_shared_auth-0.2.60.dist-info/WHEEL +5 -0
- maquinaweb_shared_auth-0.2.60.dist-info/top_level.txt +1 -0
- shared_auth/__init__.py +7 -0
- shared_auth/abstract_models.py +897 -0
- shared_auth/app.py +9 -0
- shared_auth/authentication.py +55 -0
- shared_auth/conf.py +33 -0
- shared_auth/decorators.py +122 -0
- shared_auth/exceptions.py +23 -0
- shared_auth/fields.py +51 -0
- shared_auth/management/__init__.py +0 -0
- shared_auth/management/commands/__init__.py +0 -0
- shared_auth/management/commands/generate_permissions.py +147 -0
- shared_auth/managers.py +344 -0
- shared_auth/middleware.py +281 -0
- shared_auth/mixins.py +475 -0
- shared_auth/models.py +191 -0
- shared_auth/permissions.py +266 -0
- shared_auth/permissions_cache.py +249 -0
- shared_auth/permissions_helpers.py +251 -0
- shared_auth/router.py +22 -0
- shared_auth/serializers.py +439 -0
- shared_auth/storage_backend.py +6 -0
- shared_auth/urls.py +8 -0
- shared_auth/utils.py +356 -0
- shared_auth/views.py +40 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Helper functions para checagem e listagem de permissões
|
|
3
|
+
Funções principais para o sistema de permissões multi-sistema
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def user_has_permission(user_id, organization_id, permission_codename, system_id, request=None):
|
|
8
|
+
"""
|
|
9
|
+
Verifica se um usuário tem uma permissão específica.
|
|
10
|
+
|
|
11
|
+
OTIMIZADO: Usa cache por request para eliminar queries duplicadas.
|
|
12
|
+
|
|
13
|
+
Fluxo:
|
|
14
|
+
1. Busca Member (user + organization) - COM CACHE
|
|
15
|
+
2. Busca MemberSystemGroup (member + system) - COM CACHE
|
|
16
|
+
3. Busca GroupOrganizationPermissions - COM CACHE
|
|
17
|
+
4. Verifica se permission_codename está no grupo - COM CACHE
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
user_id: ID do usuário
|
|
21
|
+
organization_id: ID da organização
|
|
22
|
+
permission_codename: Código da permissão (ex: 'create_invoices')
|
|
23
|
+
system_id: ID do sistema
|
|
24
|
+
request: Request object (opcional, para cache)
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
bool: True se usuário tem a permissão, False caso contrário
|
|
28
|
+
|
|
29
|
+
Usage:
|
|
30
|
+
from shared_auth.permissions_helpers import user_has_permission
|
|
31
|
+
|
|
32
|
+
if user_has_permission(5, 1, 'create_invoices', 2, request):
|
|
33
|
+
# Usuário pode criar faturas
|
|
34
|
+
pass
|
|
35
|
+
"""
|
|
36
|
+
from .permissions_cache import (
|
|
37
|
+
get_cached_member,
|
|
38
|
+
get_cached_member_group,
|
|
39
|
+
get_cached_permission_codenames,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# 1. Buscar membro (COM CACHE)
|
|
43
|
+
member = get_cached_member(user_id, organization_id, request)
|
|
44
|
+
if not member:
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
# 2. Buscar grupo do membro no sistema (COM CACHE)
|
|
48
|
+
member_group = get_cached_member_group(member.id, system_id, request)
|
|
49
|
+
if not member_group:
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
# 3. Buscar codenames de permissões do grupo (COM CACHE)
|
|
53
|
+
codenames = get_cached_permission_codenames(member_group.group_id, system_id, request)
|
|
54
|
+
|
|
55
|
+
# 4. Verificar se permissão está no set (O(1) lookup)
|
|
56
|
+
return permission_codename in codenames
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_user_permissions(user_id, organization_id, system_id, request=None):
|
|
60
|
+
"""
|
|
61
|
+
Retorna todas as permissões do usuário em um sistema.
|
|
62
|
+
|
|
63
|
+
OTIMIZADO: Usa cache por request para eliminar queries duplicadas.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
user_id: ID do usuário
|
|
67
|
+
organization_id: ID da organização
|
|
68
|
+
system_id: ID do sistema
|
|
69
|
+
request: Request object (opcional, para cache)
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
list[Permission]: Lista de permissões do usuário
|
|
73
|
+
|
|
74
|
+
Usage:
|
|
75
|
+
from shared_auth.permissions_helpers import get_user_permissions
|
|
76
|
+
|
|
77
|
+
perms = get_user_permissions(5, 1, 2, request)
|
|
78
|
+
for perm in perms:
|
|
79
|
+
print(perm.codename)
|
|
80
|
+
"""
|
|
81
|
+
from .permissions_cache import (
|
|
82
|
+
get_cached_all_permissions,
|
|
83
|
+
get_cached_member,
|
|
84
|
+
get_cached_member_group,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# 1. Buscar membro (COM CACHE)
|
|
88
|
+
member = get_cached_member(user_id, organization_id, request)
|
|
89
|
+
if not member:
|
|
90
|
+
return []
|
|
91
|
+
|
|
92
|
+
# 2. Buscar grupo do membro no sistema (COM CACHE)
|
|
93
|
+
member_group = get_cached_member_group(member.id, system_id, request)
|
|
94
|
+
if not member_group:
|
|
95
|
+
return []
|
|
96
|
+
|
|
97
|
+
# 3. Buscar todas as permissões (COM CACHE)
|
|
98
|
+
return get_cached_all_permissions(member_group.group_id, system_id, request)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def get_user_permission_codenames(user_id, organization_id, system_id, request=None):
|
|
102
|
+
"""
|
|
103
|
+
Retorna lista de codenames de permissões do usuário.
|
|
104
|
+
|
|
105
|
+
OTIMIZADO: Usa cache por request.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
user_id: ID do usuário
|
|
109
|
+
organization_id: ID da organização
|
|
110
|
+
system_id: ID do sistema
|
|
111
|
+
request: Request object (opcional, para cache)
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
List[str]: Lista de codenames
|
|
115
|
+
|
|
116
|
+
Usage:
|
|
117
|
+
from shared_auth.permissions_helpers import get_user_permission_codenames
|
|
118
|
+
|
|
119
|
+
codenames = get_user_permission_codenames(5, 1, 2, request)
|
|
120
|
+
# ['create_invoices', 'edit_invoices', 'view_reports']
|
|
121
|
+
"""
|
|
122
|
+
permissions = get_user_permissions(user_id, organization_id, system_id, request)
|
|
123
|
+
return [perm.codename for perm in permissions]
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def get_organization_permissions(organization_id, system_id):
|
|
127
|
+
"""
|
|
128
|
+
Retorna permissões disponíveis no plano da organização.
|
|
129
|
+
|
|
130
|
+
Fluxo:
|
|
131
|
+
1. Busca Subscription ativa
|
|
132
|
+
2. Busca Plan
|
|
133
|
+
3. Retorna permissões dos GroupPermissions do plano
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
organization_id: ID da organização
|
|
137
|
+
system_id: ID do sistema
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
QuerySet[Permission]: Permissões do plano
|
|
141
|
+
|
|
142
|
+
Usage:
|
|
143
|
+
from shared_auth.permissions_helpers import get_organization_permissions
|
|
144
|
+
|
|
145
|
+
perms = get_organization_permissions(1, 2)
|
|
146
|
+
for perm in perms:
|
|
147
|
+
print(perm.codename)
|
|
148
|
+
"""
|
|
149
|
+
from .utils import get_permission_model, get_subscription_model
|
|
150
|
+
|
|
151
|
+
Permission = get_permission_model()
|
|
152
|
+
|
|
153
|
+
# 1. Buscar assinatura ativa
|
|
154
|
+
Subscription = get_subscription_model()
|
|
155
|
+
subscription = Subscription.objects.valid_for_organization_and_system(
|
|
156
|
+
organization_id,
|
|
157
|
+
system_id
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
if not subscription:
|
|
161
|
+
return Permission.objects.none()
|
|
162
|
+
|
|
163
|
+
# 2. Buscar plano
|
|
164
|
+
plan = subscription.plan
|
|
165
|
+
if not plan:
|
|
166
|
+
return Permission.objects.none()
|
|
167
|
+
|
|
168
|
+
# 3. Buscar grupos de permissões do plano
|
|
169
|
+
group_permissions = plan.group_permissions.all()
|
|
170
|
+
|
|
171
|
+
# 4. Coletar todas as permissões dos grupos
|
|
172
|
+
permission_ids = []
|
|
173
|
+
for group in group_permissions:
|
|
174
|
+
permission_ids.extend(
|
|
175
|
+
group.permissions.values_list('id', flat=True)
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# 5. Retornar permissões únicas
|
|
179
|
+
return Permission.objects.filter(id__in=set(permission_ids))
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def get_organization_permission_codenames(organization_id, system_id):
|
|
183
|
+
"""
|
|
184
|
+
Retorna lista de codenames de permissões do plano da organização.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
organization_id: ID da organização
|
|
188
|
+
system_id: ID do sistema
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
List[str]: Lista de codenames
|
|
192
|
+
|
|
193
|
+
Usage:
|
|
194
|
+
from shared_auth.permissions_helpers import get_organization_permission_codenames
|
|
195
|
+
|
|
196
|
+
codenames = get_organization_permission_codenames(1, 2)
|
|
197
|
+
"""
|
|
198
|
+
permissions = get_organization_permissions(organization_id, system_id)
|
|
199
|
+
return list(permissions.values_list('codename', flat=True))
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def user_has_any_permission(user_id, organization_id, permission_codenames, system_id):
|
|
203
|
+
"""
|
|
204
|
+
Verifica se usuário tem pelo menos uma das permissões.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
user_id: ID do usuário
|
|
208
|
+
organization_id: ID da organização
|
|
209
|
+
permission_codenames: Lista de codenames
|
|
210
|
+
system_id: ID do sistema
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
bool: True se tem pelo menos uma permissão
|
|
214
|
+
|
|
215
|
+
Usage:
|
|
216
|
+
from shared_auth.permissions_helpers import user_has_any_permission
|
|
217
|
+
|
|
218
|
+
if user_has_any_permission(5, 1, ['view_reports', 'create_reports'], 2):
|
|
219
|
+
# Usuário pode ver ou criar relatórios
|
|
220
|
+
pass
|
|
221
|
+
"""
|
|
222
|
+
for codename in permission_codenames:
|
|
223
|
+
if user_has_permission(user_id, organization_id, codename, system_id):
|
|
224
|
+
return True
|
|
225
|
+
return False
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def user_has_all_permissions(user_id, organization_id, permission_codenames, system_id):
|
|
229
|
+
"""
|
|
230
|
+
Verifica se usuário tem todas as permissões.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
user_id: ID do usuário
|
|
234
|
+
organization_id: ID da organização
|
|
235
|
+
permission_codenames: Lista de codenames
|
|
236
|
+
system_id: ID do sistema
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
bool: True se tem todas as permissões
|
|
240
|
+
|
|
241
|
+
Usage:
|
|
242
|
+
from shared_auth.permissions_helpers import user_has_all_permissions
|
|
243
|
+
|
|
244
|
+
if user_has_all_permissions(5, 1, ['create_invoices', 'edit_invoices'], 2):
|
|
245
|
+
# Usuário pode criar E editar faturas
|
|
246
|
+
pass
|
|
247
|
+
"""
|
|
248
|
+
for codename in permission_codenames:
|
|
249
|
+
if not user_has_permission(user_id, organization_id, codename, system_id):
|
|
250
|
+
return False
|
|
251
|
+
return True
|
shared_auth/router.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class SharedAuthRouter:
|
|
2
|
+
"""
|
|
3
|
+
Direciona queries dos models compartilhados para o banco correto
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
route_app_labels = {"shared_auth", "auth", "admin", "contenttypes"}
|
|
7
|
+
|
|
8
|
+
def db_for_read(self, model, **hints):
|
|
9
|
+
if model._meta.app_label in self.route_app_labels:
|
|
10
|
+
return "auth_db"
|
|
11
|
+
return None
|
|
12
|
+
|
|
13
|
+
def db_for_write(self, model, **hints):
|
|
14
|
+
if model._meta.app_label in self.route_app_labels:
|
|
15
|
+
return "auth_db"
|
|
16
|
+
return None
|
|
17
|
+
|
|
18
|
+
def allow_migrate(self, db, app_label, model_name=None, **hints):
|
|
19
|
+
"""Bloqueia migrations"""
|
|
20
|
+
if app_label in self.route_app_labels:
|
|
21
|
+
return False
|
|
22
|
+
return None
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Serializers compartilhados para DRF
|
|
3
|
+
|
|
4
|
+
Separação de responsabilidades:
|
|
5
|
+
- CreateSerializerMixin: apenas para criação (seta IDs do request)
|
|
6
|
+
- SerializerMixin: listagem com dados aninhados + criação (compatibilidade com código existente)
|
|
7
|
+
|
|
8
|
+
Se quiser usar separadamente:
|
|
9
|
+
- Use apenas *CreateSerializerMixin para create
|
|
10
|
+
- Use apenas *SerializerMixin para listagem
|
|
11
|
+
- Ou herde ambos se precisar
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from rest_framework import serializers
|
|
15
|
+
|
|
16
|
+
from .utils import get_organization_model, get_organization_serializer, get_user_model
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class OrganizationCreateSerializerMixin(serializers.ModelSerializer):
|
|
20
|
+
"""
|
|
21
|
+
Mixin APENAS para criação.
|
|
22
|
+
Automaticamente seta organization_id no create a partir do request context.
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
class RascunhoCreateSerializer(OrganizationCreateSerializerMixin, serializers.ModelSerializer):
|
|
26
|
+
class Meta:
|
|
27
|
+
model = Rascunho
|
|
28
|
+
fields = ['id', 'titulo', 'conteudo']
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
organization_id = serializers.IntegerField(required=False)
|
|
32
|
+
|
|
33
|
+
def create(self, validated_data):
|
|
34
|
+
"""Automatically set organization_id from request context"""
|
|
35
|
+
if self.context.get("request") and hasattr(
|
|
36
|
+
self.context["request"], "organization_id"
|
|
37
|
+
):
|
|
38
|
+
validated_data["organization_id"] = self.context["request"].organization_id
|
|
39
|
+
return super().create(validated_data)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class OrganizationSerializerMixin(serializers.ModelSerializer):
|
|
43
|
+
"""
|
|
44
|
+
Mixin para serializers que incluem dados de organização como objeto aninhado
|
|
45
|
+
e automaticamente setam organization_id no create a partir do request context.
|
|
46
|
+
|
|
47
|
+
Retorna:
|
|
48
|
+
{
|
|
49
|
+
"id": 1,
|
|
50
|
+
"titulo": "Teste",
|
|
51
|
+
"organization": {
|
|
52
|
+
"id": 123,
|
|
53
|
+
"name": "Empresa XYZ",
|
|
54
|
+
"cnpj": "12.345.678/0001-90",
|
|
55
|
+
"email": "contato@xyz.com",
|
|
56
|
+
"is_active": true
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
Usage:
|
|
61
|
+
class RascunhoSerializer(OrganizationSerializerMixin, serializers.ModelSerializer):
|
|
62
|
+
class Meta:
|
|
63
|
+
model = Rascunho
|
|
64
|
+
fields = ['id', 'titulo', 'organization']
|
|
65
|
+
|
|
66
|
+
# Ou apenas para listagem (sem create):
|
|
67
|
+
class RascunhoListSerializer(OrganizationSerializerMixin, serializers.ModelSerializer):
|
|
68
|
+
class Meta:
|
|
69
|
+
model = Rascunho
|
|
70
|
+
fields = ['id', 'titulo', 'organization']
|
|
71
|
+
|
|
72
|
+
# Ou combinar com create:
|
|
73
|
+
class RascunhoFullSerializer(OrganizationSerializerMixin, OrganizationCreateSerializerMixin):
|
|
74
|
+
class Meta:
|
|
75
|
+
model = Rascunho
|
|
76
|
+
fields = ['id', 'titulo', 'organization']
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
organization = serializers.SerializerMethodField()
|
|
80
|
+
|
|
81
|
+
def get_organization(self, obj):
|
|
82
|
+
req = self.context.get("request")
|
|
83
|
+
org = getattr(req, "_orgs_dict", {}).get(obj.organization_id) if req else None
|
|
84
|
+
if org:
|
|
85
|
+
return self._serialize_org(org)
|
|
86
|
+
return self._serialize_org(obj.organization)
|
|
87
|
+
|
|
88
|
+
def _serialize_org(self, org):
|
|
89
|
+
return {
|
|
90
|
+
"id": org.pk,
|
|
91
|
+
"name": org.name,
|
|
92
|
+
"fantasy_name": org.fantasy_name,
|
|
93
|
+
"image_organization": org.image_organization.url
|
|
94
|
+
if org.image_organization
|
|
95
|
+
else None,
|
|
96
|
+
"cnpj": org.cnpj,
|
|
97
|
+
"email": org.email,
|
|
98
|
+
"telephone": org.telephone,
|
|
99
|
+
"cellphone": org.cellphone,
|
|
100
|
+
"is_branch": org.is_branch,
|
|
101
|
+
"is_active": org.is_active(),
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class OrganizationListCreateSerializerMixin(
|
|
106
|
+
OrganizationSerializerMixin, OrganizationCreateSerializerMixin
|
|
107
|
+
):
|
|
108
|
+
"""
|
|
109
|
+
Mixin COMPLETO pronto para usar: listagem + criação.
|
|
110
|
+
Combina OrganizationSerializerMixin + OrganizationCreateSerializerMixin.
|
|
111
|
+
|
|
112
|
+
Usage:
|
|
113
|
+
class RascunhoSerializer(OrganizationListCreateSerializerMixin, serializers.ModelSerializer):
|
|
114
|
+
class Meta:
|
|
115
|
+
model = Rascunho
|
|
116
|
+
fields = ['id', 'titulo', 'organization']
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class OrganizationSerializer(get_organization_serializer()):
|
|
123
|
+
"""
|
|
124
|
+
Serializer para o model de Organization configurado.
|
|
125
|
+
Usa get_organization_model() para suportar models customizados.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def __init__(self, *args, **kwargs):
|
|
129
|
+
super().__init__(*args, **kwargs)
|
|
130
|
+
self.Meta.model = get_organization_model()
|
|
131
|
+
|
|
132
|
+
class Meta:
|
|
133
|
+
model = None # Será definido dinamicamente no __init__
|
|
134
|
+
fields = "__all__"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class UserCreateSerializerMixin(serializers.ModelSerializer):
|
|
138
|
+
"""
|
|
139
|
+
Mixin APENAS para criação.
|
|
140
|
+
Automaticamente seta user_id no create a partir do request context.
|
|
141
|
+
|
|
142
|
+
Usage:
|
|
143
|
+
class RascunhoCreateSerializer(UserCreateSerializerMixin, serializers.ModelSerializer):
|
|
144
|
+
class Meta:
|
|
145
|
+
model = Rascunho
|
|
146
|
+
fields = ['id', 'titulo', 'conteudo']
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
user_id = serializers.IntegerField(required=False)
|
|
150
|
+
|
|
151
|
+
def create(self, validated_data):
|
|
152
|
+
"""Automatically set user_id from request context"""
|
|
153
|
+
if self.context.get("request") and hasattr(self.context["request"], "user"):
|
|
154
|
+
validated_data["user_id"] = self.context["request"].user.id
|
|
155
|
+
return super().create(validated_data)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class UserSerializerMixin(serializers.ModelSerializer):
|
|
159
|
+
"""
|
|
160
|
+
Mixin para serializers que incluem dados de usuário como objeto aninhado
|
|
161
|
+
e automaticamente setam user_id no create a partir do request context.
|
|
162
|
+
|
|
163
|
+
Retorna:
|
|
164
|
+
{
|
|
165
|
+
"id": 1,
|
|
166
|
+
"titulo": "Teste",
|
|
167
|
+
"user": {
|
|
168
|
+
"id": 456,
|
|
169
|
+
"username": "joao",
|
|
170
|
+
"email": "joao@xyz.com",
|
|
171
|
+
"full_name": "João Silva",
|
|
172
|
+
"is_active": true
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
Usage:
|
|
177
|
+
class RascunhoSerializer(UserSerializerMixin, serializers.ModelSerializer):
|
|
178
|
+
class Meta:
|
|
179
|
+
model = Rascunho
|
|
180
|
+
fields = ['id', 'titulo', 'user']
|
|
181
|
+
|
|
182
|
+
# Ou apenas para listagem (sem create):
|
|
183
|
+
class RascunhoListSerializer(UserSerializerMixin, serializers.ModelSerializer):
|
|
184
|
+
class Meta:
|
|
185
|
+
model = Rascunho
|
|
186
|
+
fields = ['id', 'titulo', 'user']
|
|
187
|
+
|
|
188
|
+
# Ou combinar com create:
|
|
189
|
+
class RascunhoFullSerializer(UserSerializerMixin, UserCreateSerializerMixin):
|
|
190
|
+
class Meta:
|
|
191
|
+
model = Rascunho
|
|
192
|
+
fields = ['id', 'titulo', 'user']
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
user = serializers.SerializerMethodField()
|
|
196
|
+
|
|
197
|
+
def get_user(self, obj):
|
|
198
|
+
"""Retorna dados do usuário como objeto"""
|
|
199
|
+
try:
|
|
200
|
+
user = obj.user
|
|
201
|
+
return {
|
|
202
|
+
"id": user.pk,
|
|
203
|
+
"username": user.username,
|
|
204
|
+
"email": user.email,
|
|
205
|
+
"avatar": user.avatar.url if user.avatar else None,
|
|
206
|
+
"first_name": user.first_name,
|
|
207
|
+
"last_name": user.last_name,
|
|
208
|
+
"full_name": user.get_full_name(),
|
|
209
|
+
"is_active": user.is_active,
|
|
210
|
+
}
|
|
211
|
+
except Exception:
|
|
212
|
+
return None
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class UserListCreateSerializerMixin(UserSerializerMixin, UserCreateSerializerMixin):
|
|
216
|
+
"""
|
|
217
|
+
Mixin COMPLETO pronto para usar: listagem + criação.
|
|
218
|
+
Combina UserSerializerMixin + UserCreateSerializerMixin.
|
|
219
|
+
|
|
220
|
+
Usage:
|
|
221
|
+
class RascunhoSerializer(UserListCreateSerializerMixin, serializers.ModelSerializer):
|
|
222
|
+
class Meta:
|
|
223
|
+
model = Rascunho
|
|
224
|
+
fields = ['id', 'titulo', 'user']
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
pass
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class UserSerializer(serializers.ModelSerializer):
|
|
231
|
+
"""
|
|
232
|
+
Serializer para o model de User configurado.
|
|
233
|
+
Usa get_user_model() para suportar models customizados.
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
def __init__(self, *args, **kwargs):
|
|
237
|
+
super().__init__(*args, **kwargs)
|
|
238
|
+
self.Meta.model = get_user_model()
|
|
239
|
+
|
|
240
|
+
class Meta:
|
|
241
|
+
model = None # Será definido dinamicamente no __init__
|
|
242
|
+
fields = [
|
|
243
|
+
"id",
|
|
244
|
+
"username",
|
|
245
|
+
"first_name",
|
|
246
|
+
"last_name",
|
|
247
|
+
"email",
|
|
248
|
+
"is_active",
|
|
249
|
+
"is_staff",
|
|
250
|
+
"is_superuser",
|
|
251
|
+
"date_joined",
|
|
252
|
+
"last_login",
|
|
253
|
+
]
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class OrganizationUserCreateSerializerMixin(serializers.ModelSerializer):
|
|
257
|
+
"""
|
|
258
|
+
Mixin APENAS para criação com organization + user.
|
|
259
|
+
Automaticamente seta organization_id e user_id no create a partir do request context.
|
|
260
|
+
|
|
261
|
+
Usage:
|
|
262
|
+
class RascunhoCreateSerializer(OrganizationUserCreateSerializerMixin, serializers.ModelSerializer):
|
|
263
|
+
class Meta:
|
|
264
|
+
model = Rascunho
|
|
265
|
+
fields = ['id', 'titulo', 'conteudo']
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
organization_id = serializers.IntegerField(required=False)
|
|
269
|
+
user_id = serializers.IntegerField(required=False)
|
|
270
|
+
|
|
271
|
+
def create(self, validated_data):
|
|
272
|
+
"""Automatically set both organization_id and user_id from request context"""
|
|
273
|
+
if self.context.get("request"):
|
|
274
|
+
request = self.context["request"]
|
|
275
|
+
if hasattr(request, "organization_id"):
|
|
276
|
+
validated_data["organization_id"] = request.organization_id
|
|
277
|
+
if hasattr(request, "user"):
|
|
278
|
+
validated_data["user_id"] = request.user.id
|
|
279
|
+
return super().create(validated_data)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class OrganizationUserSerializerMixin(OrganizationSerializerMixin, UserSerializerMixin):
|
|
283
|
+
"""
|
|
284
|
+
Mixin combinado com organization e user como objetos aninhados
|
|
285
|
+
e automaticamente seta organization_id e user_id no create a partir do request context.
|
|
286
|
+
|
|
287
|
+
Retorna:
|
|
288
|
+
{
|
|
289
|
+
"id": 1,
|
|
290
|
+
"titulo": "Teste",
|
|
291
|
+
"organization": {
|
|
292
|
+
"id": 123,
|
|
293
|
+
"name": "Empresa XYZ",
|
|
294
|
+
...
|
|
295
|
+
},
|
|
296
|
+
"user": {
|
|
297
|
+
"id": 456,
|
|
298
|
+
"username": "joao",
|
|
299
|
+
...
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
Usage:
|
|
304
|
+
class RascunhoSerializer(OrganizationUserSerializerMixin, serializers.ModelSerializer):
|
|
305
|
+
class Meta:
|
|
306
|
+
model = Rascunho
|
|
307
|
+
fields = ['id', 'titulo', 'conteudo', 'organization', 'user']
|
|
308
|
+
|
|
309
|
+
# Ou apenas para listagem (sem create):
|
|
310
|
+
class RascunhoListSerializer(OrganizationUserSerializerMixin, serializers.ModelSerializer):
|
|
311
|
+
class Meta:
|
|
312
|
+
model = Rascunho
|
|
313
|
+
fields = ['id', 'titulo', 'organization', 'user']
|
|
314
|
+
|
|
315
|
+
# Ou combinar com create:
|
|
316
|
+
class RascunhoFullSerializer(OrganizationUserSerializerMixin, OrganizationUserCreateSerializerMixin):
|
|
317
|
+
class Meta:
|
|
318
|
+
model = Rascunho
|
|
319
|
+
fields = ['id', 'titulo', 'organization', 'user']
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
pass
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class OrganizationUserListCreateSerializerMixin(
|
|
326
|
+
OrganizationUserSerializerMixin, OrganizationUserCreateSerializerMixin
|
|
327
|
+
):
|
|
328
|
+
"""
|
|
329
|
+
Mixin COMPLETO pronto para usar: listagem + criação com organization + user.
|
|
330
|
+
Combina OrganizationUserSerializerMixin + OrganizationUserCreateSerializerMixin.
|
|
331
|
+
|
|
332
|
+
Usage:
|
|
333
|
+
class RascunhoSerializer(OrganizationUserListCreateSerializerMixin, serializers.ModelSerializer):
|
|
334
|
+
class Meta:
|
|
335
|
+
model = Rascunho
|
|
336
|
+
fields = ['id', 'titulo', 'organization', 'user']
|
|
337
|
+
"""
|
|
338
|
+
|
|
339
|
+
pass
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
class OrganizationSimpleSerializerMixin(serializers.ModelSerializer):
|
|
343
|
+
"""
|
|
344
|
+
Versão simplificada que retorna apenas campos essenciais da organização
|
|
345
|
+
e automaticamente seta organization_id no create a partir do request context.
|
|
346
|
+
|
|
347
|
+
Usage:
|
|
348
|
+
# Para listagem:
|
|
349
|
+
class RascunhoListSerializer(OrganizationSimpleSerializerMixin, serializers.ModelSerializer):
|
|
350
|
+
class Meta:
|
|
351
|
+
model = Rascunho
|
|
352
|
+
fields = ['id', 'titulo', 'organization']
|
|
353
|
+
|
|
354
|
+
# Para criar com organization:
|
|
355
|
+
class RascunhoCreateSerializer(OrganizationSimpleSerializerMixin, OrganizationCreateSerializerMixin):
|
|
356
|
+
class Meta:
|
|
357
|
+
model = Rascunho
|
|
358
|
+
fields = ['id', 'titulo', 'organization']
|
|
359
|
+
"""
|
|
360
|
+
|
|
361
|
+
organization = serializers.SerializerMethodField()
|
|
362
|
+
|
|
363
|
+
def get_organization(self, obj):
|
|
364
|
+
try:
|
|
365
|
+
org = obj.organization
|
|
366
|
+
return {
|
|
367
|
+
"id": org.pk,
|
|
368
|
+
"name": org.name,
|
|
369
|
+
"cnpj": org.cnpj,
|
|
370
|
+
}
|
|
371
|
+
except Exception:
|
|
372
|
+
return None
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class OrganizationSimpleListCreateSerializerMixin(
|
|
376
|
+
OrganizationSimpleSerializerMixin, OrganizationCreateSerializerMixin
|
|
377
|
+
):
|
|
378
|
+
"""
|
|
379
|
+
Versão simplificada COMPLETA pronta para usar: listagem + criação.
|
|
380
|
+
Retorna apenas campos essenciais da organização.
|
|
381
|
+
|
|
382
|
+
Usage:
|
|
383
|
+
class RascunhoSerializer(OrganizationSimpleListCreateSerializerMixin, serializers.ModelSerializer):
|
|
384
|
+
class Meta:
|
|
385
|
+
model = Rascunho
|
|
386
|
+
fields = ['id', 'titulo', 'organization']
|
|
387
|
+
"""
|
|
388
|
+
|
|
389
|
+
pass
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
class UserSimpleSerializerMixin(serializers.ModelSerializer):
|
|
393
|
+
"""
|
|
394
|
+
Versão simplificada que retorna apenas campos essenciais do usuário
|
|
395
|
+
e automaticamente seta user_id no create a partir do request context.
|
|
396
|
+
|
|
397
|
+
Usage:
|
|
398
|
+
# Para listagem:
|
|
399
|
+
class RascunhoListSerializer(UserSimpleSerializerMixin, serializers.ModelSerializer):
|
|
400
|
+
class Meta:
|
|
401
|
+
model = Rascunho
|
|
402
|
+
fields = ['id', 'titulo', 'user']
|
|
403
|
+
|
|
404
|
+
# Para criar com user:
|
|
405
|
+
class RascunhoCreateSerializer(UserSimpleSerializerMixin, UserCreateSerializerMixin):
|
|
406
|
+
class Meta:
|
|
407
|
+
model = Rascunho
|
|
408
|
+
fields = ['id', 'titulo', 'user']
|
|
409
|
+
"""
|
|
410
|
+
|
|
411
|
+
user = serializers.SerializerMethodField()
|
|
412
|
+
|
|
413
|
+
def get_user(self, obj):
|
|
414
|
+
try:
|
|
415
|
+
user = obj.user
|
|
416
|
+
return {
|
|
417
|
+
"id": user.pk,
|
|
418
|
+
"email": user.email,
|
|
419
|
+
"full_name": user.get_full_name(),
|
|
420
|
+
}
|
|
421
|
+
except Exception:
|
|
422
|
+
return None
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
class UserSimpleListCreateSerializerMixin(
|
|
426
|
+
UserSimpleSerializerMixin, UserCreateSerializerMixin
|
|
427
|
+
):
|
|
428
|
+
"""
|
|
429
|
+
Versão simplificada COMPLETA pronta para usar: listagem + criação.
|
|
430
|
+
Retorna apenas campos essenciais do usuário.
|
|
431
|
+
|
|
432
|
+
Usage:
|
|
433
|
+
class RascunhoSerializer(UserSimpleListCreateSerializerMixin, serializers.ModelSerializer):
|
|
434
|
+
class Meta:
|
|
435
|
+
model = Rascunho
|
|
436
|
+
fields = ['id', 'titulo', 'user']
|
|
437
|
+
"""
|
|
438
|
+
|
|
439
|
+
pass
|