Наследование модели Django: создать подэкземпляр существующего экземпляра (downcast)? - PullRequest
46 голосов
/ 31 октября 2010

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

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

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

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

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

Ответы [ 7 ]

65 голосов
/ 01 ноября 2010

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

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

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

6 голосов
/ 29 августа 2013

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

for field in parent_obj._meta.fields
    setattr(child_obj, field.attname, getattr(parent_obj, field.attname))
5 голосов
/ 18 мая 2015

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

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

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

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

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

2 голосов
/ 19 июня 2014

Я использую 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, сохранить его, и тогда все будет работать нормально.

1 голос
/ 25 марта 2019

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

Предлагаемый патч (https://github.com/django/django/compare/master...ar45:child_object_from_parent_model) не использует 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 в родительском классе.

0 голосов
/ 02 мая 2019

@ ответ 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()
0 голосов
/ 28 августа 2018

Как насчет этого:

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...