Расширение auth.User модель, проксированные поля и администратор Django - PullRequest
1 голос
/ 06 марта 2011

(Правка: я знаю, что в Django есть совершенно отдельная функция, называемая "Proxy Models". Эта функция мне не помогает, потому что мне нужно иметь возможность добавлять поля в UserProfile.)

ИтакЯ запускаю новое приложение Django и создаю модель UserProfile, которая является расширением django.contrib.auth.models.User и не отвечает на запросы атрибутов обратно пользователю, следующим образом:

from django.db import models
from django.contrib.auth.models import User

class UserProfile(models.Model):
    user = models.OneToOneField(User, related_name='profile')

    def __getattr__(self, name, *args):
        if name == 'user' or name == '_user_cache':
            raise AttributeError(name)

        try:
            return getattr(self.user, name, *args)
        except AttributeError, e:
            raise AttributeError(name)

В целом это работает нормально, но ломается, когда я пытаюсь использовать поле User в UserProfileAdmin.list_display.Проблема в коде проверки администратора здесь:

def validate(cls, model):
    """
    Does basic ModelAdmin option validation. Calls custom validation
    classmethod in the end if it is provided in cls. The signature of the
    custom validation classmethod should be: def validate(cls, model).
    """
    # Before we can introspect models, they need to be fully loaded so that
    # inter-relations are set up correctly. We force that here.
    models.get_apps()

    opts = model._meta
    validate_base(cls, model)

    # list_display
    if hasattr(cls, 'list_display'):
        check_isseq(cls, 'list_display', cls.list_display)
        for idx, field in enumerate(cls.list_display):
            if not callable(field):
                if not hasattr(cls, field):
                    if not hasattr(model, field):
                        try:
                            opts.get_field(field)
                        except models.FieldDoesNotExist:
                            raise ImproperlyConfigured("%s.list_display[%d], %r is not a callable or an attribute of %r or found in the model %r."
                                % (cls.__name__, idx, field, cls.__name__, model._meta.object_name))

Проблема в том, что, хотя экземпляр UserProfile будет иметь прокси-поля, например, электронную почту, сам класс UserProfile не имеет.Демонстрация в оболочке Django:

>>> hasattr(UserProfile, 'email')
False
>>> hasattr(UserProfile.objects.all()[0], 'email')
True

После некоторого копания похоже, что я бы хотел переопределить django.db.models.options.Options.get_field для UserProfile._meta.Но, похоже, не существует нехакерского способа сделать это (у меня сейчас очень хакерское решение, которое включает в себя мартовское исправление UserProfile._meta. [Get_field, get_field_by_name]) ... какие-нибудь предложения?Спасибо.

Ответы [ 3 ]

2 голосов
/ 06 марта 2011

Будьте проще. Вот пример модели UserProfile из библиотеки, которую мы используем:

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    accountcode = models.PositiveIntegerField(null=True, blank=True)

Вот и все. Не беспокойтесь о переопределении __getattr__. Вместо этого настройте интерфейс администратора:

from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User

class UserProfileInline(admin.StackedInline):
    model = UserProfile

class StaffAdmin(UserAdmin):
    inlines = [UserProfileInline]
    # provide further customisations here

admin.site.register(User, StaffAdmin)

Это позволяет вам CRUD-объект User с доступом к UserProfile в качестве встроенного. Теперь вам не нужно прокси-поиск атрибутов из UserProfile в модель User. Чтобы получить доступ к UserProfile из экземпляра User u, используйте u.get_profile()

0 голосов
/ 06 марта 2011

Если вы просто хотите, чтобы поле User было в list_display в вашем UserProfileAdmin, попробуйте:

class UserProfileAdmin(admin.ModelAdmin):
    list_display = ('user__email',)

Если вы хотите, чтобы оно было частью формы, добавьте его в свой UserProfileForm в качестве дополнительного поля и подтвердите его в форме.

0 голосов
/ 06 марта 2011

Это не прокси-класс, это отношения.Подробнее о Proxy Model s, которые являются подклассом исходной модели, с Meta.proxy = True

...