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

Django中传递变量作为relativedelta关键字参数触发TypeError求助

问题:Django信号接收器中relativedelta传参报错TypeError: keywords must be strings

我在Django项目的post_save信号接收器create_forecasted里,尝试用变量作为关键字参数传给relativedelta方法,核心代码如下:

@receiver(post_save, sender=BudgetTransaction)
def create_forecasted(sender, instance, created, **kwargs):
    if instance.transaction_type == "EC":
        return True

    if created:
        today = instance.next_date
        last_day = date(today.year, 12, 31)
        this_month = today.month
        weeks_left = int((last_day - today).days / 7)
        fortnights_left = int(weeks_left / 2)
        months_left = 12 - this_month

        if instance.frequency == 'weeks':
            create_range = weeks_left
        elif instance.frequency == 'fort':
            create_range = fortnights_left
        else:
            create_range = months_left

        loop = 1
        for i in range(create_range):
            if instance.frequency == 'fort':
                true_frequency = 'weeks'
                true_loop = loop * 2
            elif instance.frequency == '4week':
                true_frequency = 'weeks'
                true_loop = loop * 4
            else:
                true_frequency = instance.frequency
                true_loop = loop

            ### 问题出在这行 ###
            next_month = today + relativedelta(**{true_frequency: true_loop})
            date_string = next_month.strftime("%Y-%m-%d")

            BudgetTransaction.objects.bulk_create([BudgetTransaction(
                owner=instance.owner,
                transaction_amount=instance.transaction_amount,
                transaction_name=instance.transaction_name,
                transaction_type=instance.transaction_type,
                next_date=date_string,
                frequency=instance.frequency,
            )])

            loop += 1

代码在网站运行时正常,但执行测试时触发错误:

Error
Traceback (most recent call last):
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\bf_app\tests\test_models.py", line 26, in test_transaction_has_owner
    transaction = TransactionFactory(owner=user)
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\venv\lib\site-packages\factory\base.py", line 40, in __call__
    return cls.create(**kwargs)
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\venv\lib\site-packages\factory\base.py", line 528, in create
    return cls._generate(enums.CREATE_STRATEGY, kwargs)
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\venv\lib\site-packages\factory\django.py", line 117, in _generate
    return super()._generate(strategy, params)
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\venv\lib\site-packages\factory\base.py", line 465, in _generate
    return step.build()
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\venv\lib\site-packages\factory\builder.py", line 262, in build
    instance = self.factory_meta.instantiate(
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\venv\lib\site-packages\factory\base.py", line 317, in instantiate
    return self.factory._create(model, *args, **kwargs)
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\venv\lib\site-packages\factory\django.py", line 166, in _create
    return manager.create(*args, **kwargs)
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\venv\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\venv\lib\site-packages\django\db\models\query.py", line 514, in create
    obj.save(force_insert=True, using=self.db)
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\venv\lib\site-packages\django\db\models\base.py", line 806, in save
    self.save_base(
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\venv\lib\site-packages\django\db\models\base.py", line 872, in save_base
    post_save.send(
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\venv\lib\site-packages\django\dispatch\dispatcher.py", line 176, in send
    return [
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\venv\lib\site-packages\django\dispatch\dispatcher.py", line 177, in <listcomp>
    (receiver, receiver(signal=self, sender=sender, **named))
  File "C:\Users\Mitchell\PycharmProjects\BudgieFinance_TW\bf_app\models.py", line 82, in create_forecasted
    next_month = next_date + relativedelta(**{true_frequency: true_loop})
TypeError: keywords must be strings

解决方案

错误原因

测试场景下,true_frequency变量可能不是字符串类型,或者TransactionFactory生成的frequency字段值不符合预期,导致传给relativedelta的关键字参数不是合法字符串。relativedelta要求关键字参数是weeks、months这类字符串。

修复步骤

  1. 确保true_frequency是合法字符串
    新增校验逻辑,强制转换类型并过滤无效参数;
  2. 检查测试工厂配置
    确保TransactionFactory生成的frequency字段是合法字符串(比如'weeks'、'fort'、'4week'),不要传入非字符串类型。

修改后的代码片段

# 在循环内添加校验逻辑
if instance.frequency == 'fort':
    true_frequency = 'weeks'
    true_loop = loop * 2
elif instance.frequency == '4week':
    true_frequency = 'weeks'
    true_loop = loop * 4
else:
    true_frequency = instance.frequency
    true_loop = loop

# 新增:确保true_frequency是字符串且为relativedelta支持的参数
valid_relativedelta_keys = {'weeks', 'months'}
# 强制转换为字符串,避免非字符串类型
true_frequency = str(true_frequency)
if true_frequency not in valid_relativedelta_keys:
    # 跳过无效情况,避免报错
    loop += 1
    continue

# 正常计算日期
next_month = today + relativedelta(**{true_frequency: true_loop})

另外,检查你的TransactionFactory,确保frequency字段的默认值或传入值都是字符串,比如:

class TransactionFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = BudgetTransaction
    
    # 确保frequency是字符串类型
    frequency = factory.Faker('word', ext_word_list=['weeks', 'fort', '4week'])
    # 其他字段...

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 17:16:00