One place for hosting & domains

      Información

      Cómo crear una aplicación web moderna para gestionar la información de clientes con Django y React on Ubuntu 18.04


      El autor seleccionó Open Sourcing Mental Illness Ltd para recibir una donación como parte del programa Write for Donations.

      Introducción

      Las personas utilizan diferentes tipos de dispositivos para conectarse a internet y navegar por la Web. Debido a esto, las aplicaciones deben ser accesibles desde varios lugares. Para los sitios web tradicionales, tener una IU receptiva suele ser suficiente, pero las aplicaciones más complejas suelen requerir el uso de otras técnicas y arquitecturas. Entre ellas se contempla tener aplicaciones back-end y front-end REST independientes que puedan implementarse como aplicaciones web para el cliente, aplicaciones web progresivas (PWA) o aplicaciones móviles nativas.

      Algunas herramientas que puede utilizar al crear aplicaciones más complejas incluyen:

      • React, un marco de trabajo de JavaScript que permite a los desarrolladores crear frontends web y nativos para sus backend de API REST.
      • Django, una estructura web de Python gratuita y de código abierto que sigue el patrón de arquitectura del software de modelo vista controlador (MVC).
      • Django REST framework, un conjunto de herramientas potentes y flexibles para el desarrollo de REST APIs en Django.

      En este tutorial, usted creará una aplicación web moderna con un backend REST API independiente y un frontend utilizando React, Django y Django REST Framework. Al utilizar React con Django, podrá beneficiarse de los últimos avances en el desarrollo de JavaScript y front-end. En lugar de crear una aplicación de Django que utilice un motor de plantillas incorporado, usted utilizará React como biblioteca de IU, aprovechando su enfoque virtual de Modelo de objetos de documentos (DOM), enfoque declarativo y componentes que rápidamente reproduzcan cambios en los datos.

      La aplicación web que usted creará almacena registros sobre clientes en una base de datos, y puede utilizarlo como punto de partida para una aplicación CRM. Cuando haya terminado, podrá crear, leer, actualizar y borrar registros utilizando una interfaz React de estilo Bootstrap 4.

      Requisitos previos

      Para completar este tutorial, necesitará lo siguiente:

      Paso 1: Creación de un entorno virtual de Python e instalación de dependencias

      En este paso, crearemos un entorno virtual e instalaremos las dependencias necesarias para nuestra aplicación, entre ellas, Django, Django REST framework y django-cors-headers.

      Nuestra aplicación utilizará dos servidores de desarrollo distintos para Django y React. Se ejecutarán en diferentes puertos y funcionarán como dos dominios separados. Debido a esto, debemos permitir el intercambio de recursos de origen cruzado (CORS) para enviar solicitudes HTTP desde React a Django sin que el navegador pueda bloquearlos.

      Navegue a su directorio principal y cree un entorno virtual utilizando el módulo venv Python 3:

      • cd ~
      • python3 -m venv ./env

      Active el entorno virtual creado utilizando “source:

      A continuación, instale las dependencias del proyecto con pip. Entre ellas se incluyen:

      • Django: el marco de trabajo web para el proyecto.
      • Django REST framework: Una aplicación externa que desarrolla REST APIs con Django.
      • django-cors-headers: un paquete que habilita CORS.

      Instalar la estructura de Django:

      • pip install django djangorestframework django-cors-headers

      Con las dependencias del proyecto instaladas, puede crear el proyecto Django y el frontend de React.

      Paso 2: Creación del proyecto Django

      En este paso, generaremos el proyecto Django utilizando las siguientes comandos y utilidades:

      • **django-admin startproject project-name**:django-admin es una utilidad de línea de comandos que se utiliza para realizar tareas con Django. El comando startproject crea un nuevo proyecto Django.

      • **python manage.py startapp myapp**: manage.py es un script de utilidad que se añade automáticamente a cada proyecto de Django, y que ejecuta varias tareas administrativas, como crear nuevas aplicaciones, migrar la base de datos y servir de forma local el proyecto de Django. Su comando startapp crea una aplicación de Django dentro del proyecto Django. En Django, el término aplicación describe un paquete de Python que proporciona algunas características en un proyecto.

      Para comenzar, cree el proyecto Django con django-admin startproject. Hemos de nombrar a nuestro proyecto djangoreactproject

      • django-admin startproject djangoreactproject

      Antes de seguir, observemos la estructura del directorio de nuestro proyecto Django utilizando el comando tree.

      Nota: tree es un comando útil para visualizar estructuras de archivos y directorio desde la línea de comandos. Puede instalarlo con el comando que se indica a continuación:

      • sudo apt-get install tree

      Para utilizarlo, use el comando cd en el directorio que desee y escriba tree o proporcione la ruta al punto de partida con tree /home/sammy/sammys-project.

      Navegue a la carpeta djangoreactproject dentro del root de su proyecto y ejecute el comando tree:

      • cd ~/djangoreactproject
      • tree

      Verá lo siguiente:

      Output

      ├── djangoreactproject │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── manage.py

      La carpeta ~/djangoreactproject es la root del proyecto. Dentro de esta carpeta, hay varios archivos que serán importantes para su labor:

      • manage.py: el script de utilidad que realiza diversas tareas administrativas.
      • settings.py: el archivo de configuración principal para el proyecto de Django, donde puede modificar la configuración del proyecto. Estos ajustes incluyen variables como INSTALLED_APPS, una lista de strings que designan las aplicaciones habilitadas para su proyecto. La documentación de Django contiene más información sobre los ajustes disponibles.
      • urls.py: este archivo contiene una lista de patrones de URL y vistas relacionadas. Cada patrón traza una conexión entre una URL y la función que se debe solicitar para esa URL. Para más información sobre URL y vistas, consulte nuestro tutorial sobre Cómo crear vistas de Django.

      Nuestro primer paso en el trabajo con el proyecto será configurar los paquetes que instalamos en el paso anterior, incluidos Django REST framework y el paquete Django CORS, al añadirlos a settings.py. Abra el archivo con nano o su editor favorito:

      • nano ~/djangoreactproject/djangoreactproject/settings.py

      Navegue al ajuste INSTALLED_APPS y añada las aplicaciones rest_framework y corsheaders en la parte inferior de la lista:

      ~/djangoreactproject/djangoreactproject/settings.py

      ...
      INSTALLED_APPS = [
          'django.contrib.admin',
          'django.contrib.auth',
          'django.contrib.contenttypes',
          'django.contrib.sessions',
          'django.contrib.messages',
          'django.contrib.staticfiles',
          'rest_framework',
          'corsheaders'
      ]
      

      A continuación, añada el middleware corsheaders.middleware.CorsMiddleware desde el paquete CORS instalado previamente al ajuste MIDDLEWARE. Este ajuste es una lista de middlewares, una clase de Python que contiene código que se procesa cada vez que su aplicación web gestiona una solicitud o respuesta:

      ~/djangoreactproject/djangoreactproject/settings.py

      ...
      
      MIDDLEWARE = [
      ...
      'django.contrib.messages.middleware.MessageMiddleware',
      'django.middleware.clickjacking.XFrameOptionsMiddleware',
      'corsheaders.middleware.CorsMiddleware'
      ]
      

      A continuación, puede habilitar CORS. El ajuste CORS_ORIGIN_ALLOW_ALL especifica si desea habilitar CORS para todos los dominios o no, y CORS_ORIGIN_WHITELIST es una tupla de Python que contiene URL permitidas. En nuestro caso, dado que el servidor de desarrollo de React se ejecutará en http://localhost:3000, añadiremos nuevos ajustes CORS_ORIGIN_ALLOW_ALL = False y CORS_ORIGIN_WHITELIST('localhost:3000',) a nuestro archivo settings.py. Añade estos ajustes en cualquier parte del archivo:

      ~/djangoreactproject/djangoreactproject/settings.py

      
      ...
      CORS_ORIGIN_ALLOW_ALL = False
      
      CORS_ORIGIN_WHITELIST = (
             'localhost:3000',
      )
      ...
      

      Puede encontrar más opciones de configuración en la documentación de django-cors-headers.

      Guarde el archivo y salga del editor cuando haya terminado.

      Aún en el directorio ~/djangoreactproject, cree una nueva aplicación de Django llamada customers:

      • python manage.py startapp customers

      Contendrá los modelos y las vistas para gestionar clientes. Los modelos definen los campos y los comportamientos de nuestros datos de aplicaciones, mientras que las vistas permiten a nuestras aplicaciones gestionar adecuadamente las solicitudes web y devolver las respuestas requeridas.

      A continuación, añada esta aplicación a la lista de aplicaciones instaladas en el archivo settings.py de su proyecto para que Django la reconozca como parte del proyecto. Abra settings.py de nuevo:

      • nano ~/djangoreactproject/djangoreactproject/settings.py

      Añada la aplicación customers:

      ~/djangoreactproject/djangoreactproject/settings.py

      ...
      INSTALLED_APPS = [
          ...
          'rest_framework',
          'corsheaders',
          'customers'
      ]
      ...
      

      A continuación, *migre *la base de datos e inicie el servidor de desarrollo local. Las migraciones son la manera en que Django propaga los cambios que usted realiza a sus modelos en su esquema de base de datos. Estos cambios pueden ser, por ejemplo, añadir un campo o eliminar un modelo. Para obtener más información sobre modelos y migraciones, consulte Cómo crear modelos de Django.

      Migre la base de datos:

      Inicie el servidor de desarrollo local:

      • python manage.py runserver

      El resultado debe ser similar a lo siguiente:

      Output

      Performing system checks... System check identified no issues (0 silenced). October 22, 2018 - 15:14:50 Django version 2.1.2, using settings 'djangoreactproject.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.

      Su aplicación web se ejecuta desde http://127.0.0.1:8000. Si se dirige a esta dirección en su navegador web, debería ver la siguiente página:

      Página de demostración de Django

      En este momento, deje la aplicación en ejecución y abra una nueva terminal para seguir desarrollando el proyecto.

      Paso 3: creación del frontend de React

      En esta sección, vamos a crear la aplicación frontend de nuestro proyecto utilizando React.

      React tiene una herramienta oficial que le permite generar proyectos de React de forma rápida sin tener que configurar Webpack directamente. Webpack es un empaquetador de módulos que se utiliza para agrupar recursos web, como código de JavaScript, CSS e imágenes. Normalmente, para poder utilizar Webpack, debe establecer varias opciones de configuración, pero, gracias a la herramienta create-react-app, no tiene que lidiar con Webpack directamente hasta que decida que necesita más control. Para ejecutar create-react-app puede utilizar npx, una herramienta que ejecuta binarios de paquetes npm.

      En su segunda terminal, asegúrese de que estar en el directorio de su proyecto:

      Cree un proyecto de React llamado frontend utilizando create-react-app y npx

      • npx create-react-app frontend

      A continuación, navegue al interior de su aplicación de React e inicie el servidor de desarrollo:

      • cd ~/djangoreactproject/frontend
      • npm start

      Su aplicación se ejecutará desde http://localhost:3000/:

      Página de demostración de React

      Deje el servidor de desarrollo de React en ejecución y abra otra ventana de terminal para proceder.

      Para ver la estructura del directorio de todo el proyecto en este punto, navegue a la carpeta root y ejecute el comando tree de nuevo:

      • cd ~/djangoreactproject
      • tree

      Verá una estructura como esta:

      Output

      ├── customers │ ├── admin.py │ ├── apps.py │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── djangoreactproject │ ├── __init__.py │ ├── __pycache__ │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── frontend │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ ├── README.md │ ├── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ └── registerServiceWorker.js │ └── yarn.lock └── manage.py

      Nuestra aplicación utilizará Bootstrap 4 para dar forma a la interfaz de React, por lo que lo incluiremos en el archivo frontend/src/App.css que gestiona nuestros ajustes de CSS. Abra el archivo:

      • nano ~/djangoreactproject/frontend/src/App.css

      Añada la importación que se indica a continuación al comienzo del archivo. Puede eliminar el contenido existente del archivo, pero no es necesario hacerlo:

      ~/djangoreactproject/frontend/src/App.css

      @import  'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css';
      

      Aquí, @import es una instrucción de CSS que se utiliza para importar reglas de estilo de otras hojas de estilo.

      Ahora que hemos creado las aplicaciones back-end y front-end, vamos a crear el modelo de cliente y algunos datos de prueba.

      Paso 4: creación del modelo de cliente y datos iniciales

      Tras crear la aplicación de Django y el frontend de React, nuestro próximo paso será crear el modelo de cliente, que representa la tabla de base de datos que almacenará información sobre los clientes. No necesita nada de SQL, dado que el mapeo objeto-relacional (ORM) de Django se encargará de las operaciones de la base de datos al asignar las clases y variables de Python a tablas y columnas de SQL. De esta manera, el ORM de Django extrae las interacciones de SQL con la base de datos a través de una interfaz de Python.

      Active su entorno virtual de nuevo:

      • cd ~
      • source env/bin/activate

      Diríjase al directorio customers y abra models.py, un archivo de Python que contiene los modelos de su aplicación:

      • cd ~/djangoreactproject/customers/
      • nano models.py

      El archivo incluirá el siguiente contenido:

      ~/djangoreactproject/customers/models.py

      from django.db import models
      # Create your models here.
      

      La API del modelo de cliente ya está importada en el archivo gracias a la instrucción import from django.db import models. Ahora, añadirá la clase Customer, que extiende models.Model. Cada modelo de Django es una clase de Python que extiende django.db.models.Model.

      El modelo Customer tendrá estos campos de base de datos:

      • first_name: el nombre del cliente.
      • last_name: el apellido del cliente.
      • email: la dirección de correo electrónico del cliente.
      • phone: el número de teléfono del cliente.
      • address: la dirección del cliente.
      • description: la descripción del cliente.
      • createdAt: la fecha en que se añade el cliente.

      También añadiremos la función __str__(), que define la manera en que se mostrará el modelo. En nuestro caso, será con el nombre del cliente. Para obtener más información sobre la creación de clases y la definición de objetos, consulte Cómo crear clases y definir objetos en Python 3.

      Añada el código siguiente al archivo:

      ~/djangoreactproject/customers/models.py

      from django.db import models
      
      class Customer(models.Model):
          first_name = models.CharField("First name", max_length=255)
          last_name = models.CharField("Last name", max_length=255)
          email = models.EmailField()
          phone = models.CharField(max_length=20)
          address =  models.TextField(blank=True, null=True)
          description = models.TextField(blank=True, null=True)
          createdAt = models.DateTimeField("Created At", auto_now_add=True)
      
          def __str__(self):
              return self.first_name
      

      A continuación, migre la base de datos para crear las tablas de la base de datos. El comando makemigrations crea los archivos de migración en los que se añadirán los cambios al modelo, y migrate aplica los cambios en los archivos de migraciones a la base de datos.

      Vuelva a navegar a la carpeta root del proyecto:

      Ejecute lo siguiente para crear los archivos de migración:

      • python manage.py makemigrations

      Verá algo similar a esto:

      Output

      customers/migrations/0001_initial.py - Create model Customer

      Aplique estos cambios en la base de datos:

      Verá un resultado que indica que la migración se realizó correctamente:

      Output

      Operations to perform: Apply all migrations: admin, auth, contenttypes, customers, sessions Running migrations: Applying customers.0001_initial... OK

      A continuación, utilizará un archivo de migración de datos para crear datos iniciales de clientes. Un archivo de migración de datos es una migración que añade o altera datos en la base de datos. Cree un archivo de migración de datos vacío para la aplicación customers:

      • python manage.py makemigrations --empty --name customers customers

      Visualizará la siguiente confirmación con el nombre de su archivo de migración:

      Output

      Migrations for 'customers': customers/migrations/0002_customers.py

      Tenga en cuenta que el nombre de su archivo de migración es 0002_customers.py.

      A continuación, navegue al interior de la carpeta de migraciones de la aplicación customers:

      • cd ~/djangoreactproject/customers/migrations

      Abra el archivo de migración creado:

      Este es el contenido inicial del archivo:

      ~/djangoreactproject/customers/migrations/0002_customers.py

      from django.db import migrations
      
      class Migration(migrations.Migration):
          dependencies = [
              ('customers', '0001_initial'),
          ]
          operations = [
          ]        
      

      La instrucción import importa la API de migraciones, una API de Django para crear migraciones, desde django.db, un paquete incorporado que contiene clases para trabajar con bases de datos.

      La clase Migration es una clase de Python que describe las operaciones que se ejecutan al migrar bases de datos. Esta clase extiende migrations.Migration y tiene dos listas:

      • dependencies: contiene las migraciones dependientes.
      • operations: contiene las operaciones que se ejecutarán al aplicar la migración.

      A continuación, añada un método para crear datos de clientes de prueba. Añada el método que se indica a continuación antes de la definición de la clase Migration:

      ~/djangoreactproject/customers/migrations/0002_customers.py

      ...
      def create_data(apps, schema_editor):
          Customer = apps.get_model('customers', 'Customer')
          Customer(first_name="Customer 001", last_name="Customer 001", email="[email protected]", phone="00000000", address="Customer 000 Address", description= "Customer 001 description").save()
      
      ...
      

      En este método, estamos tomando la clase Customer de nuestra aplicación customers y creando un cliente de prueba para insertar en la base de datos.

      Para obtener la clase Customer, que permitirá la creación de nuevos clientes, usamos el método get_model() del objeto apps. El objeto apps representa el registro de aplicaciones instaladas y sus modelos de base de datos.

      El objeto apps se pasará del método RunPython() cuando lo usemos para ejecutar create_data(). Añada el método migrations.RunPython() a la lista operations vacía:

      ~/djangoreactproject/customers/migrations/0002_customers.py

      
      ...
          operations = [
              migrations.RunPython(create_data),
          ]  
      

      RunPython() es parte de la API de Migrations que le permite ejecutar código de Python personalizado en una migración. Nuestra lista operations especifica que este método se ejecutará al aplicar la migración.

      Este es el archivo completo:

      ~/djangoreactproject/customers/migrations/0002_customers.py

      from django.db import migrations
      
      def create_data(apps, schema_editor):
          Customer = apps.get_model('customers', 'Customer')
          Customer(first_name="Customer 001", last_name="Customer 001", email="[email protected]", phone="00000000", address="Customer 000 Address", description= "Customer 001 description").save()
      
      class Migration(migrations.Migration):
          dependencies = [
              ('customers', '0001_initial'),
          ]
          operations = [
              migrations.RunPython(create_data),
          ]        
      

      Para obtener más información sobre migraciones de datos, consulte la documentación sobre migraciones de datos en Django

      Para migrar su base de datos, primero, vuelva a navegar a la carpeta root de su proyecto:

      Migre su base de datos para crear los datos de prueba:

      Visualizará un resultado que confirma la migración:

      Output

      Operations to perform: Apply all migrations: admin, auth, contenttypes, customers, sessions Running migrations: Applying customers.0002_customers... OK

      Para obtener más detalles sobre este proceso, consulte Cómo crear modelos de Django.

      Con el modelo Customer y los datos de prueba creados, podemos pasar a la creación de la API REST.

      Paso 5: creación de la API REST

      En este paso, vamos a crear la API REST utilizando Django REST Framework. Crearemos varias vistas de API diferentes. Una vista de API es una función que gestiona una solicitud o llamada de API, mientras que un punto de final de API es una URL única que representa un punto de contacto con el sistema REST. Por ejemplo, cuando el usuario envía una solicitud de GET a un punto final de API, Django llama a la función o vista de API correspondiente para gestionar la solicitud y devolver los resultados posibles.

      También utilizaremos serializadores. Un serializador de Django REST Framework permite que instancias de modelos complejas y QuerySets se conviertan en formato JSON para el consumo de API. La clase de serialización también puede funcionar en dirección inversa, proporcionando mecanismos para el análisis y la deserialización de datos en QuerySets y modelos de Django.

      Nuestros puntos finales de API incluirán lo siguiente:

      • api/customers: este punto final se utiliza para crear clientes y devuelve conjuntos paginados de clientes.
      • api/clusters/<pk>: este punto final se utiliza para obtener, actualizar y eliminar clientes individuales por clave primaria o id.

      También crearemos URL en el archivo urls.py del proyecto para los puntos finales correspondientes (es decir, api/clusters y api/clusters/<pk>).

      Comencemos por crear la clase de serialización para nuestro modelo Customer.

      Incorporación de la clase de serialización

      Es necesario crear una clase de serialización para nuestro modelo Customer para transformar las instancias de cliente y QuerySets hacia y desde JSON. Para crear la clase de serialización, primero, cree un archivo serializer.py dentro de la aplicación customers:

      • cd ~/djangoreactproject/customers/
      • nano serializers.py

      Añada el código siguiente para importar el modelo Customer y la API de serializadores:

      ~/djangoreactproject/customers/serializers.py

      from rest_framework import serializers
      from .models import Customer
      

      A continuación, cree una clase de serialización que extienda serializers.ModelSerializer y especifique los campos que han de serializarse:

      ~/djangoreactproject/customers/serializers.py

      
      ...
      class CustomerSerializer(serializers.ModelSerializer):
      
          class Meta:
              model = Customer 
              fields = ('pk','first_name', 'last_name', 'email', 'phone','address','description')
      

      La clase Meta especifica qué campos y modelos se serializarán: pk, first_name, last_name, email, phone, address, description.

      Este es el contenido completo del archivo:

      ~/djangoreactproject/customers/serializers.py

      from rest_framework import serializers
      from .models import Customer
      
      class CustomerSerializer(serializers.ModelSerializer):
      
          class Meta:
              model = Customer 
              fields = ('pk','first_name', 'last_name', 'email', 'phone','address','description')
      

      Ahora que hemos creado nuestra clase de serialización, podemos añadir las vistas de API.

      Incorporación de vistas de API

      En esta sección, crearemos las vistas de API para nuestra aplicación, a las que llamará Django cuando el usuario visite el punto final correspondiente a la función de vista.

      Abra ~/djangoreactproject/customers/views.py:

      • nano ~/djangoreactproject/customers/views.py

      Elimine lo que haya allí y añada las siguientes importaciones:

      ~/djangoreactproject/customers/views.py

      from rest_framework.response import Response
      from rest_framework.decorators import api_view
      from rest_framework import status
      
      from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
      from .models import Customer 
      from .serializers import *
      

      Estamos importando el serializador que creamos, junto con el modelo Customer y las API de Django y Django REST Framework.

      A continuación, añada la vista para el procesamiento de POST y las solicitudes GET HTTP:

      ~/djangoreactproject/customers/views.py

      ...
      
      @api_view(['GET', 'POST'])
      def customers_list(request):
          """
       List  customers, or create a new customer.
       """
          if request.method == 'GET':
              data = []
              nextPage = 1
              previousPage = 1
              customers = Customer.objects.all()
              page = request.GET.get('page', 1)
              paginator = Paginator(customers, 10)
              try:
                  data = paginator.page(page)
              except PageNotAnInteger:
                  data = paginator.page(1)
              except EmptyPage:
                  data = paginator.page(paginator.num_pages)
      
              serializer = CustomerSerializer(data,context={'request': request} ,many=True)
              if data.has_next():
                  nextPage = data.next_page_number()
              if data.has_previous():
                  previousPage = data.previous_page_number()
      
              return Response({'data': serializer.data , 'count': paginator.count, 'numpages' : paginator.num_pages, 'nextlink': '/api/customers/?page=' + str(nextPage), 'prevlink': '/api/customers/?page=' + str(previousPage)})
      
          elif request.method == 'POST':
              serializer = CustomerSerializer(data=request.data)
              if serializer.is_valid():
                  serializer.save()
                  return Response(serializer.data, status=status.HTTP_201_CREATED)
              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
      

      Primero, usamos el decorador @api_view(['GET', 'POST']) para crear una vista de API que pueda aceptar solicitudes GET y POST. Un decorador es una función que toma otra función y la extiende de forma dinámica.

      En el cuerpo del método, usamos la variable request.method para verificar el método HTTP actual y ejecutar la lógica correspondiente dependiendo del tipo de solicitud:

      • Si se trata de una solicitud GET, el método pagina los datos utilizando Django Paginator y devuelve a la primera página de datos después de la serialización, el conteo de clientes disponibles, el número de páginas disponibles y los enlaces a las páginas anteriores y siguientes. Paginator es una clase integrada de Django que pagina una lista de datos en páginas y proporciona métodos para acceder a los elementos de cada una de ellas.
      • Si se trata de una solicitud POST, el método serializa los datos de clientes recibidos y, luego, llama al método save() del objeto serializador. A continuación, devuelve un objeto de Response, una instancia de HttpResponse, con el código de estado 201. Cada vista que crea se encarga de regresar un objeto HttpResponse. El método save() guarda los datos serializados en la base de datos.

      Para obtener más información sobre HttpResponse y las vistas, consulte esta discusión con respecto a la creación de funciones de vista.

      Ahora, añada la vista de API que se encargará del procesamiento de las solicitudes GET, PUT y DELETE para obtener, actualizar y eliminar clientes por pk (clave primaria):

      ~/djangoreactproject/customers/views.py

      
      ...
      @api_view(['GET', 'PUT', 'DELETE'])
      def customers_detail(request, pk):
       """
       Retrieve, update or delete a customer by id/pk.
       """
          try:
              customer = Customer.objects.get(pk=pk)
          except Customer.DoesNotExist:
              return Response(status=status.HTTP_404_NOT_FOUND)
      
          if request.method == 'GET':
              serializer = CustomerSerializer(customer,context={'request': request})
              return Response(serializer.data)
      
          elif request.method == 'PUT':
              serializer = CustomerSerializer(customer, data=request.data,context={'request': request})
              if serializer.is_valid():
                  serializer.save()
                  return Response(serializer.data)
              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
      
          elif request.method == 'DELETE':
              customer.delete()
              return Response(status=status.HTTP_204_NO_CONTENT)
      

      El método está representado con @api_view(['GET', 'PUT', 'DELETE']) para indicar que se trata de una vista de API que puede aceptar las solicitudes GET, PUT y DELETE.

      La marca de revisión en el campo request.method verifica el método de solicitud y, dependiendo de su valor, llama a la lógica correcta:

      • Si se trata de una solicitud GET, se serializan los datos de clientes y se envían utilizando un objeto Response.
      • Si se trata de una solicitud PUT, el método crea un serializador para nuevos datos de cliente y, luego, llama al método save() del objeto serializador creado. Por último, envía un objeto Response con el cliente actualizado.
      • Si se trata de una solicitud DELETE, el método llama al método delete() del objeto de cliente para eliminarlo y, a continuación, devuelve un objeto Response sin datos.

      El archivo completo se ve de este modo:

      ~/djangoreactproject/customers/views.py

      from rest_framework.response import Response
      from rest_framework.decorators import api_view
      from rest_framework import status
      
      from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
      from .models import Customer 
      from .serializers import *
      
      
      @api_view(['GET', 'POST'])
      def customers_list(request):
          """
       List  customers, or create a new customer.
       """
          if request.method == 'GET':
              data = []
              nextPage = 1
              previousPage = 1
              customers = Customer.objects.all()
              page = request.GET.get('page', 1)
              paginator = Paginator(customers, 5)
              try:
                  data = paginator.page(page)
              except PageNotAnInteger:
                  data = paginator.page(1)
              except EmptyPage:
                  data = paginator.page(paginator.num_pages)
      
              serializer = CustomerSerializer(data,context={'request': request} ,many=True)
              if data.has_next():
                  nextPage = data.next_page_number()
              if data.has_previous():
                  previousPage = data.previous_page_number()
      
              return Response({'data': serializer.data , 'count': paginator.count, 'numpages' : paginator.num_pages, 'nextlink': '/api/customers/?page=' + str(nextPage), 'prevlink': '/api/customers/?page=' + str(previousPage)})
      
          elif request.method == 'POST':
              serializer = CustomerSerializer(data=request.data)
              if serializer.is_valid():
                  serializer.save()
                  return Response(serializer.data, status=status.HTTP_201_CREATED)
              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
      
      @api_view(['GET', 'PUT', 'DELETE'])
      def customers_detail(request, pk):
          """
       Retrieve, update or delete a customer by id/pk.
       """
          try:
              customer = Customer.objects.get(pk=pk)
          except Customer.DoesNotExist:
              return Response(status=status.HTTP_404_NOT_FOUND)
      
          if request.method == 'GET':
              serializer = CustomerSerializer(customer,context={'request': request})
              return Response(serializer.data)
      
          elif request.method == 'PUT':
              serializer = CustomerSerializer(customer, data=request.data,context={'request': request})
              if serializer.is_valid():
                  serializer.save()
                  return Response(serializer.data)
              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
      
          elif request.method == 'DELETE':
              customer.delete()
              return Response(status=status.HTTP_204_NO_CONTENT)
      

      Ahora, podemos pasar a crear nuestros puntos finales.

      Incorporación de puntos finales de API

      Ahora, crearemos los puntos finales de API: api/customers/ para consultar y crear clientes, y api/customers/ <pk> para obtener, actualizar o eliminar clientes individuales por su pk.

      Abra ~/djangoreactproject/djangoreactproject/urls.py:

      • nano ~/djangoreactproject/djangoreactproject/urls.py

      Deje lo que haya allí, pero añada la importación a las vistas customers en la parte superior del archivo:

      ~/djangoreactproject/djangoreactproject/urls.py

      from django.contrib import admin
      from django.urls import path
      from customers import views
      from django.conf.urls import url
      

      A continuación, añada las URL de api/customers/ y api/customers/<pk> a la lista urlpatterns que contiene las URL de la aplicación:

      ~/djangoreactproject/djangoreactproject/urls.py

      ...
      
      urlpatterns = [
          path('admin/', admin.site.urls),
          url(r'^api/customers/$', views.customers_list),
          url(r'^api/customers/(?P<pk>[0-9]+)$', views.customers_detail),
      ]
      

      Con nuestros puntos finales REST creados, veamos cómo podemos consumirlos.

      Paso 6: consumo de API REST con Axios.

      En este paso, instalaremos Axios, el cliente HTTP que utilizaremos para realizar llamadas de API. También crearemos una clase para consumir los puntos finales de API que hemos creado.

      Primero, desactive su entorno virtual:

      A continuación, navegue a su carpeta frontend:

      • cd ~/djangoreactproject/frontend

      Instale axios desde npm utilizando:

      La opción --save añade la dependencia axios al archivo package.json de su aplicación.

      A continuación, cree un archivo de JavaScript denominado CustomersService.js, que contendrá el código para llamar a las API REST. Realizaremos esto dentro de la carpeta src, donde residirá el código de la aplicación de nuestro proyecto:

      • cd src
      • nano CustomersService.js

      Añada el código siguiente, que contiene métodos para conectarse a la API REST de Django:

      ~/djangoreactproject/frontend/src/CustomersService.js

      import axios from 'axios';
      const API_URL = 'http://localhost:8000';
      
      export default class CustomersService{
      
          constructor(){}
      
      
          getCustomers() {
              const url = `${API_URL}/api/customers/`;
              return axios.get(url).then(response => response.data);
          }  
          getCustomersByURL(link){
              const url = `${API_URL}${link}`;
              return axios.get(url).then(response => response.data);
          }
          getCustomer(pk) {
              const url = `${API_URL}/api/customers/${pk}`;
              return axios.get(url).then(response => response.data);
          }
          deleteCustomer(customer){
              const url = `${API_URL}/api/customers/${customer.pk}`;
              return axios.delete(url);
          }
          createCustomer(customer){
              const url = `${API_URL}/api/customers/`;
              return axios.post(url,customer);
          }
          updateCustomer(customer){
              const url = `${API_URL}/api/customers/${customer.pk}`;
              return axios.put(url,customer);
          }
      }
      

      La clase CustomersService llamará a los siguientes métodos de Axios:

      • getCustomers(): obtiene la primera página de clientes.
      • getCustomersByURL(): obtiene clientes por URL. Esto permite obtener las siguientes páginas de clientes al pasar enlaces como /api/customers/?page=2.
      • get Customer(): obtiene un cliente por clave primaria.
      • createCustomer(): crea un cliente.
      • updateCustomer(): actualiza un cliente.
      • deleteCustomer(): elimina un cliente.

      Ahora, podemos mostrar los datos de nuestra API en nuestra IU de React al crear un componente CustomersList.

      Paso 7: visualización de datos de la API en la aplicación de React

      En este paso, crearemos el componente CustomersList de React. Un componente de React representa una parte de la IU; también le permite dividir la IU en piezas independientes y reutilizables.

      Comience por crear CustomersList.js en frontend/src:

      • nano ~/djangoreactproject/frontend/src/CustomersList.js

      Inicie la importación de React y Component para crear un componente de React:

      ~/djangoreactproject/frontend/src/CustomersList.js

      import  React, { Component } from  'react';
      

      A continuación, importe e instancie el módulo CustomersService que creó en el paso anterior, el cual proporciona métodos que interactúan con el backend de API REST:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      import  CustomersService  from  './CustomersService';
      
      const  customersService  =  new  CustomersService();
      

      A continuación, cree un componente CustomersList que extienda Component para llamar a la API REST. Un componente de React debería extender o crear una subclase de la clase Component. Para obtener más información acerca de las clases E6 y herencia, consulte nuestro tutorial sobre Comprensión de las clases de JavaScript.

      Añada el siguiente código para crear un componente de React que extienda react.Component:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      class  CustomersList  extends  Component {
      
          constructor(props) {
              super(props);
              this.state  = {
                  customers: [],
                  nextPageURL:  ''
              };
              this.nextPage  =  this.nextPage.bind(this);
              this.handleDelete  =  this.handleDelete.bind(this);
          }
      }
      export  default  CustomersList;
      

      Dentro del constructor, estamos inicializando el objeto state. Este almacena las variables de estado de nuestro componente utilizando una matriz customers vacía. Esta matriz almacenará clientes y una nextPageURL que contendrá la URL de la siguiente página que se obtendrá del back-end de la API. También procedemos a vincular los métodos nextPage() y handleDelete() a this para que sean accesibles desde el código HTML.

      A continuación, añada el método componentDidMount() y una llamada a getCustomers() dentro de la clase CustomersList, antes de la llave de cierre.

      El método componentDidMount() es un método de ciclo de vida del componente que se llama al crear el componente y se inserta en el DOM. getCustomers() llama al objeto Customers Service para obtener la primera página de datos y el enlace de la siguiente página del backend de Django:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      componentDidMount() {
          var  self  =  this;
          customersService.getCustomers().then(function (result) {
              self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
          });
      }
      

      Ahora, añada el método handleDelete(), que gestiona la eliminación de un cliente, debajo de componentDidMount():

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      handleDelete(e,pk){
          var  self  =  this;
          customersService.deleteCustomer({pk :  pk}).then(()=>{
              var  newArr  =  self.state.customers.filter(function(obj) {
                  return  obj.pk  !==  pk;
              });
              self.setState({customers:  newArr})
          });
      }
      

      El método handleDelete() llama al método deleteCustomer() para eliminar un cliente utilizando su pk (clave primaria). Si la operación se realiza correctamente, la matriz customers se filtra por el cliente eliminado.

      A continuación, añada un método nextPage() para obtener los datos de la siguiente página y actualice el enlace de la página siguiente:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      nextPage(){
          var  self  =  this;
          customersService.getCustomersByURL(this.state.nextPageURL).then((result) => {
              self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
          });
      }
      

      El método nextPage() llama a un método getCustomersByURL(), que obtiene la URL de la página siguiente del objeto de estado, this.state.nextPageURL, y actualiza la matriz customers con los datos devueltos.

      Por último, añada el método render() del componente, que produce una tabla de clientes del estado de componente:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      render() {
      
          return (
          <div  className="customers--list">
              <table  className="table">
                  <thead  key="thead">
                  <tr>
                      <th>#</th>
                      <th>First Name</th>
                      <th>Last Name</th>
                      <th>Phone</th>
                      <th>Email</th>
                      <th>Address</th>
                      <th>Description</th>
                      <th>Actions</th>
                  </tr>
                  </thead>
                  <tbody>
                      {this.state.customers.map( c  =>
                      <tr  key={c.pk}>
                          <td>{c.pk}  </td>
                          <td>{c.first_name}</td>
                          <td>{c.last_name}</td>
                          <td>{c.phone}</td>
                          <td>{c.email}</td>
                          <td>{c.address}</td>
                          <td>{c.description}</td>
                          <td>
                          <button  onClick={(e)=>  this.handleDelete(e,c.pk) }> Delete</button>
                          <a  href={"/customer/" + c.pk}> Update</a>
                          </td>
                      </tr>)}
                  </tbody>
              </table>
              <button  className="btn btn-primary"  onClick=  {  this.nextPage  }>Next</button>
          </div>
          );
      }
      

      Este es el contenido completo del archivo:

      ~/djangoreactproject/frontend/src/CustomersList.js

      import  React, { Component } from  'react';
      import  CustomersService  from  './CustomersService';
      
      const  customersService  =  new  CustomersService();
      
      class  CustomersList  extends  Component {
      
      constructor(props) {
          super(props);
          this.state  = {
              customers: [],
              nextPageURL:  ''
          };
          this.nextPage  =  this.nextPage.bind(this);
          this.handleDelete  =  this.handleDelete.bind(this);
      }
      
      componentDidMount() {
          var  self  =  this;
          customersService.getCustomers().then(function (result) {
              console.log(result);
              self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
          });
      }
      handleDelete(e,pk){
          var  self  =  this;
          customersService.deleteCustomer({pk :  pk}).then(()=>{
              var  newArr  =  self.state.customers.filter(function(obj) {
                  return  obj.pk  !==  pk;
              });
      
              self.setState({customers:  newArr})
          });
      }
      
      nextPage(){
          var  self  =  this;
          console.log(this.state.nextPageURL);        
          customersService.getCustomersByURL(this.state.nextPageURL).then((result) => {
              self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
          });
      }
      render() {
      
          return (
              <div  className="customers--list">
                  <table  className="table">
                  <thead  key="thead">
                  <tr>
                      <th>#</th>
                      <th>First Name</th>
                      <th>Last Name</th>
                      <th>Phone</th>
                      <th>Email</th>
                      <th>Address</th>
                      <th>Description</th>
                      <th>Actions</th>
                  </tr>
                  </thead>
                  <tbody>
                  {this.state.customers.map( c  =>
                      <tr  key={c.pk}>
                      <td>{c.pk}  </td>
                      <td>{c.first_name}</td>
                      <td>{c.last_name}</td>
                      <td>{c.phone}</td>
                      <td>{c.email}</td>
                      <td>{c.address}</td>
                      <td>{c.description}</td>
                      <td>
                      <button  onClick={(e)=>  this.handleDelete(e,c.pk) }> Delete</button>
                      <a  href={"/customer/" + c.pk}> Update</a>
                      </td>
                  </tr>)}
                  </tbody>
                  </table>
                  <button  className="btn btn-primary"  onClick=  {  this.nextPage  }>Next</button>
              </div>
              );
        }
      }
      export  default  CustomersList;
      

      Ahora que hemos creado el componente CustomersList para visualizar la lista de clientes, podemos añadir el componente que gestiona la creación y la actualización de clientes.

      Paso 8: incorporación del componente de React de creación y actualización de clientes

      En este paso, crearemos el componente CustomerCreateUpdate, que se encargará de crear y actualizar clientes. Lo hará al proporcionar una forma que los usuarios puedan utilizar para ingresar datos sobre un cliente nuevo o actualizar una entrada existente.

      En frontend/src, cree un archivo CustomerCreateUpdate.js:

      • nano ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      Añada el siguiente código para crear un componente de React, importando React y Component:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      import  React, { Component } from  'react';
      

      A continuación, también podemos importar e instanciar la clase CustomersService que creamos en el paso anterior, que proporciona métodos que interactúan con el backend de API REST:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      ...
      import  CustomersService  from  './CustomersService';
      
      const  customersService  =  new  CustomersService();
      

      Luego, cree un componente CustomerCreateUpdate que extienda Component para crear y actualizar clientes:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      class  CustomerCreateUpdate  extends  Component {
      
          constructor(props) {
              super(props);
          }
      
      }
      export default CustomerCreateUpdate;
      

      Dentro de la definición de clase, añada el método render() del componente, que produce un formulario HTML que toma información sobre el cliente:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      render() {
              return (
                <form onSubmit={this.handleSubmit}>
                <div className="form-group">
                  <label>
                    First Name:</label>
                    <input className="form-control" type="text" ref='firstName' />
      
                  <label>
                    Last Name:</label>
                    <input className="form-control" type="text" ref='lastName'/>
      
                  <label>
                    Phone:</label>
                    <input className="form-control" type="text" ref='phone' />
      
                  <label>
                    Email:</label>
                    <input className="form-control" type="text" ref='email' />
      
                  <label>
                    Address:</label>
                    <input className="form-control" type="text" ref='address' />
      
                  <label>
                    Description:</label>
                    <textarea className="form-control" ref='description' ></textarea>
      
      
                  <input className="btn btn-primary" type="submit" value="Submit" />
                  </div>
                </form>
              );
        }
      

      Para cada elemento de entrada del formulario, el método añade una propiedad ref para acceder al valor del elemento del formulario y establecerlo.

      A continuación, por encima del método render(), defina un método handleSubmit(event) de modo que tenga la funcionalidad correcta cuando un usuario haga clic en el botón de enviar:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      handleSubmit(event) {
          const { match: { params } } =  this.props;
          if(params  &&  params.pk){
              this.handleUpdate(params.pk);
          }
          else
          {
              this.handleCreate();
          }
          event.preventDefault();
      }
      
      ...
      

      El método handleSubmit(event) gestiona el envío del formulario y, dependiendo de la ruta, llama al método handleUpdate(pk) para actualizar el cliente con la pk correcta, o el método handleCreate() para crear un nuevo cliente. Procederemos a definir estos métodos en breve.

      De nuevo en el constructor del componente, vincule el método handleSubmit() recientemente añadido a this para poder acceder a él en su formulario:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      ...
      class CustomerCreateUpdate extends Component {
      
      constructor(props) {
          super(props);
          this.handleSubmit = this.handleSubmit.bind(this);
      }
      ...
      

      A continuación, defina el método handleCreate() para crear un cliente a partir de los datos del formulario. Encima del método handleSubmit(event), añada el siguiente código:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      handleCreate(){
          customersService.createCustomer(
              {
              "first_name":  this.refs.firstName.value,
              "last_name":  this.refs.lastName.value,
              "email":  this.refs.email.value,
              "phone":  this.refs.phone.value,
              "address":  this.refs.address.value,
              "description":  this.refs.description.value
              }).then((result)=>{
                      alert("Customer created!");
              }).catch(()=>{
                      alert('There was an error! Please re-check your form.');
              });
      }
      
      ...
      

      El método handleCreate() se utilizará para crear un cliente a partir de los datos ingresados. Llama al método CustomersService.createCustomer() correspondiente que provoca que la API real llame al backend para crear un cliente.

      A continuación, por debajo del método handleCreate(), defina el método handleUpdate(pk) para implementar actualizaciones:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      handleUpdate(pk){
      customersService.updateCustomer(
          {
          "pk":  pk,
          "first_name":  this.refs.firstName.value,
          "last_name":  this.refs.lastName.value,
          "email":  this.refs.email.value,
          "phone":  this.refs.phone.value,
          "address":  this.refs.address.value,
          "description":  this.refs.description.value
          }
          ).then((result)=>{
      
              alert("Customer updated!");
          }).catch(()=>{
              alert('There was an error! Please re-check your form.');
          });
      }
      

      El método updateCustomer() actualizará un cliente por pk, utilizando la nueva información del formulario de información de clientes. Llama al método customersService.updateCustomer().

      A continuación, añada un método componentDidMount(). Si el usuario visita una ruta customer/:pk, queremos que el formulario se complete con información relacionada con el cliente utilizando la clave primaria de la URL. Para ello, podemos añadir el método getCustomer(pk) una vez que el componente se monte en el evento de ciclo de vida de componentDidMount(). Añada el siguiente código por debajo del constructor del componente para añadir este método:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      componentDidMount(){
          const { match: { params } } =  this.props;
          if(params  &&  params.pk)
          {
              customersService.getCustomer(params.pk).then((c)=>{
                  this.refs.firstName.value  =  c.first_name;
                  this.refs.lastName.value  =  c.last_name;
                  this.refs.email.value  =  c.email;
                  this.refs.phone.value  =  c.phone;
                  this.refs.address.value  =  c.address;
                  this.refs.description.value  =  c.description;
              })
          }
      }
      

      Este es el contenido completo del archivo:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      import React, { Component } from 'react';
      import CustomersService from './CustomersService';
      
      const customersService = new CustomersService();
      
      class CustomerCreateUpdate extends Component {
          constructor(props) {
              super(props);
      
              this.handleSubmit = this.handleSubmit.bind(this);
            }
      
            componentDidMount(){
              const { match: { params } } = this.props;
              if(params && params.pk)
              {
                customersService.getCustomer(params.pk).then((c)=>{
                  this.refs.firstName.value = c.first_name;
                  this.refs.lastName.value = c.last_name;
                  this.refs.email.value = c.email;
                  this.refs.phone.value = c.phone;
                  this.refs.address.value = c.address;
                  this.refs.description.value = c.description;
                })
              }
            }
      
            handleCreate(){
              customersService.createCustomer(
                {
                  "first_name": this.refs.firstName.value,
                  "last_name": this.refs.lastName.value,
                  "email": this.refs.email.value,
                  "phone": this.refs.phone.value,
                  "address": this.refs.address.value,
                  "description": this.refs.description.value
              }          
              ).then((result)=>{
                alert("Customer created!");
              }).catch(()=>{
                alert('There was an error! Please re-check your form.');
              });
            }
            handleUpdate(pk){
              customersService.updateCustomer(
                {
                  "pk": pk,
                  "first_name": this.refs.firstName.value,
                  "last_name": this.refs.lastName.value,
                  "email": this.refs.email.value,
                  "phone": this.refs.phone.value,
                  "address": this.refs.address.value,
                  "description": this.refs.description.value
              }          
              ).then((result)=>{
                console.log(result);
                alert("Customer updated!");
              }).catch(()=>{
                alert('There was an error! Please re-check your form.');
              });
            }
            handleSubmit(event) {
              const { match: { params } } = this.props;
      
              if(params && params.pk){
                this.handleUpdate(params.pk);
              }
              else
              {
                this.handleCreate();
              }
      
              event.preventDefault();
            }
      
            render() {
              return (
                <form onSubmit={this.handleSubmit}>
                <div className="form-group">
                  <label>
                    First Name:</label>
                    <input className="form-control" type="text" ref='firstName' />
      
                  <label>
                    Last Name:</label>
                    <input className="form-control" type="text" ref='lastName'/>
      
                  <label>
                    Phone:</label>
                    <input className="form-control" type="text" ref='phone' />
      
                  <label>
                    Email:</label>
                    <input className="form-control" type="text" ref='email' />
      
                  <label>
                    Address:</label>
                    <input className="form-control" type="text" ref='address' />
      
                  <label>
                    Description:</label>
                    <textarea className="form-control" ref='description' ></textarea>
      
      
                  <input className="btn btn-primary" type="submit" value="Submit" />
                  </div>
                </form>
              );
            }  
      }
      
      export default CustomerCreateUpdate;
      

      Con el componente CustomerCreateUpdate creado, podemos actualizar el componente principal App para añadir enlaces a los diferentes componentes que hemos creado.

      Paso 9: actualización del componente principal App

      En esta sección, actualizaremos el componente App de nuestra aplicación para crear enlaces a los componentes que hemos creado en los pasos anteriores.

      Desde la carpeta frontend, ejecute el siguiente comando para instalar React Router, que le permite añadir enrutamiento y navegación entre varios componentes de React:

      • cd ~/djangoreactproject/frontend
      • npm install --save react-router-dom

      A continuación, abra ~/djangoreactproject/frontend/src/App.js:

      • nano ~/djangoreactproject/frontend/src/App.js

      Elimine todo lo que haya allí y añada el siguiente código para importar las clases necesarias para incorporar enrutamiento. Estas incluyen BrowserRouter, que crea un componente Router, y Route, que crea un componente de ruta:

      ~/djangoreactproject/frontend/src/App.js

      import  React, { Component } from  'react';
      import { BrowserRouter } from  'react-router-dom'
      import { Route, Link } from  'react-router-dom'
      import  CustomersList  from  './CustomersList'
      import  CustomerCreateUpdate  from  './CustomerCreateUpdate'
      import  './App.css';
      

      BrowserRoutermantiene la IU sincronizada con la URL utilizando la ](https://developer.mozilla.org/en-US/docs/Web/API/History_API)API de historial HTML5[.

      A continuación, cree un diseño base que proporcione el componente básico que envolverá el componente BrowserRouter:

      ~/djangoreactproject/frontend/src/App.js

      ...
      
      const  BaseLayout  = () => (
      <div  className="container-fluid">
          <nav  className="navbar navbar-expand-lg navbar-light bg-light">
              <a  className="navbar-brand"  href="https://www.digitalocean.com/#">Django React Demo</a>
              <button  className="navbar-toggler"  type="button"  data-toggle="collapse"  data-target="#navbarNavAltMarkup"  aria-controls="navbarNavAltMarkup"  aria-expanded="false"  aria-label="Toggle navigation">
              <span  className="navbar-toggler-icon"></span>
          </button>
          <div  className="collapse navbar-collapse"  id="navbarNavAltMarkup">
              <div  className="navbar-nav">
                  <a  className="nav-item nav-link"  href="/">CUSTOMERS</a>
                  <a  className="nav-item nav-link"  href="http://www.digitalocean.com/customer">CREATE CUSTOMER</a>
              </div>
          </div>
          </nav>
          <div  className="content">
              <Route  path="/"  exact  component={CustomersList}  />
              <Route  path="/customer/:pk"  component={CustomerCreateUpdate}  />
              <Route  path="/customer/"  exact  component={CustomerCreateUpdate}  />
          </div>
      </div>
      )
      

      Utilizamos el componente Route para definir las rutas de nuestra aplicación; el componente que el enrutador debe cargar cuando se encuentra una coincidencia. Cada ruta requiere un path que especifique la ruta que se debe seguir y un component que indique el componente que se debe cargar. La propiedad exact le indica al enrutador que siga la trayectoria exacta.

      Por último, cree el componente App, el componente root o de nivel superior de nuestra aplicación de React:

      ~/djangoreactproject/frontend/src/App.js

      ...
      
      class  App  extends  Component {
      
      render() {
          return (
          <BrowserRouter>
              <BaseLayout/>
          </BrowserRouter>
          );
      }
      }
      export  default  App;
      

      Hemos envuelto el componente BaseLayout con el componente BrowserRouter, dado que nuestra aplicación está diseñada para ejecutarse en un navegador.

      El archivo completo se ve de este modo:

      ~/djangoreactproject/frontend/src/App.js

      import React, { Component } from 'react';
      import { BrowserRouter } from 'react-router-dom'
      import { Route, Link } from 'react-router-dom'
      
      import  CustomersList from './CustomersList'
      import  CustomerCreateUpdate  from './CustomerCreateUpdate'
      import './App.css';
      
      const BaseLayout = () => (
        <div className="container-fluid">
      <nav className="navbar navbar-expand-lg navbar-light bg-light">
        <a className="navbar-brand" href="https://www.digitalocean.com/#">Django React Demo</a>
        <button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
          <span className="navbar-toggler-icon"></span>
        </button>
        <div className="collapse navbar-collapse" id="navbarNavAltMarkup">
          <div className="navbar-nav">
            <a className="nav-item nav-link" href="/">CUSTOMERS</a>
            <a className="nav-item nav-link" href="http://www.digitalocean.com/customer">CREATE CUSTOMER</a>
      
          </div>
        </div>
      </nav>  
      
          <div className="content">
            <Route path="/" exact component={CustomersList} />
            <Route path="/customer/:pk"  component={CustomerCreateUpdate} />
            <Route path="/customer/" exact component={CustomerCreateUpdate} />
      
          </div>
      
        </div>
      )
      
      class App extends Component {
        render() {
          return (
            <BrowserRouter>
              <BaseLayout/>
            </BrowserRouter>
          );
        }
      }
      
      export default App;
      

      Ahora que añadimos enrutamiento a nuestra aplicación, estamos listos para probarla. Navegue a http://localhost:3000. Debería ver la primera página de la aplicación:

      Página de inicio de la aplicación

      Con la implementación de esta aplicación, ahora, cuenta con la base para una aplicación de CRM.

      Conclusión

      En este tutorial, usted creó una aplicación de prueba utilizando Django y React. Utilizó Django REST framework para crear la API REST, Axios para consumir la API y Bootstrap 4 para dar estilo a su CSS. Puede encontrar el código fuente de este proyecto en este repositorio de GitHub.

      En la configuración de este tutorial, se utilizaron aplicaciones de front-end y back-end. Para obtener un enfoque diferente de la integración de React con Django, consulte este tutorial y este otro tutorial.

      Para obtener más información sobre la creación de aplicaciones con Django, puede seguir la serie de desarrollo de Django. También puede consultar la documentación oficial de Django.



      Source link

      Información sobre clases de JavaScript


      Introducción

      JavaScript es un lenguaje basado en prototipos y cada objeto en él tiene una propiedad interna oculta llamada [[Prototype]] que puede usarse para extender propiedades y métodos de objetos. Puede obtener más información sobre los prototipos de nuestro tutorial Información sobre los prototipos y la herencia en JavaScript.

      Hasta hace poco, los desarrolladores esforzados usaban funciones constructoras para imitar un patrón de diseño orientado a objetos en JavaScript. La especificación de lenguaje ECMAScript 2015, a menudo denominada ES6, introdujo clases en el lenguaje JavaScript. Las clases de JavaScript no ofrecen realmente una funcionalidad adicional y a menudo se describen como elementos que aportan “azúcar sintáctico” en comparación con los prototipos y la herencia, ya que ofrecen una sintaxis más limpia y elegante. Debido a que otros lenguajes de programación usan clases, la sintaxis de clase de JavaScript permite que los desarrolladores alternen lenguajes de forma más directa.

      Las clases son funciones

      Una clase de JavaScript es un tipo de función. Las clases se declaran con la palabra clave class. Usaremos sintaxis de expresión de función para inicializar una sintaxis de expresión de función y clase para inicializar una clase.

      // Initializing a function with a function expression
      const x = function() {}
      
      // Initializing a class with a class expression
      const y = class {}
      

      Podemos acceder a [[Prototype]] de un objeto usando el métodoObject.getPrototypeOf(). Usaremos eso para probar la función vacía que creamos.

      Object.getPrototypeOf(x);
      

      Output

      ƒ () { [native code] }

      También podemos usar ese método en la clase que acabamos de crear.

      Object.getPrototypeOf(y);
      

      Output

      ƒ () { [native code] }

      El código declarado con function y class muestra una función [[Prototype]]. Con prototipos, cualquier función puede convertirse en una instancia de constructor mediane la palabra clave new.

      const x = function() {}
      
      // Initialize a constructor from a function
      const constructorFromFunction = new x();
      
      console.log(constructorFromFunction);
      

      Output

      x {} constructor: ƒ ()

      Esto se aplica a clases también.

      const y = class {}
      
      // Initialize a constructor from a class
      const constructorFromClass = new y();
      
      console.log(constructorFromClass);
      

      Output

      y {} constructor: class

      Estos ejemplos de constructores de prototipos, de lo contrario, están vacíos. Sin embargo, podemos ver que debajo de la sintaxis ambos métodos logran el mismo resultado final.

      Definir una clase

      En el tutorial de prototipos y herencia, confeccionamos un ejemplo basado en la creación de personajes en un juego de roles basado en texto. Continuaremos con ese ejemplo aquí para actualizar la sintaxis de funciones a clases.

      Una función constructora se inicializa con varios parámetros que se asignarían como propiedades de this, en alusión a la propia función. La primera letra del identificador sería mayúscula por convención.

      constructor.js

      // Initializing a constructor function
      function Hero(name, level) {
          this.name = name;
          this.level = level;
      }
      

      Cuando traducimos esto a la sintaxis de clase, que se muestra a continuación, vemos que su estructura es bastante similar.

      class.js

      // Initializing a class definition
      class Hero {
          constructor(name, level) {
              this.name = name;
              this.level = level;
          }
      }
      

      Sabemos que una función constructora está pensada para ser un esquema de objeto por el uso de mayúscula en la primera letra del inicializador (opcional) y a través de la familiaridad con la sintaxis. La palabra clave class comunica de una manera más directa el objetivo de nuestra función.

      La única diferencia en la sintaxis de la inicialización tiene que ver con el uso de la palabra clave class en lugar de function, y con la asignación de las propiedades dentro de un método constructor().

      Definir métodos

      La práctica común con funciones constructoras consiste en asignar métodos directamente a prototype en lugar de hacerlo con la initialización, como se aprecia en el método greet() a continuación.

      constructor.js

      function Hero(name, level) {
          this.name = name;
          this.level = level;
      }
      
      // Adding a method to the constructor
      Hero.prototype.greet = function() {
          return `${this.name} says hello.`;
      }
      

      Con clases, esta sintaxis se simplifica y el método puede agregarse directamente a las clases. Al usar la abreviatura de definición de método introducida en ES6, se logra una concisión aun mayor al definir un método.

      class.js

      class Hero {
          constructor(name, level) {
              this.name = name;
              this.level = level;
          }
      
          // Adding a method to the constructor
          greet() {
              return `${this.name} says hello.`;
          }
      }
      

      Observemos estas propiedades y métodos en acción. Crearemos una nueva instancia de Hero usando la palabra clave new y asignaremos algunos valores.

      const hero1 = new Hero('Varg', 1);
      

      Si imprimimos más información sobre nuestro nuevo objeto con console.log(hero1), podemos apreciar más detalles sobre lo que sucede con la inicialización de clase.

      Output

      Hero {name: "Varg", level: 1} __proto__: ▶ constructor: class Hero ▶ greet: ƒ greet()

      Podemos ver en el resultado que las funciones constructor() y greet() se aplicaron a __proto__, o [[Prototype]] de hero1, y no directamente como método en el objeto hero1. Aunque esto queda de manifiesto en la creación de funciones de constructor, no resulta evidente al crear clases. Las clases permiten una sintaxis más sencilla y sucinta, aunque sacrifican algo de claridad en el proceso.

      Ampliar una clase

      Una característica ventajosa de las funciones y clases constructoras es que pueden ampliarse a nuevos esquemas de objeto basados en el elemento principal. Esto impide la repetición de código para objetos similares, pero necesitan características adicionales o más específicas.

      Se pueden crear nuevas funciones constructoras desde el elemento principal usando el método call(). En el siguiente ejemplo, crearemos una clase de carácter más específica llamada Mage y le asignaremos las propiedades de Hero usando call(), además de agregar una propiedad.

      constructor.js

      // Creating a new constructor from the parent
      function Mage(name, level, spell) {
          // Chain constructor with call
          Hero.call(this, name, level);
      
          this.spell = spell;
      }
      

      En este punto, podemos crear una nueva instancia de Mage usando las mismas propiedades de Hero, así como también una nueva que agregamos.

      const hero2 = new Mage('Lejon', 2, 'Magic Missile');
      

      Al enviar hero2 a la consola, podemos apreciar que creamos una nueva clase Mage basada en la constructora.

      Output

      Mage {name: "Lejon", level: 2, spell: "Magic Missile"} __proto__: ▶ constructor: ƒ Mage(name, level, spell)

      Con clases de ES6, la palabra clave super se usa en lugar de call para acceder a las funciones principales. Usaremos extends para hacer referencia a la clase principal.

      class.js

      // Creating a new class from the parent
      class Mage extends Hero {
          constructor(name, level, spell) {
              // Chain constructor with super
              super(name, level);
      
              // Add a new property
              this.spell = spell;
          }
      }
      

      Ahora podemos crear una nueva instancia de Mage de la misma manera.

      const hero2 = new Mage('Lejon', 2, 'Magic Missile');
      

      Imprimiremos hero2 en la consola y veremos el resultado.

      Output

      Mage {name: "Lejon", level: 2, spell: "Magic Missile"} __proto__: Hero ▶ constructor: class Mage

      El resultado es casi exactamente el mismo, con la diferencia de que en la construcción de clase [[Prototype]] se vincula al elemento principal; en este caso, Hero.

      A continuación, se muestra una comparación en paralelo de todo el proceso de inicialización, incorporación de métodos y herencia de una función constructor y una clase.

      constructor.js

      function Hero(name, level) {
          this.name = name;
          this.level = level;
      }
      
      // Adding a method to the constructor
      Hero.prototype.greet = function() {
          return `${this.name} says hello.`;
      }
      
      // Creating a new constructor from the parent
      function Mage(name, level, spell) {
          // Chain constructor with call
          Hero.call(this, name, level);
      
          this.spell = spell;
      }
      

      class.js

      // Initializing a class
      class Hero {
          constructor(name, level) {
              this.name = name;
              this.level = level;
          }
      
          // Adding a method to the constructor
          greet() {
              return `${this.name} says hello.`;
          }
      }
      
      // Creating a new class from the parent
      class Mage extends Hero {
          constructor(name, level, spell) {
              // Chain constructor with super
              super(name, level);
      
              // Add a new property
              this.spell = spell;
          }
      }
      

      Aunque la sintaxis es bastante distinta, el resultado subyacente es casi el mismo para ambos métodos. Las clases nos ofrecen una alternativa más concisa para crear esquemas de objeto y las funciones constructor describen con mayor precisión lo que sucede en segundo plano.

      Conclusión

      En este tutorial, incorporó conocimientos sobre las similitudes y diferencias entre las funciones constructor de JavaScript y las clases de ES6. Tanto las clases como las funciones constructor imitan un modelo de herencia orientado hacia objetos para JavaScript, que es un lenguaje de herencia basado en prototipos.

      Comprender la herencia prototípica es primordial para desempeñarse con eficacia como desarrollador de JavaScript. Familiarizarse con las clases es extremadamente útil, ya que en las bibliotecas de JavaScript populares como React se usa con frecuencia la sintaxis class.



      Source link