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.
@@ -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