我有三个模型(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...