You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中APScheduler任务报错:NoneType is not iterable 求排查

Django + APScheduler 定时任务触发 TypeError: NoneType 不可迭代问题排查

问题场景

执行命令python manage.py runserver 127.0.0.1:8002时,APScheduler定时任务BackgroundClass.update_escalation(触发规则:interval[0:01:00])抛出异常,最终错误为TypeError: argument of type 'NoneType' is not iterable。最初怀疑是requestall=Escalation.objects.all()返回None,但无法确定具体原因,附上相关代码及错误栈。

错误栈追踪

Job "BackgroundClass.update_escalation (trigger: interval[0:01:00], next run at: 2023-07-18 14:18:08 UTC)" raised an exception
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/apscheduler/executors/base.py", line 125, in run_job
    retval = job.func(*job.args, **job.kwargs)
  File "/app/BackgroundClass.py", line 25, in update_escalation
    if len(requestall)>0:
  File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 376, in __len__
    self._fetch_all()
  File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 1867, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 87, in __iter__
    results = compiler.execute_sql(
  File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1385, in execute_sql
    sql, params = self.as_sql()
  File "/usr/local/lib/python3.8/site-packages/mssql/compiler.py", line 223, in as_sql
    supports_offset_clause = self.connection.sql_server_version >= 2012
  File "/usr/local/lib/python3.8/site-packages/django/utils/functional.py", line 57, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/usr/local/lib/python3.8/site-packages/mssql/base.py", line 465, in sql_server_version
    with self.temporary_connection() as cursor:
  File "/usr/local/lib/python3.8/contextlib.py", line 113, in __enter__
    return next(self.gen)
  File "/usr/local/lib/python3.8/site-packages/django/db/backends/base/base.py", line 694, in temporary_connection
    with self.cursor() as cursor:
  File "/usr/local/lib/python3.8/site-packages/django/utils/asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/django/db/backends/base/base.py", line 323, in cursor
    return self._cursor()
  File "/usr/local/lib/python3.8/site-packages/mssql/base.py", line 258, in _cursor
    conn = super()._cursor()
  File "/usr/local/lib/python3.8/site-packages/django/db/backends/base/base.py", line 299, in _cursor
    self.ensure_connection()
  File "/usr/local/lib/python3.8/site-packages/django/utils/asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/django/db/backends/base/base.py", line 282, in ensure_connection
    self.connect()
  File "/usr/local/lib/python3.8/site-packages/django/utils/asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/django/db/backends/base/base.py", line 263, in connect
    self.connection = self.get_new_connection(conn_params)
  File "/usr/local/lib/python3.8/site-packages/mssql/base.py", line 336, in get_new_connection
    connstr = encode_connection_string(cstr_parts)
  File "/usr/local/lib/python3.8/site-packages/mssql/base.py", line 54, in encode_connection_string
    return ';'.join(
  File "/usr/local/lib/python3.8/site-packages/mssql/base.py", line 55, in <genexpr>
    '%s=%s' % (k, encode_value(v))
  File "/usr/local/lib/python3.8/site-packages/mssql/base.py", line 79, in encode_value
    if ';' in v or v.strip(' ').startswith('{'):
TypeError: argument of type 'NoneType' is not iterable

相关代码

BackgroundClass.update_escalation代码

class BackgroundClass:

    @staticmethod
    def update_escalation():
        startdate = datetime.datetime.utcnow()
        enddate = startdate - datetime.timedelta(hours=2)
        Time_threshold = timezone.now()-timezone.timedelta(hours=2)
        logger.info(f"Got Time_threshold {Time_threshold} successfully")
        
        request_list=[]
        
        logger.info(f"Geting all escalation objects")
    
        requestall=Escalation.objects.all()
        
        logger.info(f"Got all escalation objects")
        
        # if ((requestall is None) or (len(requestall) == 0)):
        #     logger.info(f"No escalation requests")

        if len(requestall)>0:
            for item in requestall:
                print(item)
                if update_status("%s" % item.request_id)==True:
                    request_list.append(item.request_id)
            logger.info(f"Got request_list with true {request_list} successfully")

Escalation模型类代码

class Escalation(models.Model):

    field1= models.AutoField(primary_key=True)
    field2= models.CharField(unique=True, max_length=255, db_collation='SQL_Latin1_General_CP1_CI_AS',blank=True)
    field3= models.CharField(max_length=255, db_collation='SQL_Latin1_General_CP1_CI_AS', blank=True,unique=True,null=False) 
    choices = [('R1', _('Executive/VP Request')), ('R2', _('Past Contractual SLA')),('R3', _('Billing Dispute'))]
    field4= models.CharField(max_length=255, choices=choices, blank=True)
    request_reason= models.CharField(max_length=255, db_collation='SQL_Latin1_General_CP1_CI_AS', blank=True)
    field5 = models.IntegerField(blank=True)
    field6 = DateTimeUTCField(auto_now_add=True)
    field7 = models.CharField(max_length=255, db_collation='SQL_Latin1_General_CP1_CI_AS',default='open') #editable = False, 
    field8 = models.DateTimeField(blank=True, null=True)
    field9 = models.CharField(max_length=255, db_collation='SQL_Latin1_General_CP1_CI_AS',null=True,blank=True)


    def save(self, *args, **kwargs):
        if self.request_level=='R1' :
            self.priority = 1
            self.request_reason = _('Reason1')
            super(Escalation, self).save(*args, **kwargs)
        if self.request_level=='R2' :
            self.priority = 1
            self.request_reason = _('Reason2')    
            super(Escalation, self).save(*args, **kwargs)
        if self.request_level=='R3' :
            self.priority = 2
            self.request_reason = _('Reason3')    
            super(Escalation, self).save(*args, **kwargs)

    class Meta:
        managed = False
        db_table = 'uat_escalation' 
        verbose_name = _('Prioritized request')

问题分析

  • 排除Escalation.objects.all()返回None的可能:Django的QuerySet对象永远不会是None,即便数据库中没有匹配数据,也会返回空的QuerySet。
  • 错误根源在数据库连接层:错误栈最终指向mssql/base.py的encode_value函数,当尝试判断';' in v时,变量v为None类型,导致"不可迭代"的TypeError。这说明数据库配置中的某个参数值为None,在构建SQL Server连接字符串时无法正常处理。

解决建议

  1. 检查数据库配置完整性:打开Django的settings.py文件,查看DATABASES配置项,确保所有必填参数(如NAME、USER、PASSWORD、HOST、PORT等)都有有效值,没有出现值为None或空字符串的情况。
  2. 验证环境变量加载:如果数据库配置通过环境变量注入(如os.getenv('DB_NAME')),确认环境变量已正确设置并加载,避免因变量缺失导致参数值为None。
  3. 排查连接参数:可以临时在代码中打印数据库配置,明确哪个参数异常:
    from django.conf import settings
    print(settings.DATABASES['default'])  # 替换为你的数据库别名
    
  4. APScheduler任务初始化检查:确保APScheduler在运行定时任务时,已正确初始化Django的数据库环境。如果使用多线程/进程执行任务,需保证每个线程/进程都能正确获取数据库配置。
  5. 优化QuerySet判断逻辑:将if len(requestall)>0:替换为if requestall.exists(),后者不会触发全量数据查询,仅执行EXISTS SQL语句,性能更优。

内容的提问来源于stack exchange,提问作者fsssaiyan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 04:52:03