Prompts for Generating Structured Data
What This Lesson Covers Lessons 22 and 81 covered extracting structured data from existing text. Lesson 99 covers getting JSON-style responses reliably. This lesson covers a different task: generating new structured data, such as:
Realistic test records for development and testing Edge cases that test how an application handles unusual input Seed data and fixtures for a development database Sample API responses for building a frontend before the backend is ready Labeled examples for evaluating prompts (Lesson 119) Generated data is useful because it is fast to create, contains no real personal information, and can be designed to cover cases that real data rarely contains. It also has risks: it can be unrealistic, repetitive, inconsistent, or subtly invalid. This lesson's recipes are designed with those risks in mind, and every recipe is paired with validation in code.
When to Use a Model and When to Use Code Field Type Better Generated By Why IDs, sequential numbers Code Exact and unique Random dates, amounts in a range Code Exact distribution control Simple names, emails, phone numbers Code (for example, the Faker library) Fast, unlimited, consistent formats Realistic free text: reviews, support messages, descriptions Model Natural language variety Records whose fields must make sense together Model, then validated in code Realistic combinations Edge cases that need creativity Model Thinking of unusual situations A good approach often combines both: code generates the structure and simple fields, and a model writes the realistic text.
Recipe 1: Realistic Test Records Use it when: you need records that look like real data for development or demos.
Prompt Generate {count} realistic student enrollment records for testing an online course platform in India. Return only a JSON array of objects with these fields:
- name: a realistic Indian full name
- email: a lowercase email address that uses the domain example.com
- course: one of "Python Basics", "Django REST APIs", "React Fundamentals"
- progress: integer from 0 to 100
- enrolled_on: a date between 2026-01-01 and 2026-10-26, as YYYY-MM-DD
Requirements:
- No two records may have the same email.
- Use a variety of names from different regions of India.
- Distribute records across all three courses.
- Do not use real people's names or real email addresses. Why It Works example.com is a domain reserved for documentation and examples, so generated emails cannot belong to real people. Allowed values and ranges for every field make validation straightforward. Variety and distribution requirements counter the model's tendency to repeat similar records. Validating the Records in Code import re from datetime import date
ALLOWED_COURSES = {"Python Basics", "Django REST APIs", "React Fundamentals"} EMAIL = re.compile(r"^[\w.+-]+@example.com$")
def validate_students(records: list[dict]) -> list[str]: problems, seen_emails = [], set() for i, record in enumerate(records, start=1): if not EMAIL.match(record.get("email", "")): problems.append(f"Record {i}: email must use the example.com domain") if record.get("email") in seen_emails: problems.append(f"Record {i}: duplicate email {record['email']}") seen_emails.add(record.get("email")) if record.get("course") not in ALLOWED_COURSES: problems.append(f"Record {i}: unknown course {record.get('course')!r}") if not 0 <= record.get("progress", -1) <= 100: problems.append(f"Record {i}: progress must be 0-100") try: if date.fromisoformat(record.get("enrolled_on", "")) > date(2026, 10, 26): problems.append(f"Record {i}: enrolled_on is in the future") except ValueError: problems.append(f"Record {i}: enrolled_on is not a valid date") return problems
records = [ {"name": "Asha Kumar", "email": "asha.k@example.com", "course": "Python Basics", "progress": 45, "enrolled_on": "2026-09-14"}, {"name": "Vikram Nair", "email": "vikram@example.com", "course": "Django REST APIs", "progress": 120, "enrolled_on": "2026-10-02"}, {"name": "Meera Iyer", "email": "asha.k@example.com", "course": "Vue Basics", "progress": 10, "enrolled_on": "2026-02-30"}, ]
for problem in validate_students(records): print(problem) Output:
Record 2: progress must be 0-100 Record 3: duplicate email asha.k@example.com Record 3: unknown course 'Vue Basics' Record 3: enrolled_on is not a valid date Code Explanation Email check: the regular expression requires the example.com domain, enforcing the privacy rule from the prompt. Uniqueness: seen_emails tracks addresses already used, so duplicates are reported. Allowed values and ranges: the course must be one of the three options, and progress must be between 0 and 100. Dates: date.fromisoformat() rejects impossible dates such as 30 February, and the comparison rejects dates after the reference date. Record 3 shows how one generated record can contain several different problems. Reject or regenerate invalid records rather than fixing them silently, so you notice if the prompt needs improvement.
Recipe 2: Edge-Case Test Data Use it when: testing how an application handles unusual, extreme, or invalid input.
Prompt Generate test inputs for the "full name" field of a student registration form.
Include cases in each category, 3 per category:
- Valid but unusual: very short, very long, initials, single names
- Names in other scripts: Tamil, Hindi, and other languages
- Characters that often break systems: apostrophes, hyphens, extra spaces, emojis
- Invalid: empty, only spaces, only digits
Return only a JSON array: [{"value": "...", "category": "...", "should_be_accepted": true or false, "why": "one sentence"}] Why It Works Categories ensure broad coverage of the ways input can be unusual. should_be_accepted turns generated data into test cases with expected results. Names in other scripts test whether the application supports the users it actually has, such as a student whose name is written in Tamil. Review the Expectations Whether a value "should be accepted" is a product decision, not a model decision. Review the expected results before using them as tests. A model might, for example, mark a valid single name as invalid.
Recipe 3: Database Seed Data and Django Fixtures Use it when: filling a development database with realistic data.
A fixture is a file of data that a framework can load into the database. In Django, a fixture is a JSON list of objects with model, pk (primary key), and fields.
Converting Validated Records Into a Fixture import json
def to_django_fixture(records: list[dict], model: str = "courses.enrollment") -> list[dict]: return [{"model": model, "pk": i, "fields": record} for i, record in enumerate(records, start=1)]
fixture = to_django_fixture(records[:1]) print(json.dumps(fixture, indent=2)) Output:
[ { "model": "courses.enrollment", "pk": 1, "fields": { "name": "Asha Kumar", "email": "asha.k@example.com", "course": "Python Basics", "progress": 45, "enrolled_on": "2026-09-14" } } ] Code Explanation Each record becomes one fixture object. The model label courses.enrollment follows Django's app_label.modelname format. pk values are assigned in code, so they are unique and sequential. The resulting file can be loaded with Django's loaddata management command. Important Details The keys in fields must match the field names of your Django model exactly. If course is a foreign key (a link to another table), the fixture must contain the primary key of the related course, not its name. Generate or look up those keys in code. Validate records before creating the fixture, so invalid data never reaches the database. Recipe 4: CSV Data Use it when: data must be imported into spreadsheets or tools that read CSV files.
Prompt Generate {count} rows of realistic course feedback as CSV with this exact header: student_id,course,rating,comment
- student_id: S001 to S{count:03}, in order
- course: "Python Basics", "Django REST APIs", or "React Fundamentals"
- rating: integer 1 to 5, with a realistic mix (not all high)
- comment: one or two sentences matching the rating; wrap the comment in double quotes
Return only the CSV, with no code fences. Parse CSV With the csv Module Comments often contain commas, which break naive splitting with line.split(","). Python's csv module handles quoted fields correctly:
import csv import io
def parse_feedback_csv(text: str) -> list[dict]: reader = csv.DictReader(io.StringIO(text.strip())) if reader.fieldnames != ["student_id", "course", "rating", "comment"]: raise ValueError(f"Unexpected header: {reader.fieldnames}") return list(reader)
sample = 'student_id,course,rating,comment\nS001,Python Basics,4,"Clear lessons, but the quiz was hard."' print(parse_feedback_csv(sample)) Output:
[{'student_id': 'S001', 'course': 'Python Basics', 'rating': '4', 'comment': 'Clear lessons, but the quiz was hard.'}] Code Explanation io.StringIO lets the CSV reader read from a string instead of a file. csv.DictReader returns each row as a dictionary keyed by the header names. The header check confirms that the model followed the exact column order. The quoted comment containing a comma is parsed as a single field. Note that values are read as strings; convert rating to an integer during validation. Recipe 5: Mock API Responses Use it when: frontend developers need realistic responses before the backend API exists, or when writing documentation examples.
Prompt Generate 3 example responses for the API endpoint GET /api/courses/{id}/ described in . Include: one normal course, one course with no prerequisites and an empty projects list, and one course that is sold out (seats_left = 0).
Every response must follow the schema in exactly: the same field names, types, and nesting. Return only a JSON array of the 3 response bodies.
{api_schema} Why It Works Named scenarios (normal, empty lists, sold out) make sure the frontend is built to handle the states it will actually meet. Exact schema adherence keeps mock data compatible with the real API. Validate Against the Schema Use a schema validation library, such as Pydantic (Lesson 22) or jsonschema, to confirm each mock response matches the specification. Mock data that does not match the real schema leads to frontend bugs later.
Recipe 6: Labeled Examples for Evaluation Use it when: building test sets for prompts, such as messages with expected classification labels (Lessons 115, 119).
Prompt Generate {count} realistic student support messages for testing a classifier with the labels in .
- Produce {per_label} messages for each label.
- Include at least 2 borderline messages per label that could be confused with another label, and mark them borderline.
- Vary length, tone, and language, including some messages in Tanglish or Hinglish.
Return only a JSON array: [{"message": "...", "expected_label": "...", "borderline": true or false}]
{label_definitions} Why It Works Equal numbers per label produce a balanced test set. Borderline cases test where classifiers usually fail (Lesson 28). Varied language and tone make the test set more like real data. A Critical Warning Generated labels must be reviewed by a person before being used as ground truth. If the model that generates test data is similar to the model being tested, both may share the same blind spots, and the test set will make the classifier look better than it is. Real, anonymized messages from your users should make up a significant part of any evaluation set.
Recipe 7: Controlling Distribution and Variety Use it when: generated data must match realistic proportions, or must not be repetitive.
Generate per Category in Code Rather than asking for "100 records with 60% positive reviews", call the model separately for each category and let code control the counts:
For each target in [("positive", 60), ("negative", 25), ("mixed", 15)]: ask the model for that number of reviews with that sentiment Why This Works Better Models are poor at producing exact proportions in a single large request, and long outputs become repetitive. Smaller, category-specific requests give exact counts and more variety. Remove near-duplicates with the method from Lesson 87.
Common Mistakes Mistake 1: Real-Looking Personal Data What it is: Generated data with realistic email domains or phone numbers.
Why it happens: The prompt asks only for "realistic" data.
What it causes: Data that may match real people, causing privacy and messaging risks if used in tests that send emails or SMS.
How to fix it: Use reserved domains such as example.com, clearly fake number ranges, and validation that enforces them.
Mistake 2: No Validation What it is: Loading generated data directly into a database or test suite.
Why it happens: Generated data looks correct.
What it causes: Invalid dates, duplicate keys, and out-of-range values cause confusing failures.
How to fix it: Validate every record against explicit rules before use.
Mistake 3: Using the Model for Simple Random Fields What it is: Asking a model to generate thousands of IDs, numbers, or dates.
Why it happens: One prompt for everything seems simpler.
What it causes: High cost, duplicates, and poor control of distributions.
How to fix it: Generate simple fields in code and use the model for realistic text.
Mistake 4: Unreviewed Ground Truth What it is: Using model-generated labels as the correct answers for evaluation.
Why it happens: Labeling is time-consuming.
What it causes: Misleading evaluation results.
How to fix it: Have people review labels, and include real data in evaluation sets.
Mistake 5: Parsing CSV by Splitting on Commas What it is: Using split(",") on generated CSV.
Why it happens: It works on simple rows.
What it causes: Broken rows whenever a text field contains a comma.
How to fix it: Use the csv module.
Best Practices Choose model or code per field. Code for exact and simple fields; models for realistic text.
Define fields, types, allowed values, and ranges in the prompt. They make validation possible.
Never generate data that could belong to real people. Use reserved domains and clearly fake identifiers.
Validate every generated record. Reject and regenerate invalid data.
Control distributions in code. Generate per category with exact counts.
Review generated test labels. And combine them with real, anonymized data.
Try It Yourself Exercise 1: Generate and Validate Use Recipe 1 to generate 20 enrollment records. Run validate_students() and record every problem. Improve the prompt to reduce the problems, and regenerate until all records pass.
Exercise 2: Build a Fixture Create a small Django app with a Course model and an Enrollment model that has a foreign key to Course. Generate enrollments with a model, validate them, replace course names with course primary keys in code, create a fixture with to_django_fixture(), and load it with python manage.py loaddata.
Exercise 3: Edge-Case Tests Use Recipe 2 to generate edge cases for an email field and a phone number field on a registration form. Review the expected results, turn them into automated tests for your form validation, and record any cases where your application behaves differently from the expectation.