phase 1 backup - support for multi-file versioned upload

This commit is contained in:
2026-08-03 22:46:22 -05:00
parent 5e58b8a183
commit f09acedbcf
14 changed files with 375 additions and 91 deletions
+34 -4
View File
@@ -201,12 +201,9 @@ class TagList(models.Model):
class Version(models.Model):
"""A tracked-file release. Every upload is stored; latest = newest created_at."""
"""A release holding one or more VersionFile entries. Latest = newest."""
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='versions')
version_name = models.CharField(max_length=64)
file = models.ForeignKey(
FileIndex, on_delete=models.SET_NULL, null=True, blank=True, related_name='project_version',
)
changelog = models.TextField(blank=True, default='')
downloads = models.PositiveIntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
@@ -218,6 +215,39 @@ class Version(models.Model):
return f'{self.project.title} {self.version_name}'
class VersionFile(models.Model):
"""One file belonging to a Version release. A version can hold several
files (e.g. multiple skins), each with its own download."""
KIND_CHOICES = [
('release', 'Release'),
('markdown', 'Markdown'),
('png', 'PNG'),
]
version = models.ForeignKey(Version, on_delete=models.CASCADE, related_name='files')
file = models.ForeignKey(FileIndex, on_delete=models.CASCADE, related_name='version_file')
kind = models.CharField(max_length=16, choices=KIND_CHOICES, default='release')
note = models.CharField(max_length=255, blank=True, default='')
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['created_at', 'pk']
def __str__(self):
return f'{self.version}{self.file.original_filename}'
@property
def filename(self):
return self.file.original_filename
@property
def is_markdown(self):
return self.kind == 'markdown'
@property
def is_png(self):
return self.kind == 'png'
class ProjectAsset(models.Model):
"""Untracked gallery media (image/video/gif) displayed on the project page."""
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='assets')