Django · Gender prediction
Batch gender enrichment with a Django command
The enrich_gender management command loads customers whose gender_status is still empty, groups them into chunks of 50 and sends each chunk to /v1/gender/batch. Item ids are the customers' primary keys as strings, so each result maps straight back to a model instance. When a customer has a country code, it is added to the item as a hint, which helps with names that lean differently by country.
Every identified or unknown prediction is saved with update_fields, and unknown results keep an empty predicted_gender so they are never looked up again. Item-level errors are skipped and picked up by the next run. The send method retries only a 429 with code rate_limited, sleeping for Retry-After, and raises CommandError for anything else so a failed batch is never resent blindly.
Steps
- 1
Add gender_status, predicted_gender and gender_confidence fields to your Customer model and run migrate.
- 2
Save the command as customers/management/commands/enrich_gender.py.
- 3
Run python manage.py enrich_gender --limit 200 to try a small batch first.
- 4
Schedule the command with cron, Celery beat or your platform's scheduler to cover new customers.
- 5
Report results in aggregate and let customers set their own gender in their profile.
pip install django requests# customers/management/commands/enrich_gender.py
import time
from django.core.management.base import BaseCommand, CommandError
from accounts.finowl import FinowlError, finowl_post
from customers.models import Customer
BATCH_SIZE = 50 # the batch endpoint accepts 1 to 50 items
class Command(BaseCommand):
help = "Estimate gender from first names for aggregate analytics."
def add_arguments(self, parser):
parser.add_argument("--limit", type=int, default=1000)
def handle(self, *args, **options):
queryset = Customer.objects.filter(gender_status="").exclude(first_name="")
customers = list(queryset.order_by("pk")[: options["limit"]])
for start in range(0, len(customers), BATCH_SIZE):
chunk = {str(c.pk): c for c in customers[start : start + BATCH_SIZE]}
items = [
{"id": pk, "type": "name", "value": c.first_name, **({"country": c.country} if c.country else {})}
for pk, c in chunk.items()
]
for row in self.send(items):
customer = chunk[row["id"]]
if "error" in row:
self.stderr.write(f"{customer.pk}: {row['error']['code']}, left for a later run")
continue
prediction = row["data"]
customer.gender_status = prediction["result_status"] # identified or unknown
customer.predicted_gender = prediction["gender"] or "" # None when unknown
customer.gender_confidence = prediction["confidence"]
customer.save(update_fields=["gender_status", "predicted_gender", "gender_confidence"])
self.stdout.write(f"Processed {start + len(chunk)} of {len(customers)}")
def send(self, items):
while True:
try:
return finowl_post("/v1/gender/batch", {"items": items}, timeout=30)["data"]
except FinowlError as exc:
if exc.status == 429 and exc.code == "rate_limited":
time.sleep(exc.retry_after or 5) # the batch never ran, so resending is safe
continue
if exc.status == 400:
raise CommandError("Batch failed validation; no item was processed") from exc
if exc.status == 401:
raise CommandError("Check FINOWL_API_KEY") from exc
raise CommandError(f"Finowl {exc.status} {exc.code}; not resending this batch") from excIN PRODUCTION
Production tips
Store gender_confidence as a nullable FloatField. Unknown results may not include a confidence, and null keeps averages and filters honest in your analytics queries.
Batch items run with ai_mode off by default, so results come only from the dataset at 1 credit each. Expect more unknown results for handles and unusual names than single lookups give.
Mention gender estimation in your privacy notice, because names are personal data. Keep the predicted field separate from any gender a customer provides, and let the provided value take precedence.
Each lookup costs 1 credit (3 when it requests AI), with 100 free credits every month. See pricing and error codes.
A FEW GOOD QUESTIONS
Common questions
Related guides
- Check email breaches in a Django signup form
- Breached password validator for Django
- Predict gender from names in Python
- Predict gender from a name in Node.js
- Batch gender prediction in TypeScript
- Predict gender from names in Go with net/http
- Gender analytics from names for fintech audiences
- Gender insights from names for e-commerce analytics
- Gender estimates for SaaS user research and analytics
Batch gender enrichment with a Django command
Grab an API key, paste the example and run it. 100 free credits every month.