Models e Schema¶
Documentação dos models do projeto.
ERD (Entity Relationship Diagram)¶
erDiagram
User ||--o{ HymnBook : owns
User ||--o{ HymnBookVersion : uploads
User ||--o{ Favorite : has
User ||--o{ Comment : writes
User ||--o{ Follow : follows
User ||--o{ Notification : receives
HymnBook ||--|{ Hymn : contains
HymnBook ||--o{ HymnBookVersion : versions
Hymn ||--o{ Favorite : has
Hymn ||--o{ Comment : has
User {
uuid id PK
string email UK
string username UK
text bio
image avatar
datetime created_at
}
HymnBook {
uuid id PK
string name UK
string slug UK
string intro_name
string owner_name
uuid owner_user_id FK
image cover_image
text description
datetime created_at
}
Hymn {
uuid id PK
uuid hymn_book_id FK
int number
string title
text text
date received_at
string offered_to
string style
text extra_instructions
string repetitions
}
HymnBookVersion {
uuid id PK
uuid original_id FK
uuid uploaded_by_id FK
int version_number
jsonb changes
string status
datetime created_at
}
Favorite {
uuid id PK
uuid user_id FK
uuid hymn_id FK
datetime created_at
}
Comment {
uuid id PK
uuid user_id FK
uuid hymn_id FK
text text
datetime created_at
}
Follow {
uuid id PK
uuid follower_id FK
uuid following_id FK
datetime created_at
}
Notification {
uuid id PK
uuid user_id FK
string type
jsonb data
bool read
datetime created_at
}
Models Detalhados¶
HymnBook¶
class HymnBook(models.Model):
"""Hinário - coleção de hinos."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
# Identificação
name = models.CharField(max_length=255, unique=True, db_index=True)
intro_name = models.CharField(max_length=100, blank=True)
slug = models.SlugField(unique=True, max_length=255)
# Proprietário
owner_name = models.CharField(max_length=255)
owner_user = models.ForeignKey(
'users.User',
on_delete=models.SET_NULL,
null=True, blank=True,
related_name='owned_hymnbooks'
)
# Mídia e descrição
cover_image = models.ImageField(upload_to='hymn_covers/', blank=True)
description = models.TextField(blank=True)
# Timestamps
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['name']
indexes = [
models.Index(fields=['name']),
models.Index(fields=['owner_name']),
]
Campos:
| Campo | Tipo | Descrição |
|---|---|---|
name |
CharField | Nome único do hinário |
slug |
SlugField | Auto-gerado de name |
intro_name |
CharField | Nome curto para exibição |
owner_name |
CharField | Texto livre - quem recebeu |
owner_user |
FK User | Usuário dono (opcional) |
cover_image |
ImageField | Capa do hinário |
description |
TextField | Descrição completa |
Hymn¶
class Hymn(models.Model):
"""Hino individual dentro de um hinário."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
hymn_book = models.ForeignKey(
HymnBook,
on_delete=models.CASCADE,
related_name='hymns'
)
# Identificação
number = models.PositiveIntegerField()
title = models.CharField(max_length=255, db_index=True)
# Conteúdo
text = models.TextField()
# Metadados
received_at = models.DateField(null=True, blank=True)
offered_to = models.CharField(max_length=255, blank=True)
style = models.CharField(max_length=50, blank=True)
extra_instructions = models.TextField(blank=True)
repetitions = models.CharField(max_length=100, blank=True)
class Meta:
ordering = ['hymn_book', 'number']
unique_together = [['hymn_book', 'number']]
Constraints:
unique_together(hymn_book, number)- Número único por hinárioCASCADE delete- Deleta hinos quando hinário é deletado
User (Custom)¶
class User(AbstractUser):
"""Usuário customizado com campos extras."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
bio = models.TextField(blank=True)
avatar = models.ImageField(upload_to='avatars/', blank=True)
HymnBookVersion¶
class HymnBookVersion(models.Model):
"""Versão de um hinário para tracking de mudanças."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
original = models.ForeignKey(HymnBook, on_delete=models.CASCADE)
uploaded_by = models.ForeignKey(User, on_delete=models.SET_NULL)
version_number = models.PositiveIntegerField()
changes = models.JSONField(default=dict)
status = models.CharField(max_length=20, choices=STATUS_CHOICES)
Favorite¶
class Favorite(models.Model):
"""Hino favoritado por usuário."""
user = models.ForeignKey(User, on_delete=models.CASCADE)
hymn = models.ForeignKey(Hymn, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = [['user', 'hymn']]
Comment¶
class Comment(models.Model):
"""Comentário em um hino."""
user = models.ForeignKey(User, on_delete=models.CASCADE)
hymn = models.ForeignKey(Hymn, on_delete=models.CASCADE)
text = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
TypeSense Schema¶
HYMNS_SCHEMA = {
'name': 'hymns',
'fields': [
{'name': 'id', 'type': 'string'},
{'name': 'hymn_book_id', 'type': 'string'},
{'name': 'hymn_book_name', 'type': 'string', 'facet': True},
{'name': 'hymn_book_slug', 'type': 'string'},
{'name': 'owner_name', 'type': 'string', 'facet': True},
{'name': 'number', 'type': 'int32', 'sort': True},
{'name': 'title', 'type': 'string'},
{'name': 'text', 'type': 'string'},
{'name': 'style', 'type': 'string', 'facet': True, 'optional': True},
],
'default_sorting_field': 'number'
}
Facets: hymn_book_name, owner_name, style
Sortable: number
Searchable: title, text
Migrations¶
Ver histórico em:
apps/hymns/migrations/apps/users/migrations/