feat: dynamic main icon
This commit is contained in:
+89
-8
@@ -1,3 +1,4 @@
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.utils.text import slugify
|
||||
@@ -12,6 +13,13 @@ INSTALLABLE_PLATFORM_SPECS = (
|
||||
("source_code_url", "Source code", None),
|
||||
)
|
||||
|
||||
DISTRIBUTION_INSTALLABLE = "installable"
|
||||
DISTRIBUTION_PACKAGE = "package"
|
||||
DISTRIBUTION_CHOICES = [
|
||||
(DISTRIBUTION_INSTALLABLE, "Installable application"),
|
||||
(DISTRIBUTION_PACKAGE, "Package (external / non-installable)"),
|
||||
]
|
||||
|
||||
|
||||
class MainProduct(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
@@ -22,6 +30,12 @@ class MainProduct(models.Model):
|
||||
max_length=20, choices=CONTENT_FORMAT_CHOICES, default=FORMAT_PLAIN
|
||||
)
|
||||
image = models.ImageField(upload_to="products/main/", blank=True, null=True)
|
||||
distribution = models.CharField(
|
||||
max_length=20,
|
||||
choices=DISTRIBUTION_CHOICES,
|
||||
default=DISTRIBUTION_INSTALLABLE,
|
||||
help_text="Only affects how releases appear on the public site.",
|
||||
)
|
||||
order = models.PositiveIntegerField(default=0)
|
||||
is_active = models.BooleanField(default=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
@@ -47,14 +61,17 @@ class MainProduct(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse("products:main_product_detail", kwargs={"main_slug": self.slug})
|
||||
|
||||
def get_versions_archive_url(self):
|
||||
return reverse(
|
||||
"products:main_product_versions",
|
||||
kwargs={"main_slug": self.slug},
|
||||
)
|
||||
|
||||
|
||||
class SubProduct(models.Model):
|
||||
DISTRIBUTION_INSTALLABLE = "installable"
|
||||
DISTRIBUTION_PACKAGE = "package"
|
||||
DISTRIBUTION_CHOICES = [
|
||||
(DISTRIBUTION_INSTALLABLE, "Installable application"),
|
||||
(DISTRIBUTION_PACKAGE, "Package (external / non-installable)"),
|
||||
]
|
||||
DISTRIBUTION_INSTALLABLE = DISTRIBUTION_INSTALLABLE
|
||||
DISTRIBUTION_PACKAGE = DISTRIBUTION_PACKAGE
|
||||
DISTRIBUTION_CHOICES = DISTRIBUTION_CHOICES
|
||||
|
||||
main_product = models.ForeignKey(
|
||||
MainProduct,
|
||||
@@ -121,10 +138,19 @@ class SubProduct(models.Model):
|
||||
|
||||
|
||||
class Article(models.Model):
|
||||
main_product = models.ForeignKey(
|
||||
MainProduct,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="articles",
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
sub_product = models.ForeignKey(
|
||||
SubProduct,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="articles",
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
badge = models.CharField(
|
||||
max_length=100,
|
||||
@@ -155,20 +181,47 @@ class Article(models.Model):
|
||||
ordering = ["order", "title"]
|
||||
verbose_name = "Article"
|
||||
verbose_name_plural = "Articles"
|
||||
constraints = [
|
||||
models.CheckConstraint(
|
||||
check=(
|
||||
models.Q(sub_product__isnull=False, main_product__isnull=True)
|
||||
| models.Q(sub_product__isnull=True, main_product__isnull=False)
|
||||
),
|
||||
name="article_exactly_one_parent",
|
||||
),
|
||||
]
|
||||
|
||||
@property
|
||||
def rendered_description(self):
|
||||
return render_content(self.description, self.description_format)
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
has_main = self.main_product_id is not None
|
||||
has_sub = self.sub_product_id is not None
|
||||
if has_main == has_sub:
|
||||
raise ValidationError(
|
||||
"An article must belong to exactly one main product or sub-product."
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class SubProductVersion(models.Model):
|
||||
main_product = models.ForeignKey(
|
||||
MainProduct,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="versions",
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
sub_product = models.ForeignKey(
|
||||
SubProduct,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="versions",
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
version = models.CharField(max_length=80)
|
||||
is_featured_stable = models.BooleanField(
|
||||
@@ -189,12 +242,40 @@ class SubProductVersion(models.Model):
|
||||
|
||||
class Meta:
|
||||
ordering = ["order", "version", "pk"]
|
||||
unique_together = [["sub_product", "version"]]
|
||||
verbose_name = "Release version"
|
||||
verbose_name_plural = "Release versions"
|
||||
constraints = [
|
||||
models.CheckConstraint(
|
||||
check=(
|
||||
models.Q(sub_product__isnull=False, main_product__isnull=True)
|
||||
| models.Q(sub_product__isnull=True, main_product__isnull=False)
|
||||
),
|
||||
name="release_version_exactly_one_parent",
|
||||
),
|
||||
models.UniqueConstraint(
|
||||
fields=["sub_product", "version"],
|
||||
condition=models.Q(sub_product__isnull=False),
|
||||
name="release_version_unique_sub_product_version",
|
||||
),
|
||||
models.UniqueConstraint(
|
||||
fields=["main_product", "version"],
|
||||
condition=models.Q(main_product__isnull=False),
|
||||
name="release_version_unique_main_product_version",
|
||||
),
|
||||
]
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
has_main = self.main_product_id is not None
|
||||
has_sub = self.sub_product_id is not None
|
||||
if has_main == has_sub:
|
||||
raise ValidationError(
|
||||
"A release version must belong to exactly one main product or sub-product."
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.sub_product.name} v{self.version}"
|
||||
parent = self.sub_product or self.main_product
|
||||
return f"{parent.name} v{self.version}"
|
||||
|
||||
def install_urls_for_specs(self, specs):
|
||||
out = []
|
||||
|
||||
Reference in New Issue
Block a user