Building my first Django app, and I'm running into a snag. I have a Django model that creates Job objects, and I want each job code to be unique and auto-generated, with a particular format. The format is: aaaMMnnYYYY, where aaa is a 3-letter client identifier that we set, nn is a counter that represents the nth job from that client in that month., and MM and YYYY are month and year respectively. e.g., for the 3rd job from client "AIE" in feb 2023, the ID would be AIE02032023.
Using a calculated property with the @property decorator causes the field to be updated with every save, so I'm trying to do this by modifying the save() method. There's also a related Cost object that has a Job attribute as a Foreign Key. The way I have it now, the job code gets assigned as expected, but when I add a Cost to the Job, the 'iterating' part of the job code iterates, changing the job code, which causes uniqueness errors as well as URL errors (I'm using the job code in the URLConf. Is there any way to have this field get calculated once and then never change?
As a side note, I'd also like to be able to override the job code. Is there a way to set flags within a model, such as job_code_overridden = False, etc.?
Here's the relevant code, let me know what else you need to see.
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,
})
Lastly, in the save() method I know I could do something like
if job_code != get_job_code():
job_code = get_job_code()
..but the job 'month' often changes throughout the life of the job, and if I run get_job_code() after the month changes then the job code will change again, which is undesirable.
In the end I simply overrode the
save()
method, using ajob_code_is_fixed
boolean flag to keep thejob_code
from updating.