from django.core.exceptions import ValidationError from django.db import models from django.utils.translation import gettext_lazy as _ from xenua.django.models import RandomSlugPKMixin class Domain(models.Model): fqdn = models.CharField( _('FQDN'), max_length=512, unique=True, ) def __str__(self): return self.fqdn @classmethod def get_from_request(cls, r): return cls.objects.get(fqdn__iexact=r.get_host()) def validate_location(loc): if "/" in loc: raise ValidationError( _('location must not contain a slash') ) class ShortLink(RandomSlugPKMixin, models.Model): class Meta: unique_together = ('domain', 'location') domain = models.ForeignKey(Domain, related_name='links', on_delete=models.CASCADE) location = models.CharField(_("short link"), max_length=100, validators=[validate_location]) to = models.URLField(_("redirect to"), max_length=2500) click_count = models.IntegerField(default=0) cache = {} def click(self): # todo: further assess performance. initial testing suggests minimal impact self.click_count += 1 self.save() def link(self): return f"{self.domain.fqdn}/{self.location}" @classmethod def get_from_request(cls, req, loc): d = Domain.get_from_request(req) return cls.objects.filter(domain=d).get(location=loc) @classmethod def hit(cls, req, loc): if r := cls.try_cache(req, loc): return r return cls.miss(req, loc) @classmethod def miss(cls, req, loc): lnk = cls.get_from_request(req, loc) cls.cache[req.get_host()][loc] = lnk.to return lnk.to @classmethod def try_cache(cls, req, loc): host = req.get_host() try: domaincache = cls.cache[host] try: return domaincache[loc] except KeyError: cls.miss(req, loc) except KeyError: cls.cache[host] = {} return ''