Добавление дополнительной пары ключ-значение к объектам в списке перед передачей его в шаблон - Работает, но я не уверен, почему
Новичок в Django и я хотел добавить пару ключ:значение к каждому объекту Job в Queryset/списке перед передачей его в шаблон. Исследования здесь говорят, что вы не можете просто добавить поле, а скорее должны создать новый dict или, возможно, добавить атрибуты или что-то еще, но возившись, я добился того, что это отлично работает, но логически кажется, что это не должно работать, и я беспокоюсь, что могу столкнуться с проблемами или несоответствием данных где-то.
Мое представление "jobs" получает все задания, которые в настоящее время не назначены пользователю, отфильтровывает задания, основываясь на текущем списке навыков пользователя (пользовательское поле для моей модели пользователя), а затем функция, которую я добавляю, проверяет расписание текущего пользователя и добавляет значение "conflict":True
, если расписание конфликтует, чтобы я мог выделить его красным цветом при выводе списка заданий на экран.
views.py (сокращенно):
def jobs (request):
//get all unassigned jobs, and create a list of only jobs where the user has matching skills
available = Job.objects.filter(assigned_to__isnull=True).order_by('start_time').all()
available = [job for job in available if request.user.is_skilled(job)]
//go through each job, make it a dict, and add "conflict":True to it if scheudle confict
for job in available:
if not request.user.is_available(job):
job = job.__dict__
job["conflict"] = True
//run custom serializer() to format each field and return list of jobs
return JsonResponse({'available': [job.serialize() for job in available]})
Часть, которая не имеет смысла для меня, это job.__dict__
должна преобразовывать объект Job в dict, поэтому я фактически ожидал получить ошибку при попытке выполнить job.serialize()
. На самом деле, есть другой вид, где я пытаюсь сделать то же самое с одним объектом Job (не списком или Queryset), и он выдает ошибку Dict does not contain method serialize()
. А если я сначала не конвертирую в dict, то получаю ошибку TypeError: 'Job' object does not support item assignment
. Но почему-то при работе со списком преобразование не происходит, а у моего объекта Job теперь есть новое поле "conflict", которое я могу проверить с помощью job.conflict
в JS, шаблонах и в методе модели serialize():
models.py (сокращенно):
class Job (models.Model):
title = models.CharField(max_length=50)
description = models.CharField(max_length=500)
start_time = models.DateTimeField()
end_time = models.DateTimeField()
skillsets = models.ManyToManyField(Skillset, related_name="jobs")
def serialize(self):
return {
'id': self.id,
'title': self.title,
'description': self.description,
'start_time': self.start_time.strftime("%b %d %Y, %I:%M %p"),
'end_time': self.end_time.strftime("%b %d %Y, %I:%M %p"),
'skillsets': [skill.title for skill in self.skillsets.all()],
'conflict': self.conflict if 'conflict' in self.__dict__ else False,
}
Вопрос в том, почему available
содержит список только объектов Job после того, как я должен был преобразовать некоторые из них в Dict? Позже я снова делаю это на объекте User, чтобы выделить пользователей, которые доступны для конкретной работы, и то же самое, несмотря на то, что я якобы преобразовал в Dict и добавил поле, конечным результатом является список объектов User с добавленным полем available
. Я ожидал, что assignable
будет списком диктов, а не списком пользовательских объектов:
Также в views.py:
def job_details (request, job_id):
//get a single Job, and list of users who are specialist, then filter out users who don't have required skills
job = Job.objects.get(pk=job_id)
users = User.objects.filter(user_type=User.SPECIALIST)
users = list(filter(lambda usr: usr.is_skilled(job), users))
//for each user, check their schedule against this job, and add "available":True/False as determined
for usr in users:
if (usr.is_available(job)):
usr = usr.__dict__
usr["available"] = True
else:
usr = usr.__dict__
usr["available"] = False
//return job and users to be rendered on screen, colored based on their availability
return render(request, "jobs/job_details.html", {
'job': job.serialize(),
'assignable': users,
'conflict': True if request.user.user_type == User.SPECIALIST and not request.user.is_available(job) else False})
Есть ли логическое объяснение? Это потому, что я делаю это в цикле for, что преобразование не происходит, но поле добавляется к объектам User/Job, как будто оно преобразуется обратно после выхода из цикла for?
Чтобы ответить на ваш непосредственный вопрос - оператор usr = usr.__dict__
, выполняемый при итерации по списку, просто присваивает переменную usr
. Оно не изменяет элемент в исходном списке. Для преобразования списка экземпляров модели в dicts можно использовать функцию map
, или list comprehension. Например:
users = list(map(lambda usr: {**usr.__dict__, "available": usr.is_available(job)}, users))
BTW, возможно, немного лучшим подходом было бы установить атрибут "available" прямо на экземпляре модели, а затем обращаться к нему в методе serialize
через getattr: getattr(self, "available", False)
.
Или вы можете пойти дальше и добавить available
как python-свойство к классу модели. Тогда вы сможете документировать его значение и использование:
class User(models.Model):
...
@property
def available(self):
"""Document the meaning..."""
return getattr(self, "_available", False)
@available.setter
def available(self, value):
self._available = value