Esteban Josué Alvarado Vargas
Noldia Chavez Cavero
Gran aporte, Gracias ✌
Miguel Angel Reyes Moreno
Gracias, esto me parece muchísimo mejor :D
Consolida una línea de comandos profesional en Python: implementa un método de lectura en el service, usa csv.DictReader para convertir registros en objetos tipo diccionario y muestra la salida con click.echo para un comportamiento consistente en cualquier sistema operativo. Aquí verás cómo pasar de imprimir cadenas sueltas a una interfaz limpia y estable.
¿Cómo implementar el método list_clients en el service?
Para obtener los clientes desde la “tabla” (archivo), se abre el recurso en modo lectura, se crea un DictReader con los nombres de campo del esquema del cliente y se convierte el reader (iterable) a una lista. Así, el service devuelve una colección lista para iterar en la consola. La idea clave: el reader es iterable y la función global list() lo materializa en memoria.
import csv CLIENT_SCHEMA = ["uid", "name", "company", "email", "position"] # del client schema class ClientService: def __init__(self, table_path): self._table_path = table_path # referencia a la "tabla" def list_clients(self): # no recibe argumentos externos with open(self._table_path, mode="r") as f: # abrir en lectura reader = csv.DictReader(f, fieldnames=CLIENT_SCHEMA) return list(reader) # convertir iterable a lista
¿Qué modos de apertura usar y por qué?
¿Cómo mostrar la lista en la consola con Click?
El comando list no requiere argumentos ni opciones adicionales. Se inicializa el service con el contexto y el nombre de la tabla, se obtiene la lista de clientes y se imprimen headers y filas. Se reemplaza print por click.echo porque print varía entre sistemas y click.echo garantiza salida consistente.
import click @click.command(name="list") @click.pass_context def list_command(ctx): # inicializar el service con contexto y nombre de la tabla service = ClientService(table_path=ctx.obj["table_name"]) clients_list = service.list_clients() # headers y separador click.echo("ID name company email position") click.echo("-" * 60) # formateo de salida por fila for client in clients_list: line = "{uid} {name} {company} {email} {position}".format( uid=client["uid"], name=client["name"], company=client["company"], email=client["email"], position=client["position"], ) click.echo(line)
¿Cómo formatear la salida de cada registro?
{} y .format(...) para cada campo.uid, name, company, email, position.¿Cómo validar el flujo completo desde la CLI?
La verificación es directa: invoca el grupo de comandos, llama list, crea otro cliente y vuelve a listar para confirmar que se agregan registros.
pvi clients. Muestra los comandos disponibles.pvi clients list. Debe aparecer el primer cliente.pvi clients list. Ahora aparecen dos clientes.Este avance muestra cómo separar lógica de negocio (clases y services) de la interfaz de consola con Click. Es un hito: se pasa de concatenar cadenas a una estructura robusta, cercana a estándares de producción.
¿Te gustaría añadir filtros, paginación o un ancho fijo por columna? Comparte tus ideas y retos en los comentarios.
Esteban Josué Alvarado Vargas
Noldia Chavez Cavero
Gran aporte, Gracias ✌
Miguel Angel Reyes Moreno
Gracias, esto me parece muchísimo mejor :D
David fernando Pinzon suarez
Deberían compartir el código por que el mio no funciona y no tengo con que compararlo....
Consensus Group
Miguel Angel Reyes Moreno
Micael Martinez
Qué diferencia hay entre: mode=‘a’ y mode=‘w’? Uno añade y el otro escribe, pero si escribes no estás añadiendo?
Aarón Espasandín
Write ('w') lo que hace es sobrescribir el archivo (limpiar el archivo y escribir sobre él). Por esto, a mi entender, en la otra versión de platzi ventas creabamos una nueva tabla y borrábamos la anterior tmp. Append ('a'), en cambio, te permite evitar el borrar el archivo y poder añadir o escribir al final del archivo.
Facundo Nicolás García Martoni
ProfesorYo implementé asi el for:
for client in client_list: name, company, email, position, id = list(client.values()) # Guarda los valores. print(f"{id} | {name} | {company} | {email} | {position}")
Brian Alvarado
javier martinez
Asi se veria usando PrettyTable.
pip install prettytable
!example
Nicolas Alpargatero
Gracias por compartir mejor que tabulate
Juan Carlos Pinzón
Me generó una inquietud y es que el profesor colocó una función en el archivo la cual tiene como nombre list y pues se supone que es una keyword reservada de Python, lo extraño es que no le haya fallado.
Ruben Eduardo Acosta Vela
José María Cuevas Ramírez
Al hacer eso lo que está haciendo es convertir la variable a una lista, como cuando tenemos un texto y lo queremos convertir a un entero.
meaning_of_life = int("42")
Noe Mikhael Huaccharaque Ruiz
🙋Aqui: 👉Archivos de la clase 43:
Luis Urdiales
al ejecutar mi programa me manda el siguiente error :
File “C:\platzi-ventas\lib\site-packages\click\decorators.py”, line 71, in _make_command raise TypeError('Attempted to convert a callback into a ' TypeError: Attempted to convert a callback into a command twice.
Alguien sabe a que se refiere ?
José María Cuevas Ramírez
Coloca tu código para que te podamos ayudar :)
Cesar Hernández Ramírez
Tienes un error en uno de tus decoradores, revisa si tienes los mismos que el maestro, puede ser un typo, a lo mejor te confundiste de nombre de variable, etc.
Gerardo Alberto Soto Alvarez del Castillo
Fantástico el uso del Framework Click para la creación de API's La función click.echo(), que en muchos sentidos funciona como la declaración print o función de Python. La principal diferencia es que funciona igual en Python 2 y 3, detecta inteligentemente los flujos de salida mal configurados y nunca fallará (excepto en Python 3)
Omar Daniel Centeno
Está muy bueno conocer más comando de Click que son prácticos y realmente aplicables :D
santiago lagos
Para las personas que quieran practicar con una clase para mejorar la visualización de la data, les comparto este package https://pypi.org/project/beautifultable/ Tanto su documentación como varios ejemplos de uso aparecen allí.
Andres Martinez
Implementacion inicial de lista de clientes
se modificos 2
services.py
se agrego funcion list_clients //metodo para leer csv a un objeto lista usando
//el metodo estatico de clase client
command.py
se modifico el metodo list_clients
se intancia la clase ClientServices con los paremetro nombre de la tabla de contexto
en la variable client_service se hace referencia a nuestra clase Client_service
//trae el listado de clientes
en la variable lista client_list se accesa al metodo de la variable client_service
instanciada al metodo list_clients
//se crea encabezado de datos a mostrar
//iteramos a lo largo de nuestra lista de clientes
Osvaldo Trejo
Blas Martin Baez Bobadilla
Jarod Fernando Fernandez Morales
Modos de apertura de archivos
Juan Carlos Zabaleta Lozano
ImportError: cannot import name nose si es la version de python :C pero no me funciona
Omar Daniel Centeno
Hola
¿Qué versión de Python estás utilizando? Puedes compartir el repositorio o integrar el código el mensaje.
Geovany Uribe Aguirre
Hola, no he podido ejecutar el programa, cuando escribo pv --help o también pv clients list, pv clients create, etc no se me ejecuta nada ¿Alguien podría ayudarme?
Hugo Orlando Gonzalez
Podes probar instalando pv, para eso se ejecuta sudo apt-get install pv
Sergio Arturo Enriquez Nava
Hola podrías poner un screenshoot de lo que te sale cuando tratas de ejecutar el programa. Seria bueno saber que sistema operativo usas y si tuviste algún problema al instalar el paquete Cick. Saludos
Geovany Uribe Aguirre
Hola, desde hace algunas clases que no he podido ejecutar el programa, cuando escribo pv --help o también pv clients list, pv clients create, etc no se me ejecuta nada ¿Alguien podría ayudarme? Aparece que estoy en el entorno virtual y todo.
Israel Yance
Como te respondí en otro consulta que hiciste. Trata de revisar si tu código es igual al que escribió el profesor o intenta a hacer todo de nuevo. Creando nuevos archivos con su entorno virtual, en otra carpeta para que no tengas conflictos.
Cesar Hernández Ramírez
Pueden ser dos cosas, puede que alguno te tus archivos no esté igual que el que vamos haciendo a lo mejor algún typo pero si no te está dando error, entonces el problema está en la instalación del --editable. Ingresa a tu carpeta y borra la carpeta que se llama algo así como egg.info--editable (es algo así), de paso también borra la carpeta venv. Una vez tengas todo eso limpio, instala de nuevo el entorno virtual, instala la librería de click con: pip install click Después instala el editable con: pip install --editable . (Importante el punto al final) Y listo, aveces movemos los archivos lo las carpetas y puede fallar alguno de estos 2. Yo tenía el mismo problema y así lo solucioné
ANIBAL OZIEL ALVARADO CERVANTES
Hola : Al correr pv clients list, me muestra los datos del id de manera diferente al profesor, ya que a el le sale una serie de números y letras en el id y a mi me muestra un modulo como lo muestro a continuación :
ID | NAME | COMPANY | EMAIL | POSITION
<module 'uuid' from '/usr/lib/python3.9/uuid.py'> | Oziel | Softek | Oziel@gmail.com | Programador <module 'uuid' from '/usr/lib/python3.9/uuid.py'> | anibal | ggg | anibal@gmail.com | gerente
Favor de apoyarme con la solución !
¡ Saludos!
atte.
Aníbal
ANIBAL OZIEL ALVARADO CERVANTES
Hola : Favor de apoyar con la pregunta que hice hace 3 días, ya que no puedo avanzar con el anterior error !!!!.
ID | NAME | COMPANY | EMAIL | POSITION
<module ‘uuid’ from ‘/usr/lib/python3.9/uuid.py’> | Oziel | Softek | Oziel@gmail.com | Programador <module ‘uuid’ from ‘/usr/lib/python3.9/uuid.py’> | anibal | ggg | anibal@gmail.com | gerente
¡ Saludos!
atte.
Aníbal
Nicolas Alpargatero
ya ni debes ver esto, pero ya la culpa era de tener un archivo aparte llamado uuid.py y si no comparten el código difícil.
Fabricio González Guasque
A alguien le sucedio que no le funcionara con click.echo() y si lo imprima con print nomas. Porque a mi me sucede eso. Tengo python 3.9. Cuando coloco con click.echo() me sale lo siguiente:
", line 298, in echo file.write(out) # type: ignore AttributeError: 'str' object has no attribute 'write'
Omar Daniel Centeno
¡Hola! :)
Si pudieras compartirnos tu código sería más sencillo encontrar la solución. Puedes copiar y pegar utilizando el botón de </> código.
¡Saludos!
Fabricio González Guasque
++Tablas con mejor formato++
¡Hola a todos! Les comparto una mejora que le hice al código para que la tabla tenga un mejor formato.
Utilicé un módulo llamado Tabulate, que pueden encontrar en la página oficial de PyPI o solo instalarlo con $ pip install tabulate.
El código del método list queda así:
from tabulate import tabulate @clients.command() @click.pass_context def list(ctx): """List all clients""" client_service = ClientService(ctx.obj['clients_table']) clients_list = client_service.list_clients() headers = [field.capitalize() for field in Client.schema()] table = [] for client in clients_list: table.append( [client['name'], client['company'], client['email'], client['position'], client['uid']]) print(tabulate(table, headers))
El resultado:
Les recomiendo leer un poco más acerca del funcionamiento del módulo aquí.
Yo lo tengo así y me funcionó:
En services.py va esto
import csv from clients.models import Client class ClientService: def __init__(self, table_name): self.table_name = table_name def create_client(self, client): with open(self.table_name, mode='a') as f: writer = csv.DictWriter(f, fieldnames=Client.schema()) writer.writerow(client.to_dict()) def list_clients(self): with open(self.table_name, mode='r') as f: reader = csv.DictReader(f, fieldnames=Client.schema()) return list(reader)
. . En commands.py va esto
import click from clients.services import ClientService from clients.models import Client @click.group() def clients(): """Manages clients lifecycle""" pass @clients.command() @click.option('-n', '--name', type=str, prompt=True, help='The client name') @click.option('-c', '--company', type=str, prompt=True, help='The client company') @click.option('-e', '--email', type=str, prompt=True, help='The client email') @click.option('-p', '--position', type=str, prompt=True, help='The client position') @click.pass_context def create(ctx,name,company,email,position): """Creates a new client""" client = Client(name, company, email, position) client_service = ClientService(ctx.obj['clients_table']) client_service.create_client(client) @clients.command() @click.pass_context def list(ctx): """List all clinets""" client_service = ClientService(ctx.obj['clients_table']) clients_list = client_service.list_clients() click.echo(' ID | NAME | COMPANY | EMAIL | POSITION') click.echo('*'*100) for client in clients_list: click.echo('{uid} | {name} | {company} | {email} | {position}'.format( uid=client['uid'], name=client['name'], company=client['company'], email=client['email'], position=client['position'])) @clients.command() @click.pass_context def update(ctx, client_uid): """Updates client""" pass @clients.command() @click.pass_context def delete(ctx, client_uid): """Deletes client""" pass all = clients
. . En pv.py
import click from clients import commands as clients_commands CLIENTS_TABLE = '.clients.csv' @click.group() @click.pass_context def cli(ctx): ctx.obj = {} ctx.obj['clients_table'] = CLIENTS_TABLE cli.add_command(clients_commands.all)
suerte
Olvidaste setup.py:
from setuptools import setup setup( name='pv', version='0.1', py_modules=['pv'], install_requires=[ 'Click', ], entry_points=''' [console_scripts] pv=pv:cli ''', )
models.py:
import uuid class Client: def __init__(self, name, company, email, position, uid=None): self.name = name self.company = company self.email = email self.position = position self.uid = uid or uuid.uuid4() #Genera ID únicos def to_dict(self): return vars(self) @staticmethod # estático es que se puede ejecutar sin necesidad de una instancia de clase def schema(): return ['name', 'company', 'email', 'position', 'uid']
Y estamos igual y aún así no me funciona
Justamente como dices, el modo w (write) sobreescribe el archivo, por lo que no era necesario crear un archivo auxiliar .tmp, quedando así el método ++_save_clients_to_storage++:
def _save_clients_to_storage(): with open(CLIENT_TABLE, 'w') as f: writer = csv.DictWriter(f, fieldnames=CLIENT_SCHEMA) writer.writerows(clients)
Con PrettyTable también es posible formatear la tabla en la consola.
from prettytable import PrettyTable @clients.command() @click.pass_context def list(ctx): """List all clients""" client_service = ClientService(ctx.obj['clients_table']) clients = client_service.list_clients() table = PrettyTable() table.field_names = [c.upper() for c in Client.schema()] for client in clients: table.add_row([ client['name'], client['company'], client['email'], client['position'], client['uid'], ]) click.echo(table)
Aunque sea una palabra reservada python no pone error, solo sobre escribe la función. Digamos, puedes decir:
list = 9
y no hay error, pero ahora list es una variable, y no la función que conocemos, por tanto si luego decimos:
a = (1,2,3) list(a)
Esto genera un el siguiente error:
TypeError: 'int' object is not callable
Ya que list es entero, no una función.
Aunque python permite sobre escribir sus propias funciones, no es bueno hacerlo.
Commands.py
import click from clients.services import ClientServices from clients.models import Client @click.group() def clients(): """ Manages the clients lifecycle""" pass @clients.command() @click.option('-n', '--name', type=str, prompt=True, help='The client name') @click.option('-c', '--company', type=str, prompt=True, help='The client Company') @click.option('-e', '--email', type=str, prompt=True, help='The client email') @click.option('-p', '--position', type=str, prompt=True, help='The client position') @click.pass_context def create(ctx, name, company, email, position): """Create a new client """ client = Client(name, company, email, position) client_service = ClientServices(ctx.obj['clients_table']) client_service.create_client(client) @clients.command() @click.pass_context def list_clients(ctx): """List all clients""" client_services = ClientServices(ctx.obj['clients_table']) client_list = client_services.list_clients() click.echo(' ID | NAME | COMPANY | EMAIL | POSITION') click.echo('*'*50) for client in client_list: print('{uid} | {name} | {company} | {email} | {position}'.format( uid = client['uid'], name = client['name'], company = client['company'], email = client['email'], position = client['position'] )) @clients.command() @click.pass_context def update(ctx, client_name): """Update a client """ @clients.command() @click.pass_context def delete(ctx, client_name): """Delete a client""" pass all = clients
services.py
import csv from clients.models import Client class ClientServices: def __init__(self, table_name): self.table_name = table_name def create_client(self, client): with open(self.table_name, mode='a') as f: write = csv.DictWriter(f, fieldnames=Client.schema()) write.writerow(client.to_dict()) def list_clients(self): with open(self.table_name, 'r') as f : reader = csv.DictReader(f, fieldnames=Client.schema()) return list(reader)
Hola ODCenteno gracias por la rapida atencion, lo que sucedia era que colocaba lo siguiente
click.echo(click.style(' ID | NOMBRE | DEPARTAMENTO | CONTACTO | EMAIL ', '\n','*'*100)
por lo que me saliá error, esto leyendo documentación, aun no encontré como colocar el salto de linea y los 100 asteriscos, porque la coma es lo que me da el error. Pero ya encontre otras funcionalidades de click que están buenas, por ejemplo:
click.echo(click.style(' ID | NOMBRE | DEPARTAMENTO | CONTACTO | EMAIL ', bg='blue'))
al agregarle click.style se le pueden dar colores a las letras con fg, o con bg se reslata(como con un marcado) todo el texto, en este caso lo puse en azul. Voy a seguir buscando como colocar en la misma linea de codigo el salto de linea. Gracias