Skip to content

Instantly share code, notes, and snippets.

@racecarparts
Last active September 25, 2024 18:57
Show Gist options
  • Save racecarparts/a718d5d3e4a4265e31b9051f2adfb503 to your computer and use it in GitHub Desktop.
Save racecarparts/a718d5d3e4a4265e31b9051f2adfb503 to your computer and use it in GitHub Desktop.
Format django queryset as a table in the terminal
def query_set_as_table(queryset, many_to_many_fields=None, foreign_key_fields=None, exclude_fields=None, prefetch_related=False):
from decimal import Decimal
if many_to_many_fields is None:
many_to_many_fields = []
if foreign_key_fields is None:
foreign_key_fields = {}
if exclude_fields is None:
exclude_fields = []
# Prefetch related data for the ManyToMany fields and select related ForeignKey fields
if prefetch_related:
queryset = queryset.prefetch_related(*many_to_many_fields).select_related(*foreign_key_fields.keys())
data = []
for obj in queryset:
# Collect field values using the field names, excluding fields listed in exclude_fields
row_data = {
field.name: getattr(obj, field.name)
for field in obj._meta.fields
if field.name not in exclude_fields
}
# Add ForeignKey field values, excluding fields listed in exclude_fields
for fk_field, fk_attr in foreign_key_fields.items():
if fk_field not in exclude_fields:
related_obj = getattr(obj, fk_field)
# Use the 'key.field' format for the label and add the value
row_data[f"{fk_field}.{fk_attr}"] = getattr(related_obj, fk_attr, None) if related_obj else None
# Add ManyToMany field values, excluding fields listed in exclude_fields
for m2m_field in many_to_many_fields:
if m2m_field not in exclude_fields:
related_objects = getattr(obj, m2m_field).all()
# Join related objects' string representation into a comma-separated string
row_data[m2m_field] = ", ".join(str(rel_obj) for rel_obj in related_objects)
data.append(row_data)
# Check if data is empty
if not data:
print("No data to display.")
return
# Extract headers, excluding fields listed in exclude_fields
headers = data[0].keys()
# Calculate initial column widths based on data
col_widths = [max(len(str(item)) for item in col) for col in zip(*[d.values() for d in data])]
# Adjust column widths to ensure they are at least as wide as the headers
col_widths = [max(width, len(header)) for width, header in zip(col_widths, headers)]
# Print headers
header_row = " | ".join(f"{header:<{width}}" for header, width in zip(headers, col_widths))
print(header_row)
print("-" * len(header_row))
# Print data rows
for row in data:
print(" | ".join(f"{str(value):<{width}}" for value, width in zip(row.values(), col_widths)))
# Print row count
row_count = len(data)
print("\nSummary:")
print(f"Total Rows: {row_count}")
# Calculate and print summary data for numeric columns
summary = {}
for header in headers:
# Extract numeric values (including Decimal)
values = [row[header] for row in data if isinstance(row[header], (int, float, Decimal))]
if values:
total_sum = sum(values)
# Ensure proper formatting for Decimal types
formatted_sum = f"{total_sum:.2f}" if any(isinstance(v, Decimal) for v in values) else f"{total_sum}"
summary[header] = {
'count': len(values),
'sum': formatted_sum
}
for header, stats in summary.items():
print(f"{header}: Count = {stats['count']}, Sum = {stats['sum']}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment