Как использовать get_or_create? Ошибка: get() вернул более одного пациента — он вернул 7

#python #django

#python #django

Вопрос:

У меня есть функция для fetch API, где я создаю объект модели Django для каждого объекта в JSON и сохраняю данные в модели django. Проблема здесь в том, что каждый раз, когда я вызываю маршрут, он создает записи снова и снова, потому что я использую метод create , но я провел исследование, что лучший способ остановить это — использовать get_or_create . Итак, я попробовал этот метод, но, похоже, я что-то пропустил, потому что я получил сообщение об ошибке: feedback.models.Пациент.MultipleObjectsReturned: get() вернул более одного пациента — он вернул 7!

это мой код до того, как у меня будет 2 цикла for, поэтому я могу перебирать каждого пациента, а затем каждую роль и сохранять пациента с ролью:

 # FetchApi for Patients def fetchapi_patients(request):
    url = 'http://localhost:8000/core/users/roles/patient'
    headers={"Authorization":"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Imhha2FuQGdvLmNvbSIsImV4cCI6MTYxMDAxMTM0MSwib3JpZ19pYXQiOjE2MTAwMDc3NDF9.k2204d094DNUbEfFt8M_7chukOSjWWwfesPOH5jIiP8"}
    response = requests.get(url, headers=headers)

    #Read the JSON
    patients = response.json()

    #Create a Django model object for each object in the JSON and store the data in django model (in database)
    for patient in patients:
            Patient.objects.create(first_name=patient['first_name'], last_name=patient['last_name'], email=patient['email'], coreapi_id=patient['ID'])
            for role in patient['Roles']:
                Role.objects.create(role_name=role['name'])

    return JsonResponse({'patients': patients})
 

это когда я попытался использовать метод get_or_create:

 for patient in patients:
            patientDatabase, created = Patient.objects.get_or_create(first_name=patient['first_name'], last_name=patient['last_name'], email=patient['email'], coreapi_id=patient['ID'])
            for role in patient['Roles']:
                Role.objects.get_or_create(role_name=role['name'])
                

    return JsonResponse({'patients': patients})
 

это мой models.py:

 class Patient(models.Model):
    coreapi_id = models.CharField(max_length=100)
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    email = models.EmailField(max_length=100)
    language = models.CharField(max_length=20)
    created_date = models.DateTimeField(auto_now_add=True)

    def str(self):
        return self.email

class Role(models.Model):
    role_id = models.ManyToManyField(Patient)
    role_name = models.CharField(max_length=100)

    def __str__(self):
        return self.role_name
 

Ошибка обратной трассировки:

 Traceback (most recent call last):   File "/usr/lib/python3/dist-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)   File "/usr/lib/python3/dist-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)   File "/usr/lib/python3/dist-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)   File "/home/stela/feedbacksystem/feedback/views.py", line 88, in fetchapi_patients
    patientDatabase, created = Patient.objects.get_or_create(first_name=patient['first_name'], last_name=patient['last_name'], email=patient['email'], coreapi_id=patient['ID'])   File "/usr/lib/python3/dist-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)   File "/usr/lib/python3/dist-packages/django/db/models/query.py", line 538, in get_or_create
    return self.get(**kwargs), False   File "/usr/lib/python3/dist-packages/django/db/models/query.py", line 410, in get
    raise self.model.MultipleObjectsReturned( feedback.models.Patient.MultipleObjectsReturned: get() returned more than one Patient -- it returned 7!
 

Я прочитал официальную документацию, но есть некоторые недоразумения, которые мне не очень понятны в методе get_or_create, например, я хочу проверять пользователей только по их электронной почте, потому что я прочитал, что это должно быть что-то «уникальное», поэтому get_or_create проверяет каждое поле в моей модели или ..? Я надеюсь, что мой вопрос понятен, если это не так, пожалуйста, дайте мне знать

Ответ №1:

Просто есть несколько объектов с одинаковым ( first_name , last_name , email , coreapi_id ) и get_or_create отказывается что-либо делать, поскольку это, вероятно, логическая проблема.

То же самое произошло бы с just .get() (что .get_or_create() действительно так и есть).

Если вы хотите, например, получить только пациента, используя coreapi_id поле, но если пациента с такими данными не существует, создайте их, используя другие поля, вы захотите использовать defaults аргумент:

 patientDatabase, created = Patient.objects.get_or_create(
    coreapi_id=patient["ID"],
    defaults=dict(
        first_name=patient["first_name"],
        last_name=patient["last_name"],
        email=patient["email"],
    ),
)
 

Комментарии:

1. Итак, в вашем примере моим уникальным полем будет ‘ID’, а остальные аргументы по умолчанию? Это имеет смысл, я попробовал ваш пример, но все равно получаю ту же ошибку. Также, когда я перебираю их роль, нужно ли мне также get_or_create?

2. Это зависит от вашей модели данных. В любом случае, если вы уже запускали .create() с одними и теми же параметрами несколько раз, ваша база данных, естественно, будет иметь несколько копий одной и той же строки. Вам нужно будет это очистить (вручную?) Первый.

3. Какая глупая ошибка, это моя вина! Спасибо за быстрый ответ, я отметил ваш ответ

4. Хотя на мой предыдущий вопрос в комментариях к ролям мне нужно написать их отдельно и применить тот же метод, верно?

5. Во-первых, я нахожу очень странным, что вызывается поле «многие ко многим», связывающее роли с пациентами role_id . Я думаю, что так и должно быть patients (предполагая, что у одного пациента может быть более одной роли; если нет, то объект Patient должен иметь внешний ключ к роли). После этого Role.objects.get_or_create(role_name=role['name'])[0].patients.add(patient) будет обеспечена связь.

Ответ №2:

метод get_ot_create должен использоваться только тогда, когда что-то уникально.

в вашем случае существует несколько объектов, которые не являются уникальными, поэтому он возвращает несколько объектов, но он должен возвращать только один объект