Несколько объектов модели сохраняются в db, хотя просмотр экранирует MyModel.DoesNotExist, нарушая работу приложения

Я получаю следующую ошибку, пытаясь получить профиль пользователя из mongodb:

New searches for user 555555555
[{'search_term': 'ibm'}, {'search_term': 'ry'}]
Internal Server Error: /users/555555555
Traceback (most recent call last):
  File "/home/cchilders/.local/lib/python3.10/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/home/cchilders/.local/lib/python3.10/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/cchilders/.local/lib/python3.10/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/home/cchilders/projects/stocks_backend/users/views.py", line 50, in get_user_profile
    user = UserProfile.objects.get(user_id=user_id)
  File "/home/cchilders/.local/lib/python3.10/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/cchilders/.local/lib/python3.10/site-packages/django/db/models/query.py", line 433, in get
    raise self.model.MultipleObjectsReturned(
users.models.UserProfile.MultipleObjectsReturned: get() returned more than one UserProfile -- it returned 2!
[21/Aug/2022 09:45:31] "POST /users/555555555 HTTP/1.1" 500 76920

Наличие нескольких профилей пользователей нарушает работу приложения, и для каждого пользователя должен быть только один. Я также получаю дубликаты в другой модели StockInfo.

Мое представление, похоже, сохраняет нового пользователя только тогда, когда для этого идентификатора пользователя не существует ни одного, поэтому я запутался

users/views.py:

from django.shortcuts import render
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt

import json

from helpers.view_functions import parse_request_body
from .models import UserProfile


@csrf_exempt
def get_user_profile(request, user_id):
    if request.method == 'GET':
        try:
            user = UserProfile.objects.get(user_id=user_id)
            print("user found")
        except UserProfile.DoesNotExist:
            print("user does not exist exception")
            profile = UserProfile()
            profile.user_id = user_id
            profile.searches = [
                {'search_term': 'hd'},
                {'search_term': 'wba'},
            ]
            profile.display_settings = [
                {'setting_name': 'showYieldChange', 'visible': True},
                {'setting_name': 'showAllDividends', 'visible': True},
            ]
            profile.save()
            print("user saved in db")
            user = UserProfile.objects.get(user_id=user_id)
        except Exception as error:
            print("got an unknown exception:")
            print(error)

        data = {
            'user_id': user.user_id,
            'searches': user.searches,
            'display_settings': user.display_settings
        }
        json_data = json.dumps(data)
        return HttpResponse({json_data}, content_type='application/json')

    if request.method == 'POST':
        body = parse_request_body(request)
        searches = body['searches']
        searches_objects = [{'search_term': x} for x in searches]
        print("New searches for user {user_id}".format(user_id=user_id))
        print(searches_objects)
        user = UserProfile.objects.get(user_id=user_id)
        user.searches = searches_objects
        user.display_settings = body['display_settings']
        user.save()
        return HttpResponse("it worked")

если это имеет значение, то вот users/models.py:

from djongo import models


class RecentSearch(models.Model):
    search_term = models.CharField(max_length=100)

    class Meta:
        abstract = True


class DisplaySetting(models.Model):
    setting_name = models.CharField(max_length=150)
    visible = models.BooleanField()

    class Meta:
        abstract = True


class UserProfile(models.Model):
    user_id = models.CharField(max_length=255)
    searches = models.ArrayField(model_container=RecentSearch, null=True)
    display_settings = models.ArrayField(model_container=DisplaySetting, null=True)

    objects = models.DjongoManager()

Является ли user = UserProfile.objects.get(user_id=user_id) асинхронным и не возвращается вовремя для проверки в операторе try? Я бы не ожидал, что в Python возникнут условия гонки, поскольку я не использую никаких ключевых слов async.

Мне нужно, чтобы это представление никогда не сохраняло дублирующий профиль пользователя после создания первого профиля

В вашем django ПРИЛОЖЕНИИ (не проекте)

  • создайте файл с именем signals.py
  • .
  • Перейдите к файлу "apps.py" и добавьте следующий ready() метод к классу.
class MyApplicationConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'myapplication'

    def ready(self):
        import myapplication.signals

^^^ Сам класс является лишь примером, метод ready является самоочевидным

  • Затем мы перейдем к созданию сигнала -> зайдите в файл signals.py
  • .
from django.dispatch import receiver
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from myapplication.models import Userprofile

# ^ Nescessary imports to create a signal to be sent after User creation.

@receiver(post_save, sender=User)
def create_profile_signal(
        sender, # The User class specified above
        instance, # The actual user instance that was edited
        created, # self explanatory
        **kwargs # Nescessary kwargs.
    ):
    if created: # Assign the current user's instance to the userprofile to create one.
        profile = UserProfile.objects.create()
        profile.user_id = instance.id
        profile.searches = [
            {'search_term': 'hd'},
            {'search_term': 'wba'},
        ]
        profile.display_settings = [
            {'setting_name': 'showYieldChange', 'visible': True},
            {'setting_name': 'showAllDividends', 'visible': True},
        ]
        profile.save()
  • Затем вам необходимо полностью перезапустить ваш проект django (остановить и запустить встроенный runerver), django не умеет распознавать вновь созданные файлы.

Что теперь? Каждый раз, когда пользователь регистрируется в вашем django проекте, автоматически создается userprofile. Вам не нужно делать это самостоятельно. Никаких хлопот с представлениями и прочим. Вы, конечно, можете также установить значения по умолчанию, как и раньше.

Вопрос от меня к вам:

Есть ли конкретная причина, по которой вы не используете поле OneToOne, привязанное к пользователю?

Вернуться на верх