Недостатки метода сохранения клиента, позволяющего избежать получения пользователем userprofile_id с определенным значением. (Django/Heroku)

В моем Django-проекте мне нужно избежать создания пользователями userprofile_id со значением 3 (3, 13, 203...).

Это временное решение другой проблемы, которое, вероятно, потребует больше времени для достижения результата.

Я разместил код ниже.

Но мой вопрос больше относится к недостаткам этого промежуточного исправления.

Какие проблемы это может породить. Вот список, который я составил, и мои ответы на каждый из них:

  1. User_id будет отличаться от userprofile_id - не вижу здесь никаких проблем.
  2. Влияние на целостность базы данных при удалении пользовательского метода - я не вижу, как удаление метода может повлиять на базу данных. Надеюсь, я прав;
  3. .
  4. Некоторые проблемы с производительностью - Сохранение происходит на уровне базы данных, что может вызвать некоторые задержки, я полагаю. Это для пилота+. Я не ожидаю 1000 новых пользователей в день. Могут ли два пользователя, зарегистрировавшиеся в одно и то же время, получить одинаковый userprofile_id? Должно ли быть ожидание, чтобы ограничить возможность тупиковых ситуаций?

код для тех, кому интересно:

class UserProfile(models.Model):
    user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)

    def __str__(self):
        return str(self.user)
    
    def save(self, *args, **kwargs):
        if not self.pk:
            # Logic for generating userprofile_id without '3'
            last_userprofile = UserProfile.objects.all().order_by('-id').first()
            print(f'last_userprofile: {last_userprofile.id}')
            if last_userprofile:
                last_id = last_userprofile.id
                print(f'last_id: {last_id}')
                next_id = last_id + 1
                print(f'next_id: {next_id}')
                while '3' in str(next_id):
                    next_id += 1
                self.id = next_id
            else:
                self.id = 1 

Ответ 1 Различные идентификаторы пользователей и идентификаторы профилей пользователей не должны иметь значения, если вы следите за ними. На самом деле, мне нравится идея использования другого идентификатора пользователя на бэкенде, который просто представляет собой число, а не идентификатор пользователя, который он создал. Я делаю это на своих сайтах, чтобы использовать файлы cookie, которые защищают конфиденциальность пользователей. Я обращаюсь к ним только по их номеру, никогда не называя их по имени, которое они мне дали.

Ответ 2 Я не вижу никаких проблем с базой данных, но помните, что преимущество Django в том, что мы можем генерировать базу данных SQL с помощью моделей. В производстве нам может понадобиться изменить нашу базу данных для увеличения функциональности или масштаба, но в разработке я рекомендую убедиться, что Django правильно генерирует SQL-таблицу, прежде чем пытаться ее использовать. Поэтому, возможно, убедитесь, что ваша база данных SQL генерируется правильно из новой базы данных при вызове migrate.

Ответ 3. Ну, строго говоря, программа на Python, скомпилированная с помощью CPython и фактически выполняющаяся как машинный код на языке C, не приведет к тупику процессора. В принципе, эти проблемы решаются на гораздо более низком уровне с помощью конвейеризации и параллелизма.

Вы можете попросить двух пользователей отправить один и тот же идентификатор пользователя одновременно, один из них будет создан первым, а другой столкнется с ошибкой. Обработайте эту ошибку в блоке try/except и используйте HTTPResponseRedirect, чтобы направить пользователя обратно на страницу, где ему будет предложено повторить попытку с другим идентификатором пользователя.

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

Это происходит из-за того, как метод save решает, делать ли запрос на обновление или вставку. Из документации следует, что Django делает следующее:

  • Если атрибут первичного ключа объекта установлен в значение, которое оценивается как True (т.е. значение, отличное от None или пустой строки), Django выполняет UPDATE.
  • Если атрибут первичного ключа объекта не установлен или UPDATE ничего не обновил (например, если первичный ключ установлен в значение, которое не существует в базе данных), Django выполняет INSERT.

Учитывая, что вы сами установили первичный ключ, возможно, что:

  1. Начинается запрос 1 на создание профиля пользователя для одного пользователя.
  2. Начинается запрос 2 на создание профиля пользователя для другого пользователя.
  3. Оба получают один и тот же вычисленный идентификатор.
  4. Один из них заканчивает вставку первым.
  5. Другой заканчивает обновление только что вставленной строки.

Это приводит к тому, что два пользователя имеют один и тот же профиль. Вы можете обойти эту проблему, заставив Django выполнить вставку, установив аргумент ключевого слова force_insert в True:

def save(self, *args, **kwargs):
    if not self.pk:
        # Logic for generating userprofile_id without '3'
        last_userprofile = UserProfile.objects.all().order_by('-id').first()
        print(f'last_userprofile: {last_userprofile.id}')
        if last_userprofile:
            last_id = last_userprofile.id
            print(f'last_id: {last_id}')
            next_id = last_id + 1
            print(f'next_id: {next_id}')
            while '3' in str(next_id):
                next_id += 1
            self.id = next_id
        else:
            self.id = 1
        # Force the save to be an insert, an error is better than two users having same profile
        kwargs["force_insert"] = True

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


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

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