0

我有三个模型(URL、主机名、IPAddress)。如果我创建一个 URL save(),该类的重写方法会找到主机名并get_or_create()为主机名做。Hostname的save()方法对 IPAddress 等做类似的事情。

  • 我这样做很难吗?

我应该用它做一些继承的东西吗?我不知道继承是否可行,因为 URL 可以有一个主机名,但也可以有多个 IP 地址。继承似乎不适合这种情况。我不得不承认我还没有尝试过使用继承,但如果这是一个可能的解决方案,我愿意重写整个事情。

当我尝试添加一个具有未通过验证器的 IP 地址的 URL 时,我似乎无法得到ValidationError一直滴流的问题。validate_ip()它在 django admin 中引发异常,但不是将错误放在URL输入字段上方的好方法。而不是它给出一个 500 错误页面。

编辑:我的意思的视频捕获-> https://i.imgur.com/8W0wiWT.mp4

...snip...

def validate_ip(ip_addr):
    """Check ip_addr:
    - belongs to an ASN we manage
    - not whitelisted
    """
    asn, _, _ = lookup_asn_info(ip_addr)
    # pylint: disable=no-member
    con_objs = Constituency.objects.filter(asns__contains=[asn])
    if not con_objs:
        raise ValidationError("{} is not ours.".format(ip_addr))
    wl_networks = con_objs.values_list("ip_whitelist", flat=True).first()
    for wl_network in wl_networks:
        if wl_network.overlaps(ip_addr):
            raise ValidationError("{} is whitelisted".format(ip_addr))

...snip...

class URL(models.Model):
    """URL model."""

    url = models.CharField(max_length=2000, unique=True, verbose_name="URL")

    hostname = models.ForeignKey("Hostname", blank=True, on_delete=models.CASCADE)
    ipaddresses = models.ManyToManyField(
        "IPAddress", blank=True, verbose_name="IP Addresses"
    )

    def save(self, *args, **kwargs):
        """URL save()"""
        # Hostname
        if not hasattr(self, "hostname"):
            hostname = urlparse(self.url).hostname
            # pylint: disable=no-member
            self.hostname, _ = Hostname.objects.get_or_create(hostname=hostname)
        super().save(*args, **kwargs)
        ip_objs = self.hostname.ipaddresses.all()
        for ip_obj in ip_objs:
            ip_obj.urls.add(self)
            self.ipaddresses.add(ip_obj)  # pylint: disable=no-member
        self.hostname.urls.add(self)

    # pylint: disable=too-few-public-methods
    class Meta:
        """URL Meta."""

        verbose_name = "URL"
        verbose_name_plural = "URLs"

    def __str__(self):
        return "{}".format(self.url)

    def all_ipaddresses(self):
        """Return all IP Addresses for this ManyToManyField."""
        # pylint: disable=no-member
        return ", ".join([str(i.ipaddress) for i in self.ipaddresses.all()])


class Hostname(models.Model):
    """Hostname model."""

    hostname = models.CharField(max_length=255, unique=True)

    ipaddresses = models.ManyToManyField(
        "IPAddress", blank=True, verbose_name="IP Addresses"
    )
    urls = models.ManyToManyField(
        "URL", blank=True, related_name="+", verbose_name="URLs"
    )

    def save(self, *args, **kwargs):
        """Hostname save()"""
        # TODO: Check whitelist

        ip_objs = []
        for ip_addr in resolve_hostname(self.hostname):
            ip_obj, _ = IPAddress.objects.get_or_create(ipaddress=ip_addr)
            ip_objs.append(ip_obj)
        super().save(*args, **kwargs)
        self.ipaddresses.set(ip_objs)  # pylint: disable=no-member
        for ip_obj in ip_objs:
            ip_obj.hostnames.add(self)

    # pylint: disable=too-few-public-methods
    class Meta:
        """Hostname Meta."""

        verbose_name = "Hostname"
        verbose_name_plural = "Hostnames"

    def __str__(self):
        return "{}".format(self.hostname)

    def all_ipaddresses(self):
        """Return all IP Addresses for this ManyToManyField."""
        # pylint: disable=no-member
        return ", ".join([str(i.ipaddress) for i in self.ipaddresses.all()])

    def all_urls(self):
        """Return all URLs for this ManyToManyField."""
        # pylint: disable=no-member
        return ", ".join([i.url for i in self.urls.all()])


class IPAddress(models.Model):
    """IPAddress model."""

    ipaddress = CidrAddressField(
        verbose_name="IP Address", unique=True, validators=[validate_ip]
    )
    objects = NetManager()

    hostnames = models.ManyToManyField("Hostname", blank=True)
    urls = models.ManyToManyField("URL", blank=True, verbose_name="URLs")
    asn = models.IntegerField(blank=True, verbose_name="AS Number")
    as_name = models.CharField(max_length=255, blank=True, verbose_name="AS Name")
    as_country = models.CharField(max_length=50, blank=True, verbose_name="AS Country")

    def save(self, *args, **kwargs):
        """IPAddress save()"""
        # Fetch the ASN data for this IP address
        self.asn, self.as_country, self.as_name = lookup_asn_info(self.ipaddress)
        self.full_clean()
        super().save(*args, **kwargs)

    # pylint: disable=too-few-public-methods
    class Meta:
        """IPAddress Meta."""

        verbose_name = "IP Address"
        verbose_name_plural = "IP Addresses"

    def __str__(self):
        return "{}".format(self.ipaddress)

    def all_hostnames(self):
        """Return all Hostnames for this ManyToManyField."""
        # pylint: disable=no-member
        return ", ".join([str(i.hostname) for i in self.hostnames.all()])

    def all_urls(self):
        """Return all URLs for this ManyToManyField."""
        # pylint: disable=no-member
        return ", ".join([i.url for i in self.urls.all()])

...snip...
4

0 回答 0