One place for hosting & domains

      Cómo acceder a las cámaras delantera y trasera con getUserMedia() de JavaScript


      Introducción

      Con HTML5 llegó la introducción de las API con acceso al hardware del dispositivo, incluyendo la API MediaDevices. Esta API proporciona acceso a dispositivos de entrada multimedia como audio y video.

      Con la ayuda de esta API, los desarrolladores pueden acceder a dispositivos de audio y video para transmitir y mostrar entradas de video en vivo en el navegador. En este tutorial, accederá a la entrada de video desde el dispositivo del usuario y la mostrará en el navegador usando el método getUserMedia:

      La API getUserMedia utiliza los dispositivos de entrada multimedia para producir un MediaStream. Este MediaStream contiene los tipos de multimedia solicitados, se trate de audio o video. Usando el flujo devuelto desde la API, las entradas de video pueden mostrarse en el navegador, lo cual es útil para la comunicación en tiempo real en el navegador.

      Cuando se utiliza junto con la API MediaStream Recording, puede grabar y guardar los datos multimedia capturados en el navegador. Esta API solo funciona en orígenes seguros como el resto de las APIs recién introducidas, pero también funciona en URLs localhost y archivo.

      Requisitos previos

      Este tutorial primero explicará los conceptos y demostrará ejemplos con Codepen. En el paso final, creará una entrada de video funcional para el navegador.

      Paso 1: Comprobar la compatibilidad del dispositivo

      Primero, verá cómo comprobar si el navegador del usuario es compatible con la API mediaDevices. Esta API existe en la interfaz navigator y contiene el estado actual y la identidad del agente del usuario. La comprobación se realiza con el siguiente código que puede pegarse en Codepen:

      if ('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) {
        console.log("Let's get this party started")
      }
      

      Primero, esto comprueba si la API mediaDevices existe en navigator y, luego, comprueba si la API getUserMedia está disponible en mediaDevices. Si esto devuelve true, puede comenzar.

      Paso 2: Solicitar el permiso del usuario

      Tras confirmar que el navegador es compatible con getUserMedia, debe solicitar permiso para usar los dispositivos de entrada multimedia en el agente del usuario. Normalmente, una vez que un usuario concede permiso, se devuelve una Promise que se resuelve a un flujo multimedia. Esta Promise no se devuelve cuando el usuario deniega el permiso, lo que bloquea el acceso a estos dispositivos.

      Pegue la siguiente línea en Codepen para solicitar permiso:

      navigator.mediaDevices.getUserMedia({video: true})
      

      El objeto proporcionado como argumento para el método getUserMedia se denomina constraints. Esto determina a qué dispositivos de entrada multimedia está solicitando permiso de acceso. Por ejemplo, si el objeto contiene audio: true, se pedirá al usuario que conceda acceso al dispositivo de entrada de audio.

      Esta sección cubrirá el concepto general de constraints. El objeto constraints es un objeto MediaScreamConstraints que especifica los tipos de multimedia a solicitar y los requisitos de cada uno de esos tipos. Puede especificar requisitos para el flujo solicitado usando el objeto constraints, como la resolución del flujo a usar (front, back).

      Debe especificar un audio o video cuando se realiza la solicitud. Un NotFoundError será devuelto si los tipos multimedia solicitados no pueden encontrarse en el navegador del usuario.

      Si desea solicitar un flujo de video con una resolución 1280 x 720, puede actualizar el objeto constraints para que tenga este aspecto:

      {
        video: {
          width: 1280,
          height: 720,
        }
      }
      

      Con esta actualización, el navegador intentará hacer coincidir los ajustes de calidad especificados para el flujo. Si el dispositivo de video no puede proporcionar la resolución, el navegador devolverá otras resoluciones disponibles.

      Para garantizar que el navegador devuelve una resolución que no sea inferior a la proporcionada, tendrá que usar la propiedad min. Aquí tiene cómo podrá actualizar el objeto constraints para incluir la propiedad min:

      {
        video: {
          width: {
            min: 1280,
          },
          height: {
            min: 720,
          }
        }
      }
      

      Esto garantizará que la resolución del flujo se devolvió con al menos 1280 x 720. Si no se puede cumplir este requisito mínimo, la promesa se rechazará con un OverconstrainedError.

      El algunos casos es posible que esté preocupado sobre guardar los datos y necesite que el flujo no supere una resolución establecida. Esto puede ser útil cuando el usuario esté en un plan limitado. Para habilitar esta funcionalidad, actualice el objeto constraints para que contenga el campo max:

      {
        video: {
          width: {
            min: 1280,
            max: 1920,
          },
          height: {
            min: 720,
            max: 1080
          }
        }
      }
      

      Con estos ajustes, el navegador garantizará que el flujo de retorno no está por debajo de 1280 x 720 y no supera 1920 x 1080.

      Otros términos que pueden usarse son exact e ideal. El ajuste ideal normalmente se utiliza junto con las propiedades min y max para buscar el mejor ajuste posible más cercano a los valores ideales proporcionados.

      Puede actualizar las restricciones para usar la palabra clave ideal:

      {
        video: {
          width: {
            min: 1280,
            ideal: 1920,
            max: 2560,
          },
          height: {
            min: 720,
            ideal: 1080,
            max: 1440
          }
        }
      }
      

      Para indicar al navegador que utilice la cámara frontal o trasera (en móvil) en los dispositivos, puede especificar una propiedad facingMode en el objeto video:

      {
        video: {
          width: {
            min: 1280,
            ideal: 1920,
            max: 2560,
          },
          height: {
            min: 720,
            ideal: 1080,
            max: 1440
          },
          facingMode: 'user'
        }
      }
      

      Este ajuste usará la cámara delantera siempre en todos los dispositivos. Para usar la cámara trasera en dispositivos móviles, puede alterar la propiedad facingMode a environment.

      {
        video: {
          ...
          facingMode: {
            exact: 'environment'
          }
        }
      }
      

      Paso 4: Usar el método enumerateDevices

      Cuando se invoca el método enumerateDevices, devuelve todos los dispositivos multimedia de entrada disponibles en la PC del usuario.

      Con el método, puede proporcionar las opciones del usuario sobre las cuales usar el dispositivo multimedia de entrada para transmitir contenido de audio o video. Este método devuelve una Promise resuelta a una matriz MediaDeviceInfo que contiene información sobre cada dispositivo.

      En el siguiente snippet se muestra un ejemplo de cómo usar este método:

      async function getDevices() {
        const devices = await navigator.mediaDevices.enumerateDevices();
      }
      

      Una respuesta de muestra para cada uno de los dispositivos tendría este aspecto:

      {
        deviceId: "23e77f76e308d9b56cad920fe36883f30239491b8952ae36603c650fd5d8fbgj",
        groupId: "e0be8445bd846722962662d91c9eb04ia624aa42c2ca7c8e876187d1db3a3875",
        kind: "audiooutput",
        label: "",
      }
      

      Nota: Una etiqueta no se devolverá a menos que exista un flujo disponible, o si el usuario ha concedido permisos de acceso al dispositivo.

      Paso 5: Mostrar el flujo de video en el navegador

      Ha repasado el proceso de solicitar y obtener acceso a los dispositivos multimedia, configurado las restricciones para incluir las resoluciones requeridas y seleccionado la cámara que necesitará para grabar video.

      Tras realizar estos pasos, al menos querrá ver si el flujo está funcionando según los ajustes configurados. Para garantizar esto, usará el elemento <video> para mostrar el flujo de video en el navegador.

      Como se ha mencionado antes, el método getUserMedia devuelve una Promesa que puede resolverse a un flujo. El flujo devuelto puede convertirse a una URL de objeto usando el método createObjectURL. Esta URL se establecerá como una fuente de video.

      Creará una demostración breve donde dejaremos al usuario elegir de su lista de dispositivos de video disponibles usando el método enumerateDevices.

      Este es un método navigator.mediaDevices. Enumera los dispositivos multimedia disponibles, como micrófonos y cámaras. Devuelve una Promesa que puede resolverse a una matriz de objetos que detalla los dispositivos multimedia disponibles.

      Cree un archivo index.html y actualice el contenido con el siguiente código.

      index.html

      <!doctype html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport"
                content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
          <meta http-equiv="X-UA-Compatible" content="ie=edge">
          <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
          <link rel="stylesheet" href="style.css">
          <title>Document</title>
      </head>
      <body>
      <div class="display-cover">
          <video autoplay></video>
          <canvas class="d-none"></canvas>
      
          <div class="video-options">
              <select name="" id="" class="custom-select">
                  <option value="">Select camera</option>
              </select>
          </div>
      
          <img class="screenshot-image d-none" alt="">
      
          <div class="controls">
              <button class="btn btn-danger play" title="Play"><i data-feather="play-circle"></i></button>
              <button class="btn btn-info pause d-none" title="Pause"><i data-feather="pause"></i></button>
              <button class="btn btn-outline-success screenshot d-none" title="ScreenShot"><i data-feather="image"></i></button>
          </div>
      </div>
      
      <script src="https://unpkg.com/feather-icons"></script>
      <script src="script.js"></script>
      </body>
      </html>
      

      En el anterior snippet, ha configurado los elementos que necesitará y un par de controles para el video. Además se ha incluido un botón para hacer capturas de pantalla de la entrada de video actual.

      Ahora, vamos a crear el estilo de estos componentes.

      Cree un archivo style.css y copie los siguientes estilos en él. Bootstrap se incluyó para reducir la cantidad de CSS que necesitará escribir para poner en marcha los componentes.

      style.css

      .screenshot-image {
          width: 150px;
          height: 90px;
          border-radius: 4px;
          border: 2px solid whitesmoke;
          box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1);
          position: absolute;
          bottom: 5px;
          left: 10px;
          background: white;
      }
      
      .display-cover {
          display: flex;
          justify-content: center;
          align-items: center;
          width: 70%;
          margin: 5% auto;
          position: relative;
      }
      
      video {
          width: 100%;
          background: rgba(0, 0, 0, 0.2);
      }
      
      .video-options {
          position: absolute;
          left: 20px;
          top: 30px;
      }
      
      .controls {
          position: absolute;
          right: 20px;
          top: 20px;
          display: flex;
      }
      
      .controls > button {
          width: 45px;
          height: 45px;
          text-align: center;
          border-radius: 100%;
          margin: 0 6px;
          background: transparent;
      }
      
      .controls > button:hover svg {
          color: white !important;
      }
      
      @media (min-width: 300px) and (max-width: 400px) {
          .controls {
              flex-direction: column;
          }
      
          .controls button {
              margin: 5px 0 !important;
          }
      }
      
      .controls > button > svg {
          height: 20px;
          width: 18px;
          text-align: center;
          margin: 0 auto;
          padding: 0;
      }
      
      .controls button:nth-child(1) {
          border: 2px solid #D2002E;
      }
      
      .controls button:nth-child(1) svg {
          color: #D2002E;
      }
      
      .controls button:nth-child(2) {
          border: 2px solid #008496;
      }
      
      .controls button:nth-child(2) svg {
          color: #008496;
      }
      
      .controls button:nth-child(3) {
          border: 2px solid #00B541;
      }
      
      .controls button:nth-child(3) svg {
          color: #00B541;
      }
      
      .controls > button {
          width: 45px;
          height: 45px;
          text-align: center;
          border-radius: 100%;
          margin: 0 6px;
          background: transparent;
      }
      
      .controls > button:hover svg {
          color: white;
      }
      

      El siguiente paso es añadir funcionalidad a la demostración. Con el método enumerateDevices, obtendrá los dispositivos de video disponibles y establecerlo como las opciones en el elemento seleccionado. Cree un archivo llamado script.js y actualícelo con el siguiente snippet:

      script.js

      feather.replace();
      
      const controls = document.querySelector('.controls');
      const cameraOptions = document.querySelector('.video-options>select');
      const video = document.querySelector('video');
      const canvas = document.querySelector('canvas');
      const screenshotImage = document.querySelector('img');
      const buttons = [...controls.querySelectorAll('button')];
      let streamStarted = false;
      
      const [play, pause, screenshot] = buttons;
      
      const constraints = {
        video: {
          width: {
            min: 1280,
            ideal: 1920,
            max: 2560,
          },
          height: {
            min: 720,
            ideal: 1080,
            max: 1440
          },
        }
      };
      
      const getCameraSelection = async () => {
        const devices = await navigator.mediaDevices.enumerateDevices();
        const videoDevices = devices.filter(device => device.kind === 'videoinput');
        const options = videoDevices.map(videoDevice => {
          return `<option value="${videoDevice.deviceId}">${videoDevice.label}</option>`;
        });
        cameraOptions.innerHTML = options.join('');
      };
      
      play.onclick = () => {
        if (streamStarted) {
          video.play();
          play.classList.add('d-none');
          pause.classList.remove('d-none');
          return;
        }
        if ('mediaDevices' in navigator && navigator.mediaDevices.getUserMedia) {
          const updatedConstraints = {
            ...constraints,
            deviceId: {
              exact: cameraOptions.value
            }
          };
          startStream(updatedConstraints);
        }
      };
      
      const startStream = async (constraints) => {
        const stream = await navigator.mediaDevices.getUserMedia(constraints);
        handleStream(stream);
      };
      
      const handleStream = (stream) => {
        video.srcObject = stream;
        play.classList.add('d-none');
        pause.classList.remove('d-none');
        screenshot.classList.remove('d-none');
        streamStarted = true;
      };
      
      getCameraSelection();
      

      En el snippet anterior, suceden un par de cuestiones. Vamos a verlas:

      1. feather.replace(): este método invoca las instancias feather, que es un icono configurado para el desarrollo web.
      2. La variable constraints tiene la configuración inicial del flujo. Esto se ampliará para incluir el dispositivo multimedia que elija el usuario.
      3. getCameraSelection: esta función invoca el método enumarateDevices. A continuación, filtrará a través de la matriz desde la Promesa resuelva y seleccionará los dispositivos de entrada de video. Desde los resultados filtrados, cree <option> para el elemento <select>.
      4. La invocación del método getUserMedia sucede en la escucha onclick del botón play. Aquí, comprobará si este método es compatible con el navegador del usuario que inicia el flujo.
      5. A continuación, invocará la función startStream que asume el argumento constraints. Invoca el método getUserMedia con las restrictions proporcionadas. handleStream se invoca usando el flujo de la Promesa resuelta. Este método establece el flujo devuelto al srcObject del elemento de video.

      A continuación, añadirá escuchas a los controles de botón en la página para pausar, detener y hacer capturas de pantalla. Además, añadirá una escucha al elemento <select> para actualizar las restricciones del flujo con el dispositivo de video seleccionado.

      Actualice el archivo script.js con el siguiente código:

      script.js

      ...
      cameraOptions.onchange = () => {
        const updatedConstraints = {
          ...constraints,
          deviceId: {
            exact: cameraOptions.value
          }
        };
        startStream(updatedConstraints);
      };
      
      const pauseStream = () => {
        video.pause();
        play.classList.remove('d-none');
        pause.classList.add('d-none');
      };
      
      const doScreenshot = () => {
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        canvas.getContext('2d').drawImage(video, 0, 0);
        screenshotImage.src = canvas.toDataURL('image/webp');
        screenshotImage.classList.remove('d-none');
      };
      
      pause.onclick = pauseStream;
      screenshot.onclick = doScreenshot;
      

      Ahora, cuando abra el archivo index.html en el navegador, al hacer clic en el botón Play se iniciará el flujo.

      Aquí está la demostración completa:

      Conclusión

      Este tutorial introdujo la API getUserMedia. Es una adición interesante a HTML5 que facilita el proceso de capturar multimedia en la web.

      La API asume un parámetro constraints que puede usarse para configurar el acceso a los dispositivos de entrada de audio y video. También puede usarse para especificar la resolución de video necesaria para su aplicación.

      Puede ampliar la demo para dar al usuario una opción de guardar las capturas de pantalla tomadas, y grabar y almacenar datos de video y audio con la ayuda de la API MediaStream Recording.



      Source link

      Información sobre las bases de datos relacionales


      Introducción

      Los sistemas de administración de bases de datos (DBMS) son programas que permiten a los usuarios interactuar con una base de datos. Les permiten controlar el acceso a una base de datos, escribir datos, ejecutar consultas y realizar otras tareas relacionadas con la administración de bases de datos.

      Sin embargo, para realizar cualquiera de estas tareas, los DBMS deben tener algún tipo de modelo subyacente que defina la organización de los datos. El modelo relacional es un enfoque para la organización de datos que se ha utilizado ampliamente en programas de software de bases de datos desde su concepción a fines de la década de 1960; a tal grado que, a la fecha de redacción de este artículo, cuatro de los cinco DBMS más populares son relacionales.

      Este artículo conceptual describe la historia del modelo relacional, la manera en que se organizan los datos en las bases de datos relacionales y cómo se utilizan hoy en día.

      Historia del modelo relacional

      Las bases de datos son conjuntos de información, o datos, modelados de forma lógica. Cualquier recopilación de datos es una base de datos, independientemente de cómo o dónde se almacene. Incluso un gabinete de archivos con información sobre nómina es una base de datos, al igual que una pila de formularios de pacientes hospitalizados o la recopilación de información sobre clientes de una empresa repartida en varias ubicaciones. Antes de que almacenar y administrar datos con computadoras se convirtiera en una práctica común, las bases de datos físicas como estas eran lo único con lo que contaban las organizaciones gubernamentales y empresariales que necesitaban almacenar información.

      A mediados del siglo XX, los desarrollos en las ciencias de la computación dieron lugar a máquinas con mayor capacidad de procesamiento y almacenamiento, tanto local como externo. Estos avances hicieron que los especialistas en ciencias de la computación comenzaran a reconocer el potencial que tenían estas máquinas para almacenar y administrar cantidades de datos cada vez más grandes.

      Sin embargo, no había teorías sobre cómo las computadoras podían organizar datos de manera significativa y lógica. Una cosa es almacenar datos no ordenados en una máquina, pero es mucho más complicado diseñar sistemas que permitan agregar, recuperar, clasificar y administrar esos datos de forma sistemática y práctica. La necesidad de contar con un marco de trabajo lógico para almacenar y organizar datos dio lugar a varias propuestas sobre cómo utilizar las computadoras para administrar datos.

      Uno de los primeros modelos de bases de datos fue el modelo jerárquico, en el que los datos se organizan con una estructura de árbol similar a la de los sistemas de archivos modernos. El siguiente ejemplo muestra el aspecto que podría tener el diseño de una parte de una base de datos jerárquica utilizada para categorizar animales:

      Ejemplo de base de datos jerárquica: categorización de animales

      El modelo jerárquico se implementó ampliamente en los primeros sistemas de administración de bases de datos, pero resultaba poco flexible. En este modelo, a pesar de que los registros individuales pueden tener varios “elementos secundarios”, cada uno puede tener un solo “elemento primario” en la jerarquía. Es por eso que estas primeras bases de datos jerárquicas se limitaban a representar relaciones “uno a uno” y “uno a varios”. Esta carencia de relaciones “varios a varios” podía provocar problemas al trabajar con puntos de datos que se deseaba asociar a más de un elemento primario.

      A fines de los años 60, Edgar F. Codd, un especialista en ciencias de la computación que trabajaba en IBM, diseñó el modelo relacional de administración de bases de datos. El modelo relacional de Codd permitía que los registros individuales se relacionaran con más de una tabla y, de esta manera, posibilitaba las relaciones “varios a varios” entre los puntos de datos además de las relaciones “uno a varios”. Esto proporcionó más flexibilidad que los demás modelos existentes a la hora de diseñar estructuras de base de datos y permitió que los sistemas de gestión de bases de datos relacionales (RDBMS) pudieran satisfacer una gama mucho más amplia de necesidades empresariales.

      Codd propuso un lenguaje para la administración de datos relacionales, conocido como Alfa, que influyó en el desarrollo de los lenguajes de bases de datos posteriores. Dos colegas de Codd en IBM, Donald Chamberlin y Raymond Boyce, crearon un lenguaje similar inspirado en Alpha. Lo llamaron SEQUEL, abreviatura de Structured English Query Language (Lenguaje de consulta estructurado en inglés), pero debido a una marca comercial existente, lo abreviaron a SQL (conocido formalmente como* Lenguaje de consulta estructurado*).

      Debido a las limitaciones de hardware, las primeras bases de datos relacionales eran prohibitivamente lentas y el uso de la tecnología tardó un tiempo en generalizarse. Pero a mediados de los años ochenta, el modelo relacional de Codd se había implementado en varios productos comerciales de administración de bases de datos, tanto de IBM como de sus competidores. Estos proveedores también siguieron el liderazgo de IBM al desarrollar e implementar sus propios dialectos de SQL. Para 1987, tanto el Instituto Nacional Estadounidense de Estándares (American National Standards Institute) como la Organización Internacional de Normalización (International Organization for Standardization) habían ratificado y publicado normas para SQL, lo que consolidó su estado como el lenguaje aceptado para la administración de RDBMS.

      Gracias al uso extendido del modelo relacional en varias industrias, se lo comenzó a reconocer como el modelo estándar para la administración de datos. Incluso con el surgimiento de varias bases de datos NoSQL en los últimos años, las bases de datos relacionales siguen siendo las herramientas predominantes para almacenar y organizar datos.

      Cómo organizan los datos las bases de datos relacionales

      Ahora que tiene una idea general de la historia del modelo relacional, vamos a analizar en detalle cómo organiza los datos.

      Los elementos más fundamentales del modelo relacional son las relaciones, que los usuarios y los RDBMS modernos reconocen como tablas. Una relación es un conjunto de tuplas, o filas de una tabla, en el que cada una de ellas comparte un conjunto de atributos o columnas:

      Diagrama de ejemplo de la asociación entre las relaciones, las tuplas y los atributos

      Una columna es la estructura de organización más pequeña de una base de datos relacional y representa las distintas facetas que definen los registros en la tabla. De ahí proviene su nombre más formal: atributos. Se puede pensar que cada tupla es como una instancia única de cualquier tipo de personas, objetos, eventos o asociaciones que contenga la tabla.  Estas instancias pueden ser desde empleados de una empresa y ventas de un negocio en línea hasta resultados de pruebas de laboratorio. Por ejemplo, en una tabla que contiene registros de los maestros de una escuela, las tuplas pueden tener atributos como name, subjects, start_date, etc.

      Al crear una columna, especificamos un* tipo de datos *que determina el tipo de entradas que se permiten en esa columna. Los sistemas de administración de bases de datos relacionales (RDBMS) suelen implementar sus propios tipos de datos únicos, que pueden no ser directamente intercambiables con tipos de datos similares de otros sistemas. Algunos tipos de datos frecuentes son fechas, cadenas, enteros y booleanos.

      En el modelo relacional, cada tabla contiene, por lo menos, una columna que puede utilizarse para identificar de forma única cada fila, lo que se denomina clave primaria. Esto es importante, dado que significa que los usuarios no necesitan saber dónde se almacenan físicamente los datos en una máquina; en su lugar, sus DBMS pueden realizar un seguimiento de cada registro y devolverlo según corresponda. A su vez, significa que los registros no tienen un orden lógico definido y que los usuarios tienen la capacidad de devolver sus datos en cualquier orden y a través de los filtros que deseen.

      Si tiene dos tablas que desea asociar, una manera de hacerlo es con una clave externa. Una clave externa es, básicamente, una copia de la clave primaria de una tabla (la tabla “primaria”) insertada en una columna de otra tabla (la “secundaria”). El siguiente ejemplo destaca la relación entre dos tablas: una se utiliza para registrar información sobre los empleados de una empresa y la otra, para realizar un seguimiento de las ventas de la empresa. En este ejemplo, la clave primaria de la tabla EMPLOYEES se utiliza como clave externa de la tabla SALES:

      Diagrama de ejemplo de cómo la clave primaria de la tabla EMPLOYEE actúa como la clave externa de la tabla SALES

      Si intenta agregar un registro a la tabla secundaria y el valor ingresado en la columna de la clave externa no existe en la clave primaria de la tabla primaria, la instrucción de inserción no será válida. Esto ayuda a mantener la integridad del nivel de las relaciones, dado que las filas de ambas tablas siempre se relacionarán correctamente.

      Los elementos estructurales del modelo relacional ayudan a mantener los datos almacenados de forma organizada, pero el almacenamiento de datos solo es útil si se pueden recuperar. Para obtener información de un RDBMS, puede emitir una consulta o una solicitud estructurada de un conjunto de información. Como se mencionó anteriormente, la mayoría de las bases de datos relacionales utilizan SQL para administrar y consultar datos. SQL permite filtrar y manipular los resultados de las consultas con una variedad de cláusulas, predicados y expresiones, lo que, a su vez, permite controlar correctamente qué datos se mostrarán en el conjunto de resultados.

      Ventajas y limitaciones de las bases de datos relacionales

      Teniendo en cuenta la estructura organizativa subyacente de las bases de datos relacionales, consideremos algunas de sus ventajas y desventajas.

      Hoy en día, tanto SQL como las bases de datos que lo implementan se desvían del modelo relacional de Codd de diversas maneras. Por ejemplo, el modelo de Codd determina que cada fila de una tabla debe ser única, mientras que, por cuestiones de practicidad, la mayoría de las bases de datos relacionales modernas permiten la duplicación de filas. Algunas personas no consideran que las bases de datos SQL sean verdaderas bases de datos relacionales a menos que cumplan con las especificaciones de Codd del modelo relacional. Sin embargo, en términos prácticos, es probable que cualquier DBMS que utilice SQL y, por lo menos, se adhiera en cierta medida al modelo relacional se denomine “sistema de administración de bases de datos relacionales”.

      A pesar de que las bases de datos relacionales ganaron popularidad rápidamente, algunas de las deficiencias del modelo relacional comenzaron a hacerse evidentes a medida que los datos se volvieron más valiosos y las empresas comenzaron a almacenar más de ellos. Entre otras cosas, puede ser difícil escalar una base de datos relacional de forma horizontal.  Los términos escalado horizontal o escala horizontal se refieren a la práctica de agregar más máquinas a una pila existente para extender la carga y permitir un mayor tráfico y un procesamiento más rápido. Se suele contrastar con el escalado vertical, que implica la actualización del hardware de un servidor existente, generalmente, añadiendo más RAM o CPU.

      El motivo por el cual es difícil escalar una base de datos relacional de forma horizontal está relacionado con el hecho de que el modelo relacional se diseñó para garantizar coherencia, lo que significa que los clientes que realizan consultas en la misma base de datos siempre obtendrán los mismos datos. Al querer escalar una base de datos relacional de forma horizontal en varias máquinas, resulta difícil asegurar la coherencia, dado que los clientes pueden escribir datos en un nodo pero no en otros.  Es probable que haya un retraso entre la escritura inicial y el momento en que se actualicen los demás nodos para reflejar los cambios, lo que daría lugar a inconsistencias entre ellos.

      Otra limitación que presentan los RDBMS es que el modelo relacional se diseñó para administrar datos estructurados o alineados con un tipo de datos predeterminado o, por lo menos, organizado de una manera predeterminada para que se puedan ordenar o buscar de forma más sencilla. Sin embargo, con la difusión de los equipos informáticos personales y el auge de Internet a principios de la década de 1990, los datos no estructurados, como los mensajes de correo electrónico, las fotos, los videos, etc., se volvieron más comunes.

      Nada de esto significa que las bases de datos relacionales no sean útiles. Al contrario, después de más de 40 años, el modelo relacional sigue siendo el marco dominante para la administración de datos. Su prevalencia y longevidad indican que las bases de datos relacionales son una tecnología madura, lo que constituye una de sus principales ventajas. Hay muchas aplicaciones diseñadas para trabajar con el modelo relacional, así como muchos administradores de bases de datos profesionales expertos en bases de datos relacionales. También hay una amplia variedad de recursos disponibles en formato impreso y digital para quienes desean comenzar a utilizar bases de datos relacionales.

      Otra ventaja de las bases de datos relacionales es que casi todos los RDBMS admiten transacciones. Las transacciones constan de una o más instrucciones SQL individuales que se ejecutan en secuencia como una unidad de trabajo única. Las transacciones tienen un enfoque de todo o nada, lo que significa que todas las instrucciones SQL de una transacción deben ser válidas; de lo contrario, falla en su totalidad. Esto es muy útil para garantizar la integridad de los datos al realizar cambios en varias filas o tablas.

      Por último, las bases de datos relacionales son sumamente flexibles. Se han utilizado para crear una amplia variedad de aplicaciones distintas y siguen funcionando de forma eficiente incluso con grandes cantidades de datos. SQL también es sumamente potente y permite agregar y cambiar datos sobre la marcha, así como alterar la estructura de los esquemas y las tablas de las bases de datos sin afectar a los datos existentes.

      Conclusión

      Gracias a su flexibilidad y diseño para la integridad de los datos, más de cincuenta años después de su concepción inicial, las bases de datos relacionales siguen siendo la principal forma de administrar y almacenar datos.  A pesar del surgimiento de diversas bases de datos NoSQL en los últimos años, la comprensión del modelo relacional y la manera de trabajar con RDBMS es fundamental para cualquier persona que desee crear aplicaciones que aprovechen el poder de los datos.

      Para obtener más información sobre RDBMS populares de código abierto, le recomendamos consultar nuestra comparación de diversas bases de datos relacionales SQL de código abierto. Si desea obtener más información sobre las bases de datos en general, le recomendamos consultar nuestra biblioteca completa de contenidos relacionados con bases de datos.



      Source link

      Cómo usar el módulo pathlib para manipular las rutas de sistemas de archivos en Python 3


      El autor seleccionó el COVID-19 Relief Fund para que reciba una donación como parte del programa Write for DOnations.

      Introducción

      Python 3 incluye el módulo pathlib para manipular rutas de sistemas de archivos de forma agnóstica en cualquier sistema operativo. El módulo pathlib es similar al os.path, pero pathlib ofrece una interfaz de nivel más alto, y, a menudo, más conveniente, que os.path.

      Podemos identificar archivos en una computadora con rutas jerárquicas. Por ejemplo, podemos identificar el archivo wave.txt en una computadora con esta ruta: /Users/sammy/ocean/wave.txt. Cada sistema operativo tiene una manera ligeramente distinta de representar rutas. Windows puede representar la ruta al archivo wave.txt de la siguiente manera: C:Userssammyoceanwave.txt.

      El módulo pathlib le puede resultar útil si desea a crear o mover archivos en el sistema de archivos de su programa de Python, enumerar los archivos del sistema de archivos que coincidan con una extensión o un patrón determinado o crear rutas de archivos apropiadas para el sistema operativo basadas en colecciones de cadenas sin procesar. Si bien es posible usar otras herramientas (como el módulo os.path) para realizar muchas de estas tareas, el módulo pathlib le permite realizar estas operaciones con un alto grado de legibilidad y una cantidad de código mínima.

      En este tutorial, revisaremos algunas de las maneras de usar el módulo pathlib para representar y manipular las rutas de los sistemas de archivos.

      Requisitos previos

      Para sacar el máximo provecho de este tutorial, se recomienda tener cierta familiaridad con la programación en Python 3. Puede consultar estos tutoriales para obtener la información de fondo necesaria:

      Cómo crear instancias Path

      El módulo pathlib proporciona varias clases, pero una de las más importantes es la clase Path. Las instancias de la clase Path representan una ruta a un archivo o un directorio en el sistema de archivos de nuestra computadora.

      Por ejemplo, el siguiente código inicia una instancia Path que representa una parte de la ruta a un archivo wave.txt:

      from pathlib import Path
      
      wave = Path("ocean", "wave.txt")
      print(wave)
      

      Si ejecutamos este código, obtendremos un resultado como el siguiente:

      Output

      ocean/wave.txt

      from pathlib import Path permite que la clase Path esté disponible en nuestro programa. Luego, Path("ocean", "wave.txt") crea una instancia de Path nueva. El resultado muestra que Python ha añadido el separador / adecuado del sistema operativo entre los dos componentes de la ruta que le proporcionamos: "ocean" y "wave.txt".

      Nota: Sus resultados pueden diferir ligeramente de los que se muestran como ejemplo en este tutorial en función del sistema operativo que utilice. Si utiliza Windows, por ejemplo, su resultado para este primer ejemplo puede tener este aspecto: oceanwave.txt.

      Actualmente, el objeto Path asignado a la variable wave contiene una ruta relativa. En otras palabras, ocean/wave.txt puede existir en varias ubicaciones de nuestro sistema de archivos. Por ejemplo, puede estar presente en /Users/user_1/ocean/wave.txt o /Users/user_2/research/ocean/wave.txt, pero no especificamos exactamente a cuál nos estamos refiriendo. Por el contrario, una ruta absoluta se refiere inequívocamente a una ubicación en el sistema de archivos.

      Puede usar Path.home() para obtener la ruta absoluta al directorio principal del usuario actual:

      home = Path.home()
      wave_absolute = Path(home, "ocean", "wave.txt")
      print(home)
      print(wave_absolute)
      

      Si ejecutamos este código, obtendremos un resultado similar al siguiente:

      Output

      /Users/sammy /Users/sammy/ocean/wave.txt

      Nota: Como se mencionó anteriormente, su resultado variará dependiendo de su sistema operativo. Por supuesto, su directorio principal también será distinto de /Users/sammy.

      Path.home() devuelve una instancia Path con una ruta absoluta al directorio principal del usuario actual. Luego, pasamos esta instancia de Path y las cadenas "ocean" y "wave.txt" a otro constructor de Path para crear una ruta absoluta al archivo wave.txt. El resultado indica que la primera línea es el directorio principal y la segunda, el directorio principal más ocean/wave.txt.

      Este ejemplo también ilustra una característica importante de la clase Path: el constructor Path acepta tanto cadenas como objetos preexistentes de Path.

      Analicemos con mayor detalle cómo es que el constructor Path admite tanto cadenas como de objetos de Path:

      shark = Path(Path.home(), "ocean", "animals", Path("fish", "shark.txt"))
      print(shark)
      

      Si ejecutamos este código de Python, obtendremos un resultado similar al siguiente:

      Output

      /Users/sammy/ocean/animals/fish/shark.txt

      shark es un Path a un archivo que construimos usando tanto objetos Path (Path.home() y Path("fish", "shark.txt")) como cadenas ("ocean" y "animals"). El constructor Path gestiona de forma inteligente ambos tipos de objetos y los une de forma correcta usando el separador correspondiente del sistema operativo, en este caso: /.

      Acceder a los atributos de los archivos

      Ahora que hemos aprendido a crear instancias de Path, vamos a repasar cómo puede usar esas instancias para acceder a información sobre un archivo.

      Podemos usar los atributos name y suffix para acceder a los nombres y los sufijos de archivos:

      wave = Path("ocean", "wave.txt")
      print(wave)
      print(wave.name)
      print(wave.suffix)
      

      Al ejecutar este código, obtendremos un resultado similar al siguiente:

      Output

      /Users/sammy/ocean/wave.txt wave.txt .txt

      Este resultado indica que el nombre del archivo al final de nuestra ruta es wave.txt y el sufijo de ese archivo es .txt.

      Las instancias de Path también ofrecen la función with_name, que le permite crear de forma sencilla un objeto Path nuevo con un nombre distinto:

      wave = Path("ocean", "wave.txt")
      tides = wave.with_name("tides.txt")
      print(wave)
      print(tides)
      

      Si ejecutamos este código, obtendremos un resultado similar al siguiente:

      ocean/wave.txt
      ocean/tides.txt
      

      El código, primero, construye una instancia Path que apunta a un archivo llamado wave.txt. Luego, invoca el método with_name en wave para devolver una segunda instancia Path que apunta a un archivo nuevo denominado tides.txt. La parte del directorio ocean/ de la ruta permanece intacta, por lo tanto, la ruta final queda establecida como ocean/tides.txt

      Acceder a antecesores

      A veces, resulta útil acceder a directorios que contienen una ruta determinada. Consideremos un ejemplo:

      shark = Path("ocean", "animals", "fish", "shark.txt")
      print(shark)
      print(shark.parent)
      

      Si ejecutamos este código, obtendremos un resultado similar al siguiente:

      Output

      ocean/animals/fish/shark.txt ocean/animals/fish

      El atributo parent en una instancia de Path devuelve el antecesor más inmediato de una ruta de archivos determinada. En este caso, devuelve el directorio que contiene el archivo shark.txt: ocean/animals/fish.

      Podemos acceder al atributo parent varias veces seguidas para recorrer el árbol de ancestros de un archivo determinado:

      shark = Path("ocean", "animals", "fish", "shark.txt")
      print(shark)
      print(shark.parent.parent)
      

      Si ejecutamos este código, obtendremos el siguiente resultado:

      Output

      ocean/animals/fish/shark.txt ocean/animals

      El resultado es similar al anterior, pero, ahora, hemos llegado a un nivel más alto al acceder a .parent por segunda vez. El directorio ocean/animals se encuentra dos directorios por encima de shark.txt.

      Usar Glob para enumerar archivos

      También es posible usar la clase Path para enumerar archivos usando el método glob.

      Imaginemos que tenemos una estructura de directorios similar a la siguiente:

      └── ocean
          ├── animals
          │   └── fish
          │       └── shark.txt
          ├── tides.txt
          └── wave.txt
      

      Un directorio ocean que contiene los archivos tides.txt y wave.txt. Tenemos un archivo denominado shark.txt anidado en el directorio ocean, un directorio animals y otro fish: ocean/animals/fish.

      Para enumerar todos los archivos .txt del directorio ocean, podríamos escribir lo siguiente:

      for txt_path in Path("ocean").glob("*.txt"):
          print(txt_path)
      

      Este código tendría un resultado similar al siguiente:

      Output

      ocean/wave.txt ocean/tides.txt

      El patrón glob __"*.txt" busca todos los archivos que terminan en .txt. Como el código del ejemplo ejecuta ese glob en el directorio ocean, devuelve los dos archivos .txt del directorio ocean: wave.txt y tides.txt.

      Nota: Para duplicar los resultados que se muestran en este ejemplo, copie la estructura de directorios que se ilustra aquí en su computadora.

      También podemos usar el método glob de manera recursiva. Para enumerar todos los archivos .txt del directorio ocean y todos sus subdirectorios, podemos escribir lo siguiente:

      for txt_path in Path("ocean").glob("**/*.txt"):
          print(txt_path)
      

      Al ejecutar este código, obtendríamos un resultado similar al siguiente:

      Output

      ocean/wave.txt ocean/tides.txt ocean/animals/fish/shark.txt

      La sección ** del patrón glob coincidirá con este directorio y todos sus subdirectorios de manera recursiva. Por tanto, no solo tenemos los archivos wave.txt y tides.txt en el resultado, sino que también recibimos el archivo shark.txt que estaba anidado en ocean/animals/fish.

      Calcular rutas relativas

      Podemos usar el método Path.relative_to para calcular rutas relacionadas entre sí. El método relative_to es útil cuando, por ejemplo, se desea recuperar una porción de una ruta de archivos larga.

      Analice el siguiente código:

      shark = Path("ocean", "animals", "fish", "shark.txt")
      below_ocean = shark.relative_to(Path("ocean"))
      below_animals = shark.relative_to(Path("ocean", "animals"))
      print(shark)
      print(below_ocean)
      print(below_animals)
      

      Si ejecutamos este código, obtendremos un resultado similar al siguiente:

      Output

      ocean/animals/fish/shark.txt animals/fish/shark.txt fish/shark.txt

      El método relative_to devuelve un nuevo objeto Path relacionado con el argumento determinado. En nuestro ejemplo, calculamos el Path a shark.txt en relación con el directorio ocean y, luego, en relación con los directorios ocean y animals.

      Si relative_to no puede calcular una respuesta porque le indicamos una ruta no relacionada, presenta un ValueError:

      shark = Path("ocean", "animals", "fish", "shark.txt")
      shark.relative_to(Path("unrelated", "path"))
      

      Obtendremos una excepción ValueError generada a partir de este código que será similar a la siguiente:

      Output

      Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/Python3.8/pathlib.py", line 899, in relative_to raise ValueError("{!r} does not start with {!r}" ValueError: 'ocean/animals/fish/shark.txt' does not start with 'unrelated/path'

      unrelated/path no forma parte de ocean/animals/fish/shark.txt, por lo tanto, Python no puede calcular una ruta relativa.

      Conclusión

      El módulo pathlib es un componente importante de la biblioteca estándar de Python que nos permite manipular rutas de sistemas de archivos de forma rápida en cualquier sistema operativo. En este tutorial, ha aprendido a usar algunas de las herramientas clave de pathlib para acceder a los atributos de archivos, enumerar archivos con patrones glob y desplazarse por archivos y directorios principales.

      El módulo pathlib expone clases y utilidades adicionales que no abarcamos en este tutorial. Ahora que tiene una referencia, puede usar la documentación del módulo pathlib para obtener más información sobre otras clases y utilidades disponibles.

      Si está interesado en utilizar otras bibliotecas de Python, consulte los siguientes tutoriales:



      Source link