Подтвердить что ты не робот

Наследование модели Django: создать вспомогательный экземпляр существующего экземпляра (downcast)?

Я пытаюсь интегрировать стороннее приложение Django, которое сделало неудачное решение наследовать от django.contrib.auth.models.User, что является большим нет-no для подключаемых приложений. Цитирование Malcolm Tredinnick:

Что еще более важно, хотя, как и в Python, вы не можете "опускать" Наследование модели Django. То есть, если вы уже создали пользователя экземпляр, вы не можете, не выкапывая под обложками, сделать это экземпляр соответствует экземпляру подкласса, который еще не создан.

Ну, я в ситуации, когда мне нужно интегрировать это стороннее приложение с моими существующими экземплярами пользователя. Итак, если гипотетически, я действительно готов подкачать под обложками, какие у меня варианты? Я знаю, что это не работает:

extended_user = ExtendedUser(user_ptr_id=auth_user.pk)
extended_user.save()

Нет исключения, но он разбивает всевозможные вещи, начиная с перезаписывания всех столбцов из django.contrib.auth.models.User пустыми строками...

4b9b3361

Ответ 1

Это должно работать:

extended_user = ExtendedUser(user_ptr_id=auth_user.pk)
extended_user.__dict__.update(auth_user.__dict__)
extended_user.save()

Здесь вы в основном просто копируете значения из версии auth_user в файл extended_user и повторно сохраняете его. Не очень элегантный, но он работает.

Ответ 2

Если вам не нравится решение __dict__.update, вы можете сделать это:

for field in parent_obj._meta.fields
    setattr(child_obj, field.attname, getattr(parent_obj, field.attname))

Ответ 3

Я нашел этот ответ, спросив в списке рассылки django-user:

https://groups.google.com/d/msg/django-users/02t83cuEbeg/JnPkriW-omQJ

Это не является частью общедоступного API, но вы можете положиться на то, как Django загружает данные изнутри.

parent = Restaurant.objects.get(name__iexact="Bob Place").parent
bar = Bar(parent=parent, happy_hour=True)
bar.save_base(raw=True)

Имейте в виду, что это может сломаться с любой новой версией Django.

Ответ 4

Я использую Django 1.6, а моя модель ExtendedUser - от OSQA (forum.models.user.User). По какой-то странной причине вышеупомянутые решения с dict.__update__ и с setattr иногда терпят неудачу. Это может иметь отношение к некоторым другим моделям, которые у меня есть, которые создают ограничения на пользовательские таблицы. Вот еще два обходных пути, которые вы можете попробовать:

Обходной путь №1:

extended_user = ExtendedUser(user_ptr_id = user.pk)
extended_user.save() # save first time
extended_user.__dict__.update(user.__dict__)
extended_user.save() # save second time

Обходной путь № 2:

extended_user = ExtendedUser(user_ptr_id = user.pk)
extended_user.__dict__.update(user.__dict__)
extended_user.id=None
extended_user.save()

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

Ответ 5

Для этого вопроса есть открытая ошибка: https://code.djangoproject.com/ticket/7623

Предлагаемый патч (https://github.com/django/django/compare/master...ar45:child_object_from_parent_model) не использует obj.__dict__ но создает словарь, в котором все значения полей циклически obj.__dict__ по всем полям. Вот упрощенная функция:

def create_child_from_parent_model(child_cls, parent_obj, init_values: dict):
    attrs = {}
    for field in parent_obj._meta._get_fields(reverse=False, include_parents=True):
        if field.attname not in attrs:
            attrs[field.attname] = getattr(parent_obj, field.attname)
    attrs[child_cls._meta.parents[parent_obj.__class__].name] = parent_obj
    attrs.update(init_values)
    print(attrs)
    return child_cls(**attrs)

create_child_from_parent_model(ExtendedUser, auth_user, {})

Преимущество этого метода заключается в том, что методы, которые перезаписываются дочерним элементом, не заменяются исходными родительскими методами. Для меня использование оригинальных ответов obj.__dict__.update() привело к исключениям, так как я использовал FieldTracker из model_utils в родительском классе.

Ответ 6

Как насчет чего-то вроде этого:

from django.forms.models import model_to_dict

auth_user_dict = model_to_dict(auth_user)
extended_user = ExtendedUser.objects.create(user_ptr=auth_user, **auth_user_dict)

Ответ 7

Ответ @guetti работал для меня с небольшим обновлением => Ключ был parent_ptr

parent_object = parent_model.objects.get(pk=parent_id)  
new_child_object_with_existing_parent = Child(parent_ptr=parent, child_filed1='Nothing')
new_child_object_with_existing_parent.save()

Я хотел создать запись в моей модели профиля для существующего пользователя, моя модель была похожа

from django.contrib.auth.models import User as user_model
class Profile(user_model):
     bio = models.CharField(maxlength=1000)
     another_filed = models.CharField(maxlength=1000, null=True, blank=True)

В каком-то месте мне нужно было создать профиль, если он не существует для существующего пользователя, поэтому я сделал это следующим образом:

Пример, который работал для меня

from meetings.user import Profile
from django.contrib.auth.models import User as user_model

user_object = user_model.objects.get(pk=3)  
profile_object = Profile(user_ptr=user_object, bio='some')
profile_object.save()