mirror of
https://expo.survex.com/repositories/troggle/.git
synced 2025-12-18 17:47:03 +00:00
utility to find empty fields on databse classes: for tidying
This commit is contained in:
124
core/management/commands/find_empty_fields.py
Normal file
124
core/management/commands/find_empty_fields.py
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.apps import apps
|
||||||
|
from django.db.models import Q, CharField, TextField
|
||||||
|
|
||||||
|
"""Completely written by https://gemini.google.com/ 2.5
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_all_project_models():
|
||||||
|
"""
|
||||||
|
Returns a list of all concrete model.Model classes in the Django project.
|
||||||
|
It ignores abstract models and models from third-party apps that aren't
|
||||||
|
part of the project's own apps.
|
||||||
|
"""
|
||||||
|
# You can customize this list to include/exclude specific apps
|
||||||
|
# For example: installed_apps = settings.INSTALLED_APPS
|
||||||
|
all_models = []
|
||||||
|
# This gathers all models from all installed apps.
|
||||||
|
for app_config in apps.get_app_configs():
|
||||||
|
all_models.extend(list(app_config.get_models()))
|
||||||
|
return all_models
|
||||||
|
|
||||||
|
|
||||||
|
def find_empty_or_null_fields(model_class):
|
||||||
|
"""
|
||||||
|
Takes a model class and checks every field.
|
||||||
|
|
||||||
|
Returns a list of field names for which all database entries
|
||||||
|
are either NULL or an empty string.
|
||||||
|
"""
|
||||||
|
completely_empty_fields = []
|
||||||
|
|
||||||
|
# Get the total number of records for this model.
|
||||||
|
total_records = model_class.objects.count()
|
||||||
|
|
||||||
|
# If there are no records, all fields are technically "empty".
|
||||||
|
# We return an empty list to avoid reporting every field from an empty table.
|
||||||
|
if total_records == 0:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# Iterate over all concrete fields of the model.
|
||||||
|
for field in model_class._meta.get_fields():
|
||||||
|
# We only want to check actual database columns, not relations.
|
||||||
|
if not hasattr(field, 'column'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
field_name = field.name
|
||||||
|
|
||||||
|
# Build a query to find records that have a meaningful value.
|
||||||
|
# This is more efficient than counting all null/empty records.
|
||||||
|
# We exclude records that are NULL.
|
||||||
|
query_has_value = model_class.objects.exclude(Q(**{f'{field_name}__isnull': True}))
|
||||||
|
|
||||||
|
# For text-based fields, we also exclude empty strings.
|
||||||
|
if isinstance(field, (CharField, TextField)):
|
||||||
|
query_has_value = query_has_value.exclude(Q(**{f'{field_name}': ''}))
|
||||||
|
|
||||||
|
# If the query for records WITH a value returns FALSE (no such records exist),
|
||||||
|
# it means all records for this field are empty or null.
|
||||||
|
if not query_has_value.exists():
|
||||||
|
completely_empty_fields.append(field_name)
|
||||||
|
|
||||||
|
return completely_empty_fields
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Scans all project models to find fields where all entries are empty or null."
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
self.stdout.write(self.style.SUCCESS("🚀 Starting scan for empty or null fields across all models..."))
|
||||||
|
|
||||||
|
all_models = get_all_project_models()
|
||||||
|
found_any = False
|
||||||
|
|
||||||
|
for model in all_models:
|
||||||
|
total_records = model.objects.count()
|
||||||
|
model_name = f"{model._meta.app_label}.{model._meta.object_name}"
|
||||||
|
self.stdout.write(f"\n🔍 Checking model: {self.style.HTTP_INFO(model_name)} ({total_records} instances found)")
|
||||||
|
|
||||||
|
empty_fields = find_empty_or_null_fields(model)
|
||||||
|
|
||||||
|
if empty_fields:
|
||||||
|
found_any = True
|
||||||
|
for field_name in empty_fields:
|
||||||
|
self.stdout.write(
|
||||||
|
self.style.WARNING(f" -> Field '{field_name}' is completely empty or null.")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.stdout.write(self.style.SUCCESS(" ✅ All fields contain data."))
|
||||||
|
|
||||||
|
if not found_any:
|
||||||
|
self.stdout.write(self.style.SUCCESS("\n🎉 Scan complete. No fields were found to be universally empty or null."))
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
### How to Run the Command
|
||||||
|
|
||||||
|
After saving the file, you can run your new command from your project's root directory (where `manage.py` is located):
|
||||||
|
|
||||||
|
bash
|
||||||
|
uv run manage.py find_empty_fields
|
||||||
|
|
||||||
|
|
||||||
|
### Example Output
|
||||||
|
|
||||||
|
The output in your terminal will look something like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
🚀 Starting scan for empty or null fields across all models...
|
||||||
|
|
||||||
|
🔍 Checking model: auth.Group
|
||||||
|
✅ All fields contain data.
|
||||||
|
|
||||||
|
🔍 Checking model: auth.User
|
||||||
|
-> Field 'last_name' is completely empty or null.
|
||||||
|
-> Field 'first_name' is completely empty or null.
|
||||||
|
|
||||||
|
🔍 Checking model: products.Product
|
||||||
|
-> Field 'notes' is completely empty or null.
|
||||||
|
-> Field 'discount_code' is completely empty or null.
|
||||||
|
|
||||||
|
🔍 Checking model: products.Category
|
||||||
|
✅ All fields contain data.
|
||||||
|
"""
|
||||||
Reference in New Issue
Block a user