Как массово «переназначить» все внешние ключи для пользователя в Django - PullRequest
2 голосов
/ 14 января 2020

В этом сценарии пользователь имеет несколько взаимосвязей внешнего ключа для нескольких «приложений» в рамках проекта Django. Этот пользователь решает покинуть платформу, поэтому он хочет легко передать все отношения внешнего ключа другому пользователю.

Можно ли получить список всех объектов, к которым пользователь имеет отношение внешнего ключа, и массово переназначить их ? Или есть более простой способ?

Спасибо

# models.py

class Post(models.Model):
     ...
     created_by = models.ForeignKey(User, on_delete=models.CASSCADE)
     ...

class AnotherModel(models.Model):
     ...
     created_by = models.ForeignKey(User, on_delete=models.CASSCADE)
     ...

class YetAnotherModel(models.Model):
     ...
     created_by = models.ForeignKey(User, on_delete=models.CASSCADE)
     ...

Ответы [ 4 ]

1 голос
/ 14 января 2020

Вы можете получить список отношений из / в модель User, а затем выполнить обновление:

from django.contrib.auth.models import User

my_user = User.object.get(username='my_username')
new_user = User.objects.get(username='new_username')

for f in my_user._meta.get_fields():
    if f.is_relation and f.one_to_many:
        name = f.remote_field
        getattr(my_user, f.att_name).all()<b>.update(**{remote_field: new_user})</b>

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

1 голос
/ 14 января 2020

Вы можете сделать это в вашей пользовательской модели:

old_user.post_set.all().update(created_by=new_user)

old_user.anothermodel_set.all().update(created_by=new_user)

old_user.yetanothermodel_set.all().update(created_by=new_user)

Или вы можете сделать это в самой модели:

Post.objects.filter(created_by=old_user).update(created_by=new_user)
AnotherModel.objects.filter(created_by=old_user).update(created_by=new_user)
YetAnotherModel.objects.filter(created_by=old_user).update(created_by=new_user)

, если у вас много разных моделей, вы можете сделай это как

models = (Post, AnotherModel, YetAnotherModel)
for model in models:
    model.objects.filter(created_by=old_user).update(created_by=new_user)
0 голосов
/ 14 января 2020

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

Сначала измените on_delete на SET_NULL, это позволит добавить некоторые логи c, поэтому немедленно удалить связанную запись

# models.py
class Post(models.Model):
    created_by = models.ForeignKey(User, on_delete=models.SET_NULL)

class AnotherModel(models.Model):
    created_by = models.ForeignKey(User, on_delete=models.SET_NULL)

class YetAnotherModel(models.Model):
    created_by = models.ForeignKey(User, on_delete=models.SET_NULL)

Чтобы сделать ее автоматически, у вас есть две опции, каждая из которых использует сигналы:

Опция 1: Более подробные c для удаления логинов пользователя c, Вы можете вскоре после удаления пользователя удалить все связанные права и передать его другому пользователю.

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

from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver

@receiver(pre_delete, sender=User)
def default_created_by_to_user(sender, **kwargs):
    instance = kwargs["instance"]

    default_user = User.objects.get(username='default')

    # * Here you will have to register all the related models to change the owner
    # * Filter all the owned records from the user and perform the update on them with the new default user
    Post.objects.filter(created_by=instance).update(created_by=default_user)
    AnotherModel.objects.filter(created_by=instance).update(created_by=default_user)
    YetAnotherModel.objects.filter(created_by=instance).update(created_by=default_user)    

Вариант 2: Обновление с целевых моделей. Это более надежно, так как будет срабатывать, когда пользователь удаляется, а также когда для selected_by установлено значение None. Просто зарегистрируйте одну и ту же функцию приемника для всех моделей в поле created_by.

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

@receiver(post_save, sender=Post)
@receiver(post_save, sender=AnotherModel)
@receiver(post_save, sender=YetAnotherModel)
def default_created_by_to_user(sender, **kwarg):
    instance = kwargs["instance"]

    # created_by will be None when the user is deleted
    if instance.created_by:
        default_user = User.objects.get(username='default')

        # * sender is the model class.
        # * Updating with update() will avoid to trigger post_save again
        sender.objects.filter(pk=instance.pk).update(created_by=default_user)
0 голосов
/ 14 января 2020

Не могли бы вы сделать что-то вроде:

old_user = User.objects.get(username='old_username')
new_user = User.objects.get(username='new_username')

for related in old_user._meta.related_objects:
    related.related_model.objects.filter(created_by=old_user).update(created_by=new_user)

_meta.related_objects - это django 1,8+ Я думаю

Уверен, это сработает и с этим: for related in User._meta.related_objects: ...

...