Enzo Yair

Merge branch 'feature/organismo' into 'develop'

Feature/organismo

Feature:
-Crear tabla organismo
-Crear tabla dependencia
-Crear Permisos personalizados
-Aplicar correctamente esos permisos desde el admin
-Crear endpoint para organismos y dependencias
-Test resultado:
================================================================================================== 2 passed, 1 warning in 1.79s =================================================================================================== 


See merge request !1
... ... @@ -24,7 +24,7 @@ logs
# Installer logs
pip-log.txt
# Unit test / coverage reports
# Unit tests / coverage reports
.coverage
.tox
nosetests.xml
... ...
from django.db.models import QuerySet
from django.db.models import Q
from django.contrib.auth.models import Permission
class PublicadoQuerySet(QuerySet):
def get_queryset(self):
return self.filter(publicado__isnull=False)
def filter_queryset_by_permissions(queryset, user):
if not user.is_superuser:
grupos_usuario = user.groups.all()
organismo_codenames = Permission.objects.filter(
group__in=grupos_usuario,
codename__startswith='view_'
).values_list('codename', flat=True)
organismos_permitidos = [
codename[len('view_'):].replace('_', ' ')
for codename in organismo_codenames
]
query = Q()
for name in organismos_permitidos:
query |= Q(short_name__icontains=name)
return queryset.filter(query)
return queryset
... ...
... ... @@ -28,7 +28,7 @@ def create_user(username, first_name='Admin', last_name='Root', email=None):
@pytest.fixture
def get_default_test_user():
test_user = create_user(username='test_user', first_name='Test', last_name='User', email='test@user')
test_user = create_user(username='test_user', first_name='Test', last_name='User', email='tests@user')
return test_user
... ...
from django.contrib import admin
from django.db.models import Q
from .models import Organismo, Dependencia
from core.querysets import filter_queryset_by_permissions
# Register your models here.
@admin.register(Organismo)
class OrganismoAdmin(admin.ModelAdmin):
model = Organismo
list_display = ('short_name', )
list_filter = ('short_name',)
search_fields = ('short_name', )
def get_queryset(self, request):
qs = super().get_queryset(request)
qs = filter_queryset_by_permissions(qs, request.user)
return qs
@admin.register(Dependencia)
class DependenciaAdmin(admin.ModelAdmin):
model = Dependencia
list_display = ('short_name', 'organismo',)
list_filter = ('organismo', 'short_name',)
search_fields = ('short_name',)
def get_queryset(self, request):
qs = super().get_queryset(request)
qs = filter_queryset_by_permissions(qs, request.user)
return qs
... ...
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from .models import Organismo, Dependencia
from .serializers import OrganismoSerializer, DependenciaSerializer
class OrganismoViewSets(viewsets.ReadOnlyModelViewSet):
queryset = Organismo.objects.all().order_by('id')
serializer_class = OrganismoSerializer
permission_classes = [IsAuthenticated, ]
lookup_field = 'id'
class DependenciaViewSets(viewsets.ReadOnlyModelViewSet):
queryset = Dependencia.objects.all().order_by('id')
serializer_class = DependenciaSerializer
permission_classes = [IsAuthenticated, ]
lookup_field = 'id'
... ...
from django.apps import AppConfig
class OrganismoConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'organismo'
... ...
# Generated by Django 4.2.9 on 2024-09-30 15:44
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Organismo',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('short_name', models.CharField(max_length=350, verbose_name='Nombre del organismo')),
],
options={
'verbose_name': 'Organismo',
'verbose_name_plural': 'Organismos',
},
),
migrations.CreateModel(
name='Dependencia',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('short_name', models.CharField(max_length=350, verbose_name='Nombre de la dependencia')),
('organismo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organismo.organismo')),
],
options={
'verbose_name': 'Dependencia',
'verbose_name_plural': 'Dependencias',
},
),
]
... ...
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.dispatch import receiver
# Create your models here.
class Organismo(models.Model):
class Meta:
verbose_name = 'Organismo'
verbose_name_plural = 'Organismos'
short_name = models.CharField(max_length=350, blank=False, verbose_name='Nombre del organismo')
def __str__(self):
return f'{self.short_name}'
class Dependencia(models.Model):
objects = None
class Meta:
verbose_name = 'Dependencia'
verbose_name_plural = 'Dependencias'
organismo = models.ForeignKey('Organismo', on_delete=models.CASCADE)
short_name = models.CharField(max_length=350, blank=False, verbose_name='Nombre de la dependencia')
def __str__(self):
return f'{self.organismo} - {self.short_name}'
def custom_permission_create(instance, model_class):
content_type = ContentType.objects.get_for_model(model_class)
action = 'view'
codename = f'{action}_{instance.short_name.lower().replace(" ", "_")}'
if not Permission.objects.filter(codename=codename, content_type=content_type).exists():
Permission.objects.create(
codename=codename,
name=f'Can {action} {instance.short_name}',
content_type=content_type,
)
def custom_permission_delete(instance, model_class):
content_type = ContentType.objects.get_for_model(model_class)
action = 'view'
codename = f'{action}_{instance.short_name.lower().replace(" ", "_")}'
Permission.objects.filter(codename=codename, content_type=content_type).delete()
@receiver(post_save, sender=Dependencia)
@receiver(post_save, sender=Organismo)
def manage_permissions(sender, instance, created, **kwargs):
model_class = type(instance)
if created:
custom_permission_create(instance, model_class)
else:
old_instance = model_class.objects.get(pk=instance.pk)
if old_instance.short_name != instance.short_name:
custom_permission_delete(old_instance, model_class)
custom_permission_create(instance, model_class)
... ...
from rest_framework_json_api import serializers
from .models import Dependencia, Organismo
class OrganismoSerializer(serializers.ModelSerializer):
class Meta:
model = Organismo
fields = serializers.ALL_FIELDS
class DependenciaSerializer(serializers.ModelSerializer):
class Meta:
model = Dependencia
fields = serializers.ALL_FIELDS
\ No newline at end of file
... ...
from django.test import TestCase
# Create your tests here.
... ...
import factory
from factory import SubFactory, faker, django
from organismo.models import Organismo, Dependencia
class OrganimsoFactory(django.DjangoModelFactory):
class Meta:
model = Organismo
short_name = faker.Faker(provider='sentence', nb_words=30)
class DependenciaFactory(django.DjangoModelFactory):
class Meta:
model = Dependencia
organismo = SubFactory(factory=OrganimsoFactory)
short_name = faker.Faker(provider='sentence', nb_words=30)
\ No newline at end of file
... ...
import pytest
from rest_framework import status
from django.contrib.auth.models import User
from django.urls import reverse
from rest_framework.test import APIClient
from organismo.tests.factories import OrganimsoFactory, DependenciaFactory
@pytest.mark.django_db
def test_organismo_listado():
cliente = APIClient()
user = User.objects.create_user(username='admin', email='admin@example.com', password='password123')
cliente.force_authenticate(user=user)
OrganimsoFactory.create_batch(size=4)
endpoint = reverse('organismo-list')
response = cliente.get(path=endpoint)
assert response.status_code == status.HTTP_200_OK
@pytest.mark.django_db
def test_dependencia_listado():
cliente = APIClient()
user = User.objects.create_user(username='admin', email='admin@example.com', password='password123')
cliente.force_authenticate(user=user)
DependenciaFactory.create_batch(size=2)
endpoint = reverse('dependencia-list')
response = cliente.get(path=endpoint)
assert response.status_code == status.HTTP_200_OK
\ No newline at end of file
... ...
from django.shortcuts import render
# Create your views here.
... ...
from rest_framework import routers
from organismo import api as organismo_api
# Define routes
router = routers.DefaultRouter()
router.register(prefix='organismo', viewset=organismo_api.OrganismoViewSets)
router.register(prefix='dependencia', viewset=organismo_api.DependenciaViewSets)
\ No newline at end of file
... ...
... ... @@ -55,10 +55,14 @@ THIRD_PARTY_APPS = (
'rest_framework',
'django_filters',
'corsheaders',
'oauth2_provider',
'mozilla_django_oidc',
)
PROJECT_APPS = (
'core',
'organismo',
)
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + PROJECT_APPS
... ...
... ... @@ -14,8 +14,9 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.urls import path, include, reverse_lazy
from django.conf import settings
from django.views.generic.base import RedirectView
from .router import router
... ... @@ -24,6 +25,7 @@ admin.site.site_header = getattr(settings, 'PROJECT_NAME_HEADER')
admin.site.site_title = getattr(settings, 'PROJECT_NAME_TITLE')
urlpatterns = [
path('', RedirectView.as_view(url=reverse_lazy('admin:index'))),
path('admin/', admin.site.urls),
path('api/v1/', include(router.urls)),
]
... ...
[pytest]
DJANGO_SETTINGS_MODULE=PROJECTproject-NAME.settings.testing
DJANGO_SETTINGS_MODULE=project.settings.testing
norecursedirs = requirements deployment
testpaths = tests
testpaths = test
addopts = --capture=fd --nomigrations
... ...
... ... @@ -2,3 +2,4 @@
pytest==6.2.5
pytest-django==4.4.0
factory_boy==3.3.1
... ...