Добавление дополнительной пары ключ-значение к объектам в списке перед передачей его в шаблон - Работает, но я не уверен, почему

Новичок в 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
Вернуться на верх