Realizar búsquedas con Django Rest Framework
¡Hola! Bienvenidos a esta cuarta parte del tutorial para crear una API con DRF. Para el tutorial de hoy vamos a implementar la búsqueda de curriculums además de la opción de poder listar todos los curriculums almacenados en la BBDD. Estos se mostrarán paginados por la configuración que añadimos en el tutorial anterior.
Los resultados de la búsqueda mostrarán toda la información relevante del usuario así como la experiencia, educación, proyectos y la educación extra oficial.
Lo primero que vamos a hacer es crear una carpeta llamada search en el directorio raíz, esta contendrá los siguientes archivos: __init__.py, views.py, urls.py y serializers.py.
Ahora vamos al archivo settings.py y añadimos la app como hemos hecho en otras ocasiones:
api_drf_curriculum/settings.py
INSTALLED_APPS = [
.
.
.
'search'
]
Una vez hecho esto debemos realizar una modificación en nuestros modelos para nombrar las relaciones con los usuarios, ya que necesitaremos el nombre más adelante. Para hacer esto, añadimos un nombre en related_name dentro de la clave foránea user.
Modelos
education/models.py
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='education')
experiences/models.py
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='experience')
extras/models.py
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='extra_education')
projects/models.py
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='projects')
Ahora que ya tenemos las relaciones nombradas vamos a crear un nuevo serializer, este se encargará de mostrar los datos que queramos del usuario. Para ello deberemos crear un serializer para cada modelo de nuestra base de datos.
search/serializers.py
# Django REST Framework
from rest_framework import serializers
# Models
from users.models import User
from experiences.models import Experience
from education.models import Education
from extras.models import Extra
from projects.models import Project
class ExperienceCurriculumSerializer(serializers.ModelSerializer):
"""Experience Curriculum Model Serializer"""
class Meta:
"""Meta class."""
model = Experience
fields = (
'date_ini',
'date_end',
'company',
'description',
)
class EducationCurriculumSerializer(serializers.ModelSerializer):
"""Education Curriculum Model Serializer"""
class Meta:
"""Meta class."""
model = Education
fields = (
'date_ini',
'date_end',
'title',
)
class ExtraCurriculumSerializer(serializers.ModelSerializer):
"""Extras Curriculum Model Serializer"""
class Meta:
"""Meta class."""
model = Extra
fields = (
'expedition',
'title',
'url',
'description',
)
class ProjectCurriculumSerializer(serializers.ModelSerializer):
"""Projects Curriculum Model Serializer"""
class Meta:
"""Meta class."""
model = Project
fields = (
'date',
'title',
'url',
'description',
)
Por último crearemos un serializer más que usará los creados anteriormente para mostrar todos los datos. Para usar los otros serializers, los creamos dentro del serializer principal y añadimos many = True, ya que queremos traer todos los datos de ese usuario. Después solo necesitamos añadirlo en el listado de fields.
class CurriculumSerializer(serializers.ModelSerializer):
experience = ExperienceCurriculumSerializer(many=True)
education = EducationCurriculumSerializer(many=True)
extra_education = ExtraCurriculumSerializer(many=True)
projects = ProjectCurriculumSerializer(many=True)
class Meta:
model = User
fields = (
'first_name',
'last_name',
'email',
'extract',
'phone',
'city',
'country',
'experience',
'education',
'extra_education',
'projects',
)
Ya que esta parte de la API solo la pueden ver los reclutadores, crearemos un nuevo permiso para que solo permita realizar las búsquedas a este tipo de usuario.
Permisos
users/permissions.py
class IsRecruiterUser(BasePermission):
"""Allow access to search curriculums."""
def has_permission(self, request, view):
try:
user = User.objects.get(
email=request.user,
is_recruiter=True
)
except User.DoesNotExist:
return False
return True
Para la vista extendermos de ListModelMixin para que nos ahorre todo el trabajo, utilizaremos un queryset para indicarle que solo queremos los usuarios activos y que no sean reclutadores, por último le diremos que la clase serializadora será CurriculumSerializer que es el serializer que creamos hace un momento.
Vistas
search/views.py
# Django REST Framework
from rest_framework import mixins, status, viewsets
from rest_framework.response import Response
# Permissions
from rest_framework.permissions import IsAuthenticated
# Models
from users.models import User
# Serializers
from search.serializers import CurriculumSerializer
# Permissions
from users.permissions import IsRecruiterUser
class SearchViewSet(mixins.ListModelMixin,
viewsets.GenericViewSet):
queryset = User.objects.filter(is_active=True, is_recruiter=False)
serializer_class = CurriculumSerializer
def get_permissions(self):
permission_classes = [IsAuthenticated, IsRecruiterUser]
return [permission() for permission in permission_classes]
Ahora como en los otros casos añadiremos las urls para poder acceder a esta sección de la API.
Urls
search/urls.py
"""Search URLs."""
# Django
from django.urls import include, path
# Django REST Framework
from rest_framework.routers import DefaultRouter
# Views
from search import views
router = DefaultRouter()
router.register(r'search', views.SearchViewSet, basename='search')
urlpatterns = [
path('', include(router.urls))
]
api_drf_curriculum/urls.py
"""Main URLs module."""
from django.conf import settings
from django.urls import include, path
from django.conf.urls.static import static
from django.contrib import admin
urlpatterns = [
# Django Admin
path('admin/', admin.site.urls),
path('', include(('users.urls', 'users'), namespace='users')),
path('', include(('experiences.urls', 'experience'), namespace='experience')),
path('', include(('education.urls', 'education'), namespace='education')),
path('', include(('projects.urls', 'projects'), namespace='projects')),
path('', include(('extras.urls', 'extras'), namespace='extras')),
path('', include(('search.urls', 'search'), namespace='search')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Y listo 💪, ahora ya podemos obtener el listado de usuarios con toda su información, para acceder solo tenemos que usar esta url http://localhost:8000/search/, pasar la autenticación y realizar una petición GET
Búsqueda con parámetros
Ahora ya tenemos el listado pero sería más interesante poder realizar búsquedas más exhaustivas, ya que cuando un reclutador utilice la búsqueda es probable que busque algo en concreto.
Para ellos vamos a nuestra vista e importamos la siguiente librería.
search/views.py
from rest_framework.filters import SearchFilter
Después dentro del viewset lo añadiremos como filter_backends y en search_fields añadiremos los campos por los que queremos realizar búsquedas.
class SearchViewSet(mixins.ListModelMixin,
viewsets.GenericViewSet):
filter_backends = (SearchFilter,)
queryset = User.objects.filter(is_active=True, is_recruiter=False)
serializer_class = CurriculumSerializer
search_fields = (
'extract',
'city',
'country',
'experience__company',
'experience__description',
'education__title',
'extra_education__description',
'projects__description',
)
Por defecto, este tipo de búsquedas serán como un LIKE en sql además de insensitive case (no va a diferenciar entre mayúsculas y minúsculas). El parámetro de búsqueda puede contener múltiples términos de búsqueda, que deben ser espacios en blanco y / o separados por comas. Si se utilizan varios términos de búsqueda, solo retornará los objetos que cumplan con esos términos. A parte de esto se pueden configurar búsquedas por expresiones regulares, que empiecen por un texto, que sean exactas y actualmente solo para mysql búsqueda del texto completo. Para ver más acerca de esto os dejo la documentación.
Con esto pensaba cerrar el tutorial, ya que creo que es una buena base pero he decidido realizar uno más en el que implementaremos los test unitarios para testar toda nuestra aplicación.
Espero que este post te ayude y como siempre, te recomiendo seguirme en Twitter para estar al tanto de los nuevo contenido. Ahora también puedes seguirme en Instagram donde estoy subiendo tips, tutoriales en vídeo e información sobre herramientas para developers.
Por último os dejo mi guía para aprender a trabajar con APIs donde explico todo el funcionamiento de una API, el protocolo HTTP y veremos como construir una API con arquitectura REST.
Nos leemos 👋.