25 KiB
Database Models Reference
Last Verified: January 6, 2026
Data Scoping Overview
| Scope | Models | Base Class | Filter By |
|---|---|---|---|
| Global | IntegrationProvider, AIModelConfig, SystemAISettings, GlobalAIPrompt, GlobalAuthorProfile, GlobalStrategy, GlobalModuleSettings, Industry, SeedKeyword |
models.Model |
None (platform-wide) |
| Account | Account, User, Plan, AccountSettings, ModuleEnableSettings, AISettings, AIPrompt, AuthorProfile, CreditBalance |
AccountBaseModel |
account |
| Site | Site, PublishingSettings, AutomationConfig, SiteIntegration |
AccountBaseModel |
account, site |
| Site+Sector | Keywords, Clusters, ContentIdeas, Tasks, Content, Images |
SiteSectorBaseModel |
site, sector |
System Models (v1.4.0) (igny8_core/modules/system/)
Purpose: Centralized AI configuration, provider API keys, and system-wide defaults.
IntegrationProvider (NEW v1.4.0)
Centralized storage for ALL external service API keys. Admin-only.
class IntegrationProvider(models.Model):
"""Per final-model-schemas.md - Centralized API key storage"""
provider_id = CharField(max_length=50, primary_key=True) # openai, runware, stripe, etc.
display_name = CharField(max_length=100)
provider_type = CharField(max_length=20) # ai, payment, email, storage
# Authentication
api_key = CharField(max_length=500, blank=True)
api_secret = CharField(max_length=500, blank=True)
webhook_secret = CharField(max_length=500, blank=True)
api_endpoint = URLField(blank=True)
# Configuration
config = JSONField(default=dict)
is_active = BooleanField(default=True)
is_sandbox = BooleanField(default=False)
# Audit
updated_by = ForeignKey(User, null=True)
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
Seeded Providers:
openai- AI (text + DALL-E)runware- AI (images)anthropic- AI (future)stripe- Paymentpaypal- Paymentresend- Email
Helper Methods:
IntegrationProvider.get_provider(provider_id)- Get active providerIntegrationProvider.get_api_key(provider_id)- Get API key for providerIntegrationProvider.get_providers_by_type(type)- List providers by type
AIModelConfig (NEW v1.4.0)
Single Source of Truth for all AI models with pricing and credit configuration.
class AIModelConfig(models.Model):
"""Per final-model-schemas.md - Model definitions + pricing"""
model_name = CharField(max_length=100, unique=True) # gpt-4o-mini, dall-e-3, runware:97@1
model_type = CharField(max_length=20) # text, image
provider = CharField(max_length=50) # Links to IntegrationProvider
display_name = CharField(max_length=200)
is_default = BooleanField(default=False) # One default per type
is_active = BooleanField(default=True)
# Text Model Pricing (per 1K tokens)
cost_per_1k_input = DecimalField(max_digits=10, decimal_places=6, null=True)
cost_per_1k_output = DecimalField(max_digits=10, decimal_places=6, null=True)
tokens_per_credit = IntegerField(null=True) # e.g., 1000, 10000
# Image Model Pricing
credits_per_image = IntegerField(null=True) # e.g., 1, 5, 15
quality_tier = CharField(max_length=20, null=True) # basic, quality, premium
# Model Limits
max_tokens = IntegerField(null=True)
context_window = IntegerField(null=True)
capabilities = JSONField(default=dict) # vision, function_calling, etc.
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
Credit Configuration Examples:
| Model | Type | tokens_per_credit | credits_per_image | quality_tier |
|---|---|---|---|---|
| gpt-4o | text | 1000 | - | - |
| gpt-4o-mini | text | 10000 | - | - |
| runware:97@1 | image | - | 1 | basic |
| dall-e-3 | image | - | 5 | quality |
| google:4@2 | image | - | 15 | premium |
Image Model Reference (v1.5.0 Planned):
| Model | AIR ID | Tier | Supported Dimensions |
|---|---|---|---|
| Hi Dream Full | runware:97@1 |
Basic | 1024×1024, 1280×768 |
| Bria 3.2 | bria:10@1 |
Quality | 1024×1024, 1344×768 |
| Nano Banana | google:4@2 |
Premium | 1024×1024, 1376×768 |
Helper Methods:
AIModelConfig.get_default_text_model()- Get default text modelAIModelConfig.get_default_image_model()- Get default image modelAIModelConfig.get_image_models_by_tier()- List image models by quality tier
SystemAISettings (NEW v1.4.0)
System-wide AI defaults. Singleton (pk=1).
class SystemAISettings(models.Model):
"""Per final-model-schemas.md - Renamed from GlobalIntegrationSettings"""
# AI Parameters
temperature = FloatField(default=0.7) # 0.0-2.0
max_tokens = IntegerField(default=8192)
# Image Generation Settings
image_style = CharField(max_length=30, default='photorealistic')
image_quality = CharField(max_length=20, default='standard') # standard, hd
max_images_per_article = IntegerField(default=4) # 1-8
image_size = CharField(max_length=20, default='1024x1024')
# Audit
updated_by = ForeignKey(User, null=True)
updated_at = DateTimeField(auto_now=True)
Image Style Choices:
photorealistic- Ultra realistic photographyillustration- Digital illustration3d_render- Computer generated 3Dminimal_flat- Minimal / Flat Designartistic- Artistic / Painterlycartoon- Cartoon / Stylized
Image Size Choices:
1024x1024- Square1792x1024- Landscape1024x1792- Portrait
Helper Methods:
SystemAISettings.get_instance()- Get singleton instanceSystemAISettings.get_effective_temperature(account)- Get with account overrideSystemAISettings.get_effective_image_style(account)- Get with account override
AccountSettings (Per-Account Overrides)
Generic key-value store for account-specific settings.
class AccountSettings(AccountBaseModel):
"""Per final-model-schemas.md - Account overrides"""
key = CharField(max_length=100) # Setting key
value = JSONField(default=dict) # Setting value
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
AI-Related Keys (override SystemAISettings defaults):
| Key | Type | Example | Notes |
|---|---|---|---|
ai.temperature |
float | 0.8 | Override system default |
ai.max_tokens |
int | 8192 | Override system default |
ai.image_style |
string | "illustration" | Override system default |
ai.image_quality |
string | "hd" | Override system default |
ai.max_images |
int | 6 | Override system default |
ai.image_quality_tier |
string | "premium" | User's preferred tier |
CreditCostConfig (Operation-Level Pricing)
Fixed credit costs per operation type.
class CreditCostConfig(models.Model):
"""Per final-model-schemas.md - Operation pricing"""
operation_type = CharField(max_length=50, primary_key=True) # Unique operation ID
display_name = CharField(max_length=100)
base_credits = IntegerField(default=1) # Fixed credits per operation
is_active = BooleanField(default=True)
description = TextField(blank=True)
Note: tokens_per_credit moved to AIModelConfig as of v1.4.0.
DEPRECATED Models
GlobalIntegrationSettings (DEPRECATED in v1.4.0)
Replaced by: IntegrationProvider (API keys) + AIModelConfig (model configs) + SystemAISettings (defaults)
# DEPRECATED - Do not use
class GlobalIntegrationSettings(models.Model):
# API keys now in IntegrationProvider
openai_api_key = CharField(max_length=500) # → IntegrationProvider.get_api_key('openai')
runware_api_key = CharField(max_length=500) # → IntegrationProvider.get_api_key('runware')
# Models now in AIModelConfig
openai_model = CharField(default='gpt-4o-mini') # → AIModelConfig.is_default
# Settings now in SystemAISettings
openai_temperature = FloatField(default=0.7) # → SystemAISettings.temperature
openai_max_tokens = IntegerField(default=8192) # → SystemAISettings.max_tokens
GlobalAIPrompt
class GlobalAIPrompt(models.Model):
prompt_type = CharField(max_length=100) # clustering, ideas, content_generation
prompt_value = TextField()
variables = JSONField(default=list)
is_active = BooleanField(default=True)
GlobalAuthorProfile
class GlobalAuthorProfile(models.Model):
name = CharField(max_length=255)
tone = CharField(max_length=50) # professional, casual, technical
language = CharField(max_length=10, default='en')
is_active = BooleanField(default=True)
Auth Models (igny8_core/auth/models/)
User
class User(AbstractBaseUser, PermissionsMixin):
id = UUIDField(primary_key=True)
email = EmailField(unique=True)
first_name = CharField(max_length=150)
last_name = CharField(max_length=150)
account = ForeignKey(Account, related_name='users')
role = ForeignKey(Group, null=True)
is_active = BooleanField(default=True)
is_staff = BooleanField(default=False)
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
Relations: Account (many-to-one)
Account
class Account(models.Model):
id = UUIDField(primary_key=True)
name = CharField(max_length=255)
plan = ForeignKey(Plan, null=True)
owner = ForeignKey(User, related_name='owned_accounts')
is_active = BooleanField(default=True)
created_at = DateTimeField(auto_now_add=True)
Relations: Plan (many-to-one), Users (one-to-many), Sites (one-to-many)
Site
class Site(models.Model):
id = UUIDField(primary_key=True)
name = CharField(max_length=255)
domain = CharField(max_length=255, blank=True)
account = ForeignKey(Account, related_name='sites')
industry = ForeignKey(Industry, null=True)
is_active = BooleanField(default=True)
created_at = DateTimeField(auto_now_add=True)
Relations: Account (many-to-one), Sectors (one-to-many), Industries (many-to-one)
Sector
class Sector(models.Model):
id = UUIDField(primary_key=True)
name = CharField(max_length=255)
description = TextField(blank=True)
site = ForeignKey(Site, related_name='sectors')
is_active = BooleanField(default=True)
created_at = DateTimeField(auto_now_add=True)
Relations: Site (many-to-one)
Industry
class Industry(models.Model):
id = UUIDField(primary_key=True)
name = CharField(max_length=255)
description = TextField(blank=True)
Used for: Default seed keywords, industry-specific prompts
Planner Models (igny8_core/modules/planner/models.py)
Keyword
class Keyword(models.Model):
id = UUIDField(primary_key=True)
keyword = CharField(max_length=255)
site = ForeignKey(Site, related_name='keywords')
sector = ForeignKey(Sector, null=True, related_name='keywords')
cluster = ForeignKey(Cluster, null=True, related_name='keywords')
search_volume = IntegerField(null=True)
difficulty = IntegerField(null=True)
cpc = DecimalField(null=True)
status = CharField(choices=KEYWORD_STATUS) # pending, clustered, used, archived
created_by = ForeignKey(User)
created_at = DateTimeField(auto_now_add=True)
Status Values:
pending- New, awaiting clusteringclustered- Assigned to a clusterused- Used in content ideaarchived- No longer active
Cluster
class Cluster(models.Model):
id = UUIDField(primary_key=True)
name = CharField(max_length=255)
description = TextField(blank=True)
site = ForeignKey(Site, related_name='clusters')
sector = ForeignKey(Sector, null=True, related_name='clusters')
created_by = ForeignKey(User)
created_at = DateTimeField(auto_now_add=True)
Relations: Site, Sector, Keywords (one-to-many), ContentIdeas (one-to-many)
ContentIdea
class ContentIdea(models.Model):
id = UUIDField(primary_key=True)
title = CharField(max_length=255)
description = TextField(blank=True)
site = ForeignKey(Site, related_name='ideas')
sector = ForeignKey(Sector, null=True)
cluster = ForeignKey(Cluster, related_name='ideas')
primary_keyword = ForeignKey(Keyword, related_name='primary_ideas')
secondary_keywords = ManyToManyField(Keyword, related_name='secondary_ideas')
status = CharField(choices=IDEA_STATUS) # pending, approved, used, archived
created_at = DateTimeField(auto_now_add=True)
Writer Models (igny8_core/modules/writer/models.py)
Task
class Task(models.Model):
id = UUIDField(primary_key=True)
title = CharField(max_length=255)
brief = TextField(blank=True)
site = ForeignKey(Site, related_name='tasks')
sector = ForeignKey(Sector, null=True)
idea = ForeignKey(ContentIdea, null=True, related_name='tasks')
primary_keyword = CharField(max_length=255)
secondary_keywords = JSONField(default=list)
status = CharField(choices=TASK_STATUS) # pending, in_progress, completed, cancelled
priority = CharField(choices=PRIORITY) # low, medium, high
assigned_to = ForeignKey(User, null=True)
due_date = DateField(null=True)
created_by = ForeignKey(User)
created_at = DateTimeField(auto_now_add=True)
Content
class Content(models.Model):
id = UUIDField(primary_key=True)
title = CharField(max_length=255)
body = TextField() # HTML content
excerpt = TextField(blank=True)
site = ForeignKey(Site, related_name='content')
sector = ForeignKey(Sector, null=True)
task = ForeignKey(Task, related_name='content')
meta_title = CharField(max_length=255, blank=True)
meta_description = TextField(blank=True)
# Workflow status
status = CharField(choices=CONTENT_STATUS) # draft, review, approved, published
# Publishing status (v1.3.2)
site_status = CharField(choices=SITE_STATUS) # not_published, scheduled, publishing, published, failed
scheduled_publish_at = DateTimeField(null=True)
site_status_updated_at = DateTimeField(null=True)
# External site reference
external_id = CharField(max_length=255, blank=True) # WordPress post ID
external_url = URLField(blank=True) # Published URL
word_count = IntegerField(default=0)
created_by = ForeignKey(User)
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
Workflow Status Values (status):
draft- Initial state after generationreview- Pending human reviewapproved- Ready for publishing (v1.3.2: NEW status)published- Published to WordPress
Publishing Status Values (site_status) - v1.3.2:
not_published- Not yet scheduled for external sitescheduled- Scheduled for future publishingpublishing- Currently being publishedpublished- Successfully published to external sitefailed- Publishing failed
ContentImage
class ContentImage(models.Model):
id = UUIDField(primary_key=True)
content = ForeignKey(Content, related_name='images')
url = URLField()
thumbnail_url = URLField(blank=True)
alt_text = CharField(max_length=255)
caption = TextField(blank=True)
is_featured = BooleanField(default=False)
position = IntegerField(default=0)
# AI generation metadata
prompt = TextField(blank=True)
provider = CharField(max_length=50) # dalle, runware
created_at = DateTimeField(auto_now_add=True)
Integration Models (igny8_core/business/integration/models.py)
SiteIntegration
⚠️ Note: For WordPress, Site.wp_api_key is the SINGLE source of truth for API authentication. SiteIntegration is used for sync tracking and future multi-platform support.
class SiteIntegration(models.Model):
id = UUIDField(primary_key=True)
name = CharField(max_length=255)
site = ForeignKey(Site, related_name='integrations')
platform = CharField(max_length=50) # wordpress, shopify (future)
# Configuration
external_site_url = URLField()
config_json = JSONField(default=dict) # Platform-specific settings
credentials_json = JSONField(default=dict) # Reserved for future platforms (NOT for WordPress)
# Sync Tracking
sync_enabled = BooleanField(default=True)
sync_status = CharField(max_length=50) # pending/syncing/completed/error
last_sync_at = DateTimeField(null=True)
sync_error = TextField(null=True)
# Connection
connection_status = CharField(max_length=50) # connected/error
is_active = BooleanField(default=True)
# Cached WordPress structure (from initial sync)
categories = JSONField(default=list)
tags = JSONField(default=list)
authors = JSONField(default=list)
post_types = JSONField(default=list)
structure_updated_at = DateTimeField(null=True)
created_at = DateTimeField(auto_now_add=True)
PublishingSettings (NEW v1.3.2)
class PublishingSettings(AccountBaseModel):
"""Site-level publishing configuration. Controls auto-approval, publishing limits, and scheduling."""
site = OneToOneField(Site, related_name='publishing_settings')
# Auto-approval settings
auto_approval_enabled = BooleanField(default=True)
# Auto-publish settings
auto_publish_enabled = BooleanField(default=True)
# Publishing limits
daily_publish_limit = PositiveIntegerField(default=3)
weekly_publish_limit = PositiveIntegerField(default=15)
monthly_publish_limit = PositiveIntegerField(default=50)
# Publishing schedule
publish_days = JSONField(default=['mon', 'tue', 'wed', 'thu', 'fri'])
publish_time_slots = JSONField(default=['09:00', '14:00', '18:00'])
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
Default Values:
auto_approval_enabled: True (auto-approve after review)auto_publish_enabled: True (auto-publish approved content)daily_publish_limit: 3 articles per dayweekly_publish_limit: 15 articles per weekmonthly_publish_limit: 50 articles per monthpublish_days: Monday through Fridaypublish_time_slots: 9:00 AM, 2:00 PM, 6:00 PM
Usage:
# Get or create with defaults
settings, created = PublishingSettings.get_or_create_for_site(site)
Billing Models (igny8_core/business/billing/models.py)
Plan
class Plan(models.Model):
id = UUIDField(primary_key=True)
name = CharField(max_length=100)
slug = SlugField(unique=True)
idea_credits = IntegerField(default=0)
content_credits = IntegerField(default=0)
image_credits = IntegerField(default=0)
optimization_credits = IntegerField(default=0)
max_sites = IntegerField(default=1)
max_users = IntegerField(default=1)
price_monthly = DecimalField(max_digits=10, decimal_places=2)
price_yearly = DecimalField(max_digits=10, decimal_places=2)
is_active = BooleanField(default=True)
is_internal = BooleanField(default=False)
CreditBalance
class CreditBalance(models.Model):
account = ForeignKey(Account, related_name='credit_balances')
site = ForeignKey(Site, null=True, related_name='credit_balances')
idea_credits = IntegerField(default=0)
content_credits = IntegerField(default=0)
image_credits = IntegerField(default=0)
optimization_credits = IntegerField(default=0)
period_start = DateField()
period_end = DateField()
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
CreditUsage
class CreditUsage(models.Model):
account = ForeignKey(Account, related_name='credit_usage')
site = ForeignKey(Site, null=True)
user = ForeignKey(User)
credit_type = CharField(max_length=50) # idea, content, image, optimization
amount = IntegerField()
operation = CharField(max_length=100) # generate_content, etc.
related_content_type = ForeignKey(ContentType, null=True)
related_object_id = UUIDField(null=True)
created_at = DateTimeField(auto_now_add=True)
System Models (igny8_core/modules/system/)
ModuleEnableSettings
class ModuleEnableSettings(models.Model):
account = OneToOneField(Account, primary_key=True)
planner_enabled = BooleanField(default=True)
writer_enabled = BooleanField(default=True)
linker_enabled = BooleanField(default=False)
optimizer_enabled = BooleanField(default=False)
automation_enabled = BooleanField(default=True)
integration_enabled = BooleanField(default=True)
publisher_enabled = BooleanField(default=True)
AIIntegrationSettings
class AIIntegrationSettings(models.Model):
account = ForeignKey(Account, related_name='ai_settings')
# OpenAI
openai_api_key = CharField(max_length=255, blank=True)
openai_model = CharField(max_length=50, default='gpt-4')
# Image generation
image_provider = CharField(max_length=50, default='dalle') # dalle, runware
dalle_api_key = CharField(max_length=255, blank=True)
runware_api_key = CharField(max_length=255, blank=True)
is_validated = BooleanField(default=False)
validated_at = DateTimeField(null=True)
PromptTemplate
class PromptTemplate(models.Model):
account = ForeignKey(Account, null=True) # null = system default
prompt_type = CharField(max_length=100) # auto_cluster, generate_ideas, etc.
template = TextField()
variables = JSONField(default=list)
is_active = BooleanField(default=True)
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
Publisher Models (igny8_core/modules/publisher/models.py)
PublishingRecord
class PublishingRecord(models.Model):
id = UUIDField(primary_key=True)
content = ForeignKey(Content, related_name='publishing_records')
integration = ForeignKey(SiteIntegration, related_name='publishing_records')
external_id = CharField(max_length=255) # WordPress post ID
external_url = URLField(blank=True)
status = CharField(max_length=50) # pending, published, failed
published_at = DateTimeField(null=True)
created_at = DateTimeField(auto_now_add=True)
Automation Models (igny8_core/modules/automation/models.py)
AutomationConfig
class AutomationConfig(models.Model):
site = ForeignKey(Site, related_name='automation_configs')
# Stage limits
clustering_limit = IntegerField(default=10)
ideas_limit = IntegerField(default=10)
content_limit = IntegerField(default=5)
image_limit = IntegerField(default=10)
publish_limit = IntegerField(default=5)
# Timing
delay_between_operations = IntegerField(default=5) # seconds
max_runtime = IntegerField(default=3600) # 1 hour
# Behavior
auto_approve = BooleanField(default=False)
auto_publish = BooleanField(default=False)
stop_on_error = BooleanField(default=True)
is_active = BooleanField(default=True)
AutomationRun
class AutomationRun(models.Model):
id = UUIDField(primary_key=True)
site = ForeignKey(Site, related_name='automation_runs')
config = ForeignKey(AutomationConfig)
status = CharField(max_length=50) # pending, running, paused, completed, failed, cancelled
# Progress tracking
current_stage = CharField(max_length=50, blank=True)
items_processed = IntegerField(default=0)
items_total = IntegerField(default=0)
# Timing
started_at = DateTimeField(null=True)
completed_at = DateTimeField(null=True)
# Results
error_message = TextField(blank=True)
started_by = ForeignKey(User)
created_at = DateTimeField(auto_now_add=True)
Entity Relationship Overview
Account
├── Users (many)
├── Sites (many)
│ ├── Sectors (many)
│ ├── Keywords (many)
│ ├── Clusters (many)
│ ├── ContentIdeas (many)
│ ├── Tasks (many)
│ ├── Content (many)
│ │ └── ContentImages (many)
│ ├── SiteIntegrations (many)
│ │ └── PublishingRecords (many)
│ └── AutomationConfigs (many)
│ └── AutomationRuns (many)
├── Plan (one)
├── CreditBalances (many)
├── CreditUsage (many)
├── ModuleEnableSettings (one)
├── AIIntegrationSettings (many)
└── PromptTemplates (many)