Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/309.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 根据外键计算的字段,需要保存两次吗?_Python_Django - Fatal编程技术网

Python 根据外键计算的字段,需要保存两次吗?

Python 根据外键计算的字段,需要保存两次吗?,python,django,Python,Django,我有一个这样的模型: class IPv4Pool(models.Model): name = models.CharField(max_length=50) available_ips = models.IntegerField() def save(self, *args, **kwargs): super(IPv4Pool, self).save(*args, **kwargs) self.available_ips = 0

我有一个这样的模型:

class IPv4Pool(models.Model):
    name = models.CharField(max_length=50)
    available_ips = models.IntegerField()

    def save(self, *args, **kwargs):
        super(IPv4Pool, self).save(*args, **kwargs)
        self.available_ips = 0
        for ip_range in self.ip_range_set.all():
            print str(ip_range)
            self.available_ips += len(ip_range)
由于self.available_ips是从foreignkey计算出来的,在保存模型之前不会分配它们,至少我认为可用_ips的值是在第二次保存后才计算出来的

什么是优雅的蟒蛇式的方法?我应该在计算完值后再次调用save吗

当前方法的问题

您不应该有这样计算的数据库字段。这可能会导致数据库中的数据不一致问题

以这个场景为例:

>>> pool = IPv4Pool.objects.get(pk=1)
>>> pool.available_ips
2
>>> range = IPRange()
>>> range.pool = poll
>>> range.save()
>>> pool.available_ips
2
没有人更新可用的IP,因此现在数据库中的数据不一致。有3个IPRange行指向该池,但池认为只有2行

更好的方法

您应该将模型更新为以下内容:

class IPv4Pool(models.Model):
    name = models.CharField(max_length=255)

class IPRange(models.Model):
    pool = models.ForeignKey(IPv4Pool, related_name='ip_ranges')
    ...

class Subscriber(models.Model):
    pool = models.ForeignKey(IPv4Pool, related_name='subscribers')
    ...
并使用Django ORM的功能查询此数据:

pool.ip_ranges.count()
如果您确实希望IPv4Pool类上有可用的\u ips属性,请使用以下命令:

class IPv4Pool(models.Model):
    name = models.CharField(max_length=50)

    @property
    def available_ips(self):
        return self.ip_ranges.count()
但对我来说,这似乎并没有什么好处

处理复杂查询

根据您对此答案的评论,您希望能够根据相关对象的计数筛选IPv4Pool对象。您还希望能够在这些查询中使用对象,这是明智的。这可以使用djangoapi实现

例如,假设您需要此查询:

给我所有订户数小于范围数的池 你需要这个特别的咒语:

IPv4Pool.objects.annotate(available_ips=Count('ip_ranges'),
                          num_subscribers=Count('subscribers'),
                 ).filter(num_subscribers__lt=F('available_ips'))
如果每次键入都过于冗长,您可以添加一个,它会自动将这些注释添加到每个查询中:

class AnnotatedIPv4PoolManager(models.Manager):
    def get_queryset(self):
        query = super(AnnotatedIPv4PoolManager, self).get_queryset()
        return query.annotate(available_ips=Count('ip_ranges'),
                              num_subscribers=Count('subscribers'),
                              )

class IPv4Pool(models.Model):
    name = models.CharField(max_length=255)
    annotated = AnnotatedIPv4PoolManager()
然后,您可以这样使用它,以达到与上面相同的效果:

IPv4Pool.annotated.filter(num_subscribers__lt=F('available_ips'))

无论如何,我看不出在第一次保存时,如何让任何ip_范围对象指向这个。必须有人通过将FK指向此对象来添加它们,当然,这只能在保存对象后才能很好地进行。谢谢,使用@property效果很好。因为我不会有很多FK,所以实时计算它们而不是存储在DB上就足够了。不管你有多少FK,存储这种信息永远都是不正确的。数据库的工作就是为您完成这项工作。解决性能问题的方法是为该键创建一个数据库索引,这样数据库就不必实际执行所有数据检查,只需在索引中查找该值。这与您执行相同操作的方法之间的区别在于,数据库将永远不会不一致。我发现采用这种方法存在的一个问题是,在F表达式上不允许定义为@property的字段:num\u subscribers\uu lt=F'available\u networks'。有解决方法吗?您是否也有一个订阅者模型,具有FK到IPv4池?是的,有一个订阅者,具有FK到IPv4和IPv6池,并且IPv4池具有FK到IP_范围。