Data Models
The AV application uses Django ORM with PostgreSQL for transactional data. All models use UUID primary keys and follow a multi-tenant architecture with workspaces and projects.
Architecture Overview
The system implements a multi-tenant SaaS architecture where:
- Each Workspace is an isolated tenant container
- Projects belong to workspaces and have specific types (ASO Android, ASO Apple)
- Users can belong to multiple workspaces with different roles
- Access control is managed at both workspace and project levels
Core Models
CustomUser
Custom user model extending Django's AbstractUser with UUID primary keys.
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| EmailField | Unique email (used for authentication) | |
| username | CharField | Username |
| first_name | CharField | First name |
| last_name | CharField | Last name |
| date_joined | DateTimeField | Account creation timestamp |
Workspace
Main tenant container for multi-tenancy.
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| name | CharField | Workspace name |
| owner | ForeignKey | Reference to CustomUser (owner) |
| created_at | DateTimeField | Creation timestamp |
| updated_at | DateTimeField | Last update timestamp |
Methods:
- get_projects_for_user(user) - Returns all projects accessible to a user in this workspace
WorkspaceUser
Links users to workspaces with specific roles.
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| workspace | ForeignKey | Reference to Workspace |
| user | ForeignKey | Reference to CustomUser |
| role | CharField | User role (OWNER, ADMIN, VIEWER) |
Role Permissions: - OWNER: Full control over workspace and all projects - ADMIN: Can manage workspace settings and users - VIEWER: Read-only access to workspace
Project
Base project model that can be specialized for different project types.
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| name | CharField | Project name |
| workspace | ForeignKey | Reference to Workspace |
| type | CharField | Project type (ASO_ANDROID, ASO_APPLE) |
Methods:
- user_has_access(user) - Returns True if user can access this project
ProjectUserAccess
Grants specific users access to projects within a workspace.
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| workspace_user | ForeignKey | Reference to WorkspaceUser |
| project | ForeignKey | Reference to Project |
| role | CharField | Project role (ADMIN, VIEWER) |
Android ASO Models
AndroidApp
Represents an Android application from Google Play Store.
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| name | CharField | App name |
| package_id | CharField | Unique package identifier (e.g., com.example.app) |
AndroidAppCountryLanguage
Represents app localization for a specific country and language.
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| app | ForeignKey | Reference to AndroidApp |
| country | CharField | ISO 2-letter country code |
| language | CharField | ISO language code |
AndroidAppKeyword
Keywords tracked for a specific Android app localization.
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| acl | ForeignKey | Reference to AndroidAppCountryLanguage |
| text | CharField | Keyword text |
ASOAndroidProject
Android ASO project configuration for tracking app performance across multiple markets.
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| project | OneToOneField | Reference to base Project |
| app | ForeignKey | Reference to AndroidApp |
| markets | ManyToMany | Country-language markets being tracked for this app |
| keywords | ManyToMany | Keywords to track across all markets |
| competitors | ManyToMany | Competitor app markets to monitor (in same markets as tracked markets) |
| notes | TextField | Optional notes |
Apple ASO Models
AppleApp
Represents an Apple application from App Store.
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| name | CharField | App name |
| bundle_id | CharField | Unique bundle identifier (e.g., com.example.app) |
AppleAppCountryLanguage
Represents app localization for a specific country and language.
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| app | ForeignKey | Reference to AppleApp |
| country | CharField | ISO 2-letter country code |
| language | CharField | ISO language code |
AppleAppKeyword
Keywords tracked for a specific Apple app localization.
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| acl | ForeignKey | Reference to AppleAppCountryLanguage |
| text | CharField | Keyword text |
ASOAppleProject
Apple ASO project configuration for tracking app performance across multiple markets.
| Field | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| project | OneToOneField | Reference to base Project |
| app | ForeignKey | Reference to AppleApp |
| markets | ManyToMany | Country-language markets being tracked for this app |
| keywords | ManyToMany | Keywords to track across all markets |
| competitors | ManyToMany | Competitor app markets to monitor (in same markets as tracked markets) |
| notes | TextField | Optional notes |
Access Control
The system implements a two-level access control system:
Workspace Level
Users can have one of three roles in a workspace:
- OWNER - Full control, automatic access to all projects
- ADMIN - Can manage workspace settings and users
- VIEWER - Read-only access to workspace
Project Level
Non-owner users need explicit project access:
- ADMIN - Full control over the project
- VIEWER - Read-only access to the project
Access Rules
- Workspace owners automatically have access to all projects in their workspace
- Other users must be workspace members first
- Project access is granted through
ProjectUserAccessentries - A user must be a workspace member before they can be granted project access
Usage Examples
Creating a Workspace
from workspaces.models import Workspace
from users.models import CustomUser
user = CustomUser.objects.get(email='[email protected]')
workspace = Workspace.objects.create(
name='Marketing Agency',
owner=user
)
Creating an Android ASO Project
from workspaces.models import (
Project, ProjectType, AndroidApp,
AndroidAppCountryLanguage, AndroidAppKeyword, ASOAndroidProject
)
# Create or get Android app
app = AndroidApp.objects.create(
name='My App',
package_id='com.example.myapp'
)
# Create localizations for markets you want to track
us_acl = AndroidAppCountryLanguage.objects.create(
app=app,
country='US',
language='en'
)
de_acl = AndroidAppCountryLanguage.objects.create(
app=app,
country='DE',
language='de'
)
# Create keywords for each market
us_keyword1 = AndroidAppKeyword.objects.create(
acl=us_acl,
text='messaging app'
)
us_keyword2 = AndroidAppKeyword.objects.create(
acl=us_acl,
text='secure messenger'
)
# Create project
project = Project.objects.create(
name='My App ASO',
workspace=workspace,
type=ProjectType.ASO_ANDROID
)
# Add ASO configuration with M2M relationships
aso_config = ASOAndroidProject.objects.create(
project=project,
app=app
)
# Link markets and keywords to project
aso_config.markets.set([us_acl, de_acl])
aso_config.keywords.set([us_keyword1, us_keyword2])
Adding Users to Workspace
from workspaces.models import WorkspaceUser, WorkspaceRole
# Add user as admin
workspace_user = WorkspaceUser.objects.create(
workspace=workspace,
user=new_user,
role=WorkspaceRole.ADMIN
)
Granting Project Access
from workspaces.models import ProjectUserAccess, ProjectRole
# Grant viewer access to project
ProjectUserAccess.objects.create(
workspace_user=workspace_user,
project=project,
role=ProjectRole.VIEWER
)
Checking Access
# Check if user has access to project
if project.user_has_access(user):
# User can access project
pass
# Get all accessible projects in workspace
projects = workspace.get_projects_for_user(user)
Database Schema
All models use UUID primary keys and include timestamps (created_at, updated_at) through the BaseModel abstract class.
Relationships
- Workspace → Project: One-to-Many
- Workspace → WorkspaceUser: One-to-Many
- Project → ASOAndroidProject: One-to-One
- Project → ASOAppleProject: One-to-One
- AndroidApp → AndroidAppCountryLanguage: One-to-Many
- AppleApp → AppleAppCountryLanguage: One-to-Many
- AndroidAppCountryLanguage → AndroidAppKeyword: One-to-Many
- AppleAppCountryLanguage → AppleAppKeyword: One-to-Many
Migrations
The models are defined in two Django apps:
users- Contains CustomUser modelworkspaces- Contains all workspace and project models
To apply migrations: