1. from django.db import models
    
  2. from django.db.models.fields.related import ForwardManyToOneDescriptor
    
  3. from django.utils.translation import get_language
    
  4. 
    
  5. 
    
  6. class ArticleTranslationDescriptor(ForwardManyToOneDescriptor):
    
  7.     """
    
  8.     The set of articletranslation should not set any local fields.
    
  9.     """
    
  10. 
    
  11.     def __set__(self, instance, value):
    
  12.         if instance is None:
    
  13.             raise AttributeError("%s must be accessed via instance" % self.field.name)
    
  14.         self.field.set_cached_value(instance, value)
    
  15.         if value is not None and not self.field.remote_field.multiple:
    
  16.             self.field.remote_field.set_cached_value(value, instance)
    
  17. 
    
  18. 
    
  19. class ColConstraint:
    
  20.     # Anything with as_sql() method works in get_extra_restriction().
    
  21.     def __init__(self, alias, col, value):
    
  22.         self.alias, self.col, self.value = alias, col, value
    
  23. 
    
  24.     def as_sql(self, compiler, connection):
    
  25.         qn = compiler.quote_name_unless_alias
    
  26.         return "%s.%s = %%s" % (qn(self.alias), qn(self.col)), [self.value]
    
  27. 
    
  28. 
    
  29. class ActiveTranslationField(models.ForeignObject):
    
  30.     """
    
  31.     This field will allow querying and fetching the currently active translation
    
  32.     for Article from ArticleTranslation.
    
  33.     """
    
  34. 
    
  35.     requires_unique_target = False
    
  36. 
    
  37.     def get_extra_restriction(self, alias, related_alias):
    
  38.         return ColConstraint(alias, "lang", get_language())
    
  39. 
    
  40.     def get_extra_descriptor_filter(self, instance):
    
  41.         return {"lang": get_language()}
    
  42. 
    
  43.     def contribute_to_class(self, cls, name):
    
  44.         super().contribute_to_class(cls, name)
    
  45.         setattr(cls, self.name, ArticleTranslationDescriptor(self))
    
  46. 
    
  47. 
    
  48. class ActiveTranslationFieldWithQ(ActiveTranslationField):
    
  49.     def get_extra_descriptor_filter(self, instance):
    
  50.         return models.Q(lang=get_language())
    
  51. 
    
  52. 
    
  53. class Article(models.Model):
    
  54.     active_translation = ActiveTranslationField(
    
  55.         "ArticleTranslation",
    
  56.         from_fields=["id"],
    
  57.         to_fields=["article"],
    
  58.         related_name="+",
    
  59.         on_delete=models.CASCADE,
    
  60.         null=True,
    
  61.     )
    
  62.     active_translation_q = ActiveTranslationFieldWithQ(
    
  63.         "ArticleTranslation",
    
  64.         from_fields=["id"],
    
  65.         to_fields=["article"],
    
  66.         related_name="+",
    
  67.         on_delete=models.CASCADE,
    
  68.         null=True,
    
  69.     )
    
  70.     pub_date = models.DateField()
    
  71. 
    
  72.     def __str__(self):
    
  73.         try:
    
  74.             return self.active_translation.title
    
  75.         except ArticleTranslation.DoesNotExist:
    
  76.             return "[No translation found]"
    
  77. 
    
  78. 
    
  79. class NewsArticle(Article):
    
  80.     pass
    
  81. 
    
  82. 
    
  83. class ArticleTranslation(models.Model):
    
  84.     article = models.ForeignKey(Article, models.CASCADE)
    
  85.     lang = models.CharField(max_length=2)
    
  86.     title = models.CharField(max_length=100)
    
  87.     body = models.TextField()
    
  88.     abstract = models.TextField(null=True)
    
  89. 
    
  90.     class Meta:
    
  91.         unique_together = ("article", "lang")
    
  92. 
    
  93. 
    
  94. class ArticleTag(models.Model):
    
  95.     article = models.ForeignKey(
    
  96.         Article,
    
  97.         models.CASCADE,
    
  98.         related_name="tags",
    
  99.         related_query_name="tag",
    
  100.     )
    
  101.     name = models.CharField(max_length=255)
    
  102. 
    
  103. 
    
  104. class ArticleIdea(models.Model):
    
  105.     articles = models.ManyToManyField(
    
  106.         Article,
    
  107.         related_name="ideas",
    
  108.         related_query_name="idea_things",
    
  109.     )
    
  110.     name = models.CharField(max_length=255)