Last active
September 25, 2024 18:57
-
-
Save racecarparts/a718d5d3e4a4265e31b9051f2adfb503 to your computer and use it in GitHub Desktop.
Format django queryset as a table in the terminal
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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