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

