尝试在 Django 模型中创建一次性计算字段

问题描述 投票:0回答:1

构建我的第一个 Django 应用程序,但我遇到了障碍。我有一个创建 Job 对象的 Django 模型,我希望每个作业代码都是唯一的、自动生成的,具有特定的格式。格式为:aaaMMnnYYYY,其中aaa是我们设置的3个字母的客户标识符,nn是一个计数器,代表该客户在该月的第n个工作。MM和YYYY分别是月份和年份。 例如,对于 2023 年 2 月客户“AIE”的第 3 份工作,ID 为 AIE02032023。

将计算属性与 @property 装饰器一起使用会导致每次保存时都会更新该字段,因此我尝试通过修改 save() 方法来实现此目的。还有一个相关的成本对象,其作业属性作为外键。 按照我现在的方式,作业代码按预期分配,但是当我向作业添加成本时,作业代码的“迭代”部分会迭代,更改作业代码,这会导致唯一性错误以及 URL错误(我在 URLConf 中使用职位代码。 有什么方法可以让这个字段计算一次然后永远不会改变吗?

顺便说一句,我还希望能够覆盖职位代码。有没有办法在模型中设置标志,例如 job_code_overridden = False 等?

这是相关代码,让我知道你还需要看什么。

models.py:

class Job(models.Model):
    
    job_name = models.CharField(max_length=50, default='New Job')
    client = models.ForeignKey(Client, on_delete=models.CASCADE)
    job_code = models.CharField(max_length=15, unique=True,)

    def get_job_code(self):
        '''
        I only want this to run once
        Format abcMMnnYYYY

        '''
        jc = ''
        prefix = self.client.job_code_prefix
        month = str(str(self.job_date).split('-')[1])
        identifier = len(Job.objects.filter(job_date__contains = f'-{month}-',
                                    client__job_code_prefix = prefix)) + 2
        year = str(str(self.job_date).split('-')[0])
        jc = f'{prefix}{month}{identifier:02d}{year}'

        return jc


    @property
    def total_cost(self):
        all_costs = Cost.objects.filter(job__job_code = self.job_code)
        total = 0
        if all_costs:
            for cost in all_costs:
                total += cost.amount
        return total

        # Is there a way to add something like the flags in the commented-out code here?
    def save(self, *args, **kwargs):
        # if not self.job_code_fixed:
        if self.job_code != self.get_job_code():
             self.job_code = self.get_job_code()
             # self.job_code_fixed = True
        super().save(*args, **kwargs)

costsheet.py:

class costsheetView(ListView):
    template_name = "main_app/costsheet.html"
    form_class = CostForm
    model = Cost
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        current_job_code = self.kwargs['job_code']
        currentJob = Job.objects.get(job_code=current_job_code)
        return context

    def get(self, request, *args, **kwargs):
        cost_form = self.form_class()
        current_job_code = self.kwargs['job_code']
        currentJob = Job.objects.get(job_code=current_job_code)
        all_costs = Cost.objects.filter(job__job_code = current_job_code)
        return render(request, self.template_name, {'cost_form':cost_form, 'currentJob':currentJob,'all_costs':all_costs})

    def post(self, request, *args, **kwargs):
        cost_form = self.form_class(request.POST)
        current_job_code = self.kwargs['job_code']
        currentJob = Job.objects.get(job_code=current_job_code)
        messages = []
        errors = ''
        if cost_form.is_valid():
            instance = cost_form.save()
            instance.job = currentJob
            instance.save()
            currentJob.vendors.add(instance.vendor)
            currentJob.save()
            messages.append(f'cost added, job date: {currentJob.job_date}')
        else: 
            print('oops')
            print(cost_form.errors)
            errors = cost_form.errors

        all_costs = Cost.objects.filter(job__job_code = current_job_code)
        return render(request, self.template_name, {'cost_form':cost_form, 
                                                             'currentJob':currentJob, 
                                                    'errors':errors, 
                                                    'messages':messages,
                                                    'all_costs':all_costs,
                                                    })

最后,在 save() 方法中我知道我可以做类似的事情

if job_code != get_job_code():
   job_code = get_job_code()

..但是工作“月份”在工作的整个生命周期中经常发生变化,如果我在月份更改后运行 get_job_code() ,那么工作代码将再次更改,这是不可取的。

python django debugging web-applications django-4.1
1个回答
0
投票

最后我只是重写了

save()
方法,使用
job_code_is_fixed
布尔标志来防止
job_code
更新。

class Job(models.Model):
    #...
    job_code = models.CharField(
        max_length=15, unique=True, blank=True, null=True
        )
    job_code_is_fixed = models.BooleanField(default=False)
    #...

    def set_job_code(self, prefix=None, year=None, month=None):
        '''
        Create a job code in the following format:
        {prefix}{month:02d}{i:02d}{year}

        e.g. APL06012023:
        prefix: APL
        month: 06
        i: 01 
        year: 2023

        params:
        prefix: job code prefix
        year: the year to be used in the job code
        month: the month to be used in the job code
        i: iterator for generating unique job codes
        passable args are auto generated, so they default to None unless passed in.

        This function will run in the save() method as long as job_code_is_fixed is False
        '''

        jc = ''
        prefix = self.client.job_code_prefix if prefix is None else prefix
        month = int(timezone.now().month) if month is None else month
        year = int(timezone.now().year) if year is None else year

        # the 'iterating' part of the code iterates based on the highest job number from
        # the same client in the same month
        i = 1
        while jc == '' and i <= 99:
            if not Job.objects.filter(job_code=f'{prefix}{month:02d}{i:02d}{year}').exists():
                jc = f'{prefix}{month:02d}{i:02d}{year}'
                return jc
            else:
                i += 1

        if jc == '':
            # Handle error

    def save(self, *args, **kwargs):
    # Auto-generate the job code
        if not self.job_code_is_fixed:
            self.job_code = self.set_job_code()
            self.job_code_is_fixed = True
        # ...
        super().save(*args, **kwargs)
© www.soinside.com 2019 - 2024. All rights reserved.