logo cosasdedevs

Realizar búsquedas con Django Rest Framework

Realizar búsquedas con Django Rest Framework

My Profile
Jul 03, 2020

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

Dicho esto, como siempre avisaré por mi Twitter cuando esté listo así que si queréis estar al tanto de cuando sale el nuevo tutorial no olvidéis seguirme. También os agradecería que compartieseis este tutorial si os ayudado o si creéis que puede ayudar a otras personas.

Nos leemos 👋

244 vistas

Nos tomamos en serio tu privacidad

Utilizamos cookies propias y de terceros para mejorar la experiencia del usuario a través de su navegación. Si pulsas entendido aceptas su uso. Ver política de cookies.

🐍 Sígueme en Twitter

Si te gusta el contenido que subo y no quieres perderte nada, sígueme en Twitter y te avisaré cada vez que cree contenido nuevo 💪
Luego ¡Te sigo!