保存时跳过字段(Django 模型、插入和更新)

Posted

技术标签:

【中文标题】保存时跳过字段(Django 模型、插入和更新)【英文标题】:Skipping a field on save (Django models, Insert and Update) 【发布时间】:2015-04-20 21:25:22 【问题描述】:

给定 PostgreSQL 9.2.10、Django 1.8、python 2.7.5,以下模型:

class soapProdAPI(models.Model):
    soap_id = models.PositiveIntegerField(primary_key=True)
    soap_host = models.CharField(max_length=20)
    soap_ip = models.GenericIPAddressField(default='0.0.0.0')
    soap_asset = models.CharField(max_length=20)
    soap_state = models.CharField(max_length=20)

还有如下代码:

tableProdSoap = soapProdQuery()


@periodic_task(run_every=timedelta(minutes=2))
def saveSoapProd():
    tableProdSoap = soapProdQuery()
    if tableProdSoap != None:
        for item in tableProdSoap:
            commit = soapProdAPI(soap_id=item[0], soap_host=item[1], soap_asset=item[2], soap_state=item[3])
            commit.save()
    saveSoapNullIP()

回答 Josué Padilla 的问题:

@task
def saveSoapNullIP():
    missingIP = soapProdAPI.objects.filter(soap_ip='0.0.0.0')
    if missingIP:
        for record in missingIP:
            if str(record.soap_host).lower().startswith('1a'):
                fqdn = str(record.soap_host) + 'stringvaluehere'
            elif str(record.soap_host).lower().startswith('1b'):
                fqdn = str(record.soap_host) + 'stringvaluehere'
            elif str(record.soap_host).lower().startswith('1c'):
                fqdn = str(record.soap_host) + 'stringvaluehere'
            else:
                fqdn = str(record.soap_host) + 'stringvaluehere'
            try:
                hostIp = check_output('host %s' % fqdn, shell=True)
                hostIp = hostIp.split()[-1]
            except:
                hostIp = '0.0.0.0'
            record.soap_ip = hostIp
            record.save(update_fields=['soap_ip'])

我的 soapProdQ​​uery 只返回模型中有第 5 个字段的这 4 个字段 (soap_ip)。我知道这可能不是 最好的 方法,但我有一个单独的代码块在数据库中查询 soap_ip 中的 None 值,在它们上运行一个子进程主机并将其保存回来使用IP地址(每次通过时返回/更新的行数应该变小,而不是将用于进行主机查找的逻辑放入请求中/这个芹菜任务本身将运行每个API请求。我已经尝试过了,它需要 FOREVER 才能返回完整的数据。)。我查询的soap API 不提供IP,否则我显然会以这种方式获取它。这一切都作为后台任务运行,使用 celery 使其对网络用户不可见/无缝。

我遇到的问题是,每次运行 saveSoapProd() 时,它都会用 '0.0.0.0' 覆盖以前的 soap_ip 字段,从而否定我的其他函数的工作。另一个问题是我不能force_insertforce_update 因为我需要这两个功能。我的问题是:有没有办法同时选择性地更新/插入并完全排除每次saveSoapProd() 运行时对soap_ip 执行任何操作?非常感谢任何和所有帮助。提前谢谢你。

** 编辑 1 **

我可能在update_or_create 或get_or_create 中找到了解决方案,也可能没有,但是我不确定具体的用法。文档让我有些困惑。

** 编辑 2 **

我猜 get_or_create 是失败的。作品首先通过,但之后的每次保存都失败了:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "<console>", line 8, in saveSoapProd
  File "/lib/python2.7/site-packages/django/db/models/base.py", line 690, in save
    % ', '.join(non_model_fields))
ValueError: The following fields do not exist in this model or are m2m fields: soap_id

代码如下:

@periodic_task(run_every=timedelta(minutes=2))
def saveSoapProd():
    tableProdSoap = soapProdQuery()
    if tableProdSoap != None:
        for item in tableProdSoap:
            obj, created = soapProdAPI.objects.get_or_create(soap_id=item[0], defaults='soap_host': item[1], 'soap_asset': item[2], 'soap_state': item[3])
            if created == False:
                commit = soapProdAPI(soap_id=item[0], soap_host=item[1], soap_asset=item[2], soap_state=item[3])
                commit.save(update_fields=['soap_id', 'soap_host', 'soap_asset', 'soap_state'])

老实说,我不完全确定导致此错误的原因。


** 编辑 3/当前解决方案 **

我能够通过修改我的模型和我的任务功能来解决我自己的问题。该解决方案使用 get_or_create,但您可以从提供的解决方案中轻松推断如何使用 update_or_create。请参阅下面的选定答案以获取编码示例。


** TLDR **

我想做一个 .save() ,它可能需要插入新记录或更新更改的记录没有触摸soap_ip 字段(没有insert_only 或@987654340 @)。

【问题讨论】:

saveSoapNullIP() 有什么作用?每次函数运行时都会执行它。这样可以吗? @JosuéPadilla 我在 saveSoapNullIP() 中添加了供您查看。这可能不是最好的做事方式,但它比直接放在任务本身或 api 调用中要好。这至少使我必须更新的记录数量应该减少每次通过。 【参考方案1】:

我不知道您是否已经知道这一点,但是您可以覆盖模型的 save() 函数。

class soapProdAPI(models.Model):
    soap_id = models.PositiveIntegerField(primary_key=True)
    soap_host = models.CharField(max_length=20)
    soap_ip = models.GenericIPAddressField(default='0.0.0.0')
    soap_asset = models.CharField(max_length=20)
    soap_state = models.CharField(max_length=20)

    # Override save
    def save(self, *args, **kwargs):
        if self.soap_ip != '0.0.0.0':
          self.soap_ip = your_ip # Here you can  get your old IP an save that instead of 0.0.0.0

编辑

你得到了

ValueError: The following fields do not exist in this model or are m2m fields: soap_id

因为您正在尝试更新soap_id,该字段被定义为您的模型的主键,因此在更新时它是不可变的。这就是为什么当你这样做时它会崩溃:

commit.save(update_fields=['soap_id', 'soap_host', 'soap_asset', 'soap_state'])

尝试从update_fields 中删除soap_id

【讨论】:

我知道并考虑了它作为我的选择,但我认为有一种方便的方法或可能的另一种方法可以在不重新定义模型方法的情况下做到这一点。不过感谢您的建议。【参考方案2】:

通过对我的模型进行以下更改,在不修改保存方法的情况下解决了我自己的问题:

class soapProdAPI(models.Model):
    soap_id = models.PositiveIntegerField(unique=True, null=False)
    soap_host = models.CharField(max_length=20)
    soap_ip = models.GenericIPAddressField(default='0.0.0.0')
    soap_asset = models.CharField(max_length=20)
    soap_state = models.CharField(max_length=20)

还有我的任务:

def saveSoapProd():
    tableProdSoap = soapProdQuery()
    if tableProdSoap != None:
        for item in tableProdSoap:
            try:
                obj, created = soapProdAPI.objects.get_or_create(soap_id=item[0], defaults='soap_host': item[1], 'soap_asset': item[2], 'soap_state': item[3])
                if created == False:
                    obj.soap_host  = item[1]
                    obj.soap_asset = item[2]
                    obj.soap_state = item[3]
                    obj.save(update_fields=['soap_host', 'soap_asset', 'soap_state'])
            except:
                continue
    saveSoapMissingIP()

编辑

刚刚注意到 Josué Padilla 的回应,这实际上是我用这个答案解决的问题的一部分。感谢 Josué 提供的所有帮助。

【讨论】:

以上是关于保存时跳过字段(Django 模型、插入和更新)的主要内容,如果未能解决你的问题,请参考以下文章

导入 CSV 时跳过标题或第一行

java hibernate在插入时跳过插入'id'

使用 vba 添加新记录时跳过空白字段

Django:更新 X 数量模型的表单

Django 表单字段验证 - 如何判断操作是插入还是更新?

通知在更新时跳过另一个通知