2013-05-21 3 views
1

Попытка сделать мое приложение для повторного использования и совместимы с моделями, созданные пользователем, я столкнулся следующий вопрос:Джанго получить соответствующий менеджер с неизвестным именем

Скажем, модель пользователя имеет следующий вид:

class Member(AbstractUser): 
    pass # basically the builtin User with another name 

в результате этого кода из django.contrib.auth.models:

class PermissionsMixin(models.Model): 
    """ 
    A mixin class that adds the fields and methods necessary to support 
    Django's Group and Permission model using the ModelBackend. 
    """ 
    is_superuser = models.BooleanField(_('superuser status'), default=False, 
     help_text=_('Designates that this user has all permissions without ' 
        'explicitly assigning them.')) 
    groups = models.ManyToManyField(Group, verbose_name=_('groups'), 
     blank=True, help_text=_('The groups this user belongs to. A user will ' 
           'get all permissions granted to each of ' 
           'his/her group.')) 
    user_permissions = models.ManyToManyField(Permission, 
     verbose_name=_('user permissions'), blank=True, 
     help_text='Specific permissions for this user.') 

class AbstractUser(AbstractBaseUser, PermissionsMixin): # <- included there 
    .... 

(Обратите внимание на то, что многие ко многим groups не определяет.)

Теперь при работе с группами я не могу это сделать:

group.user_set.add(...) 

так как обратное RelatedManager имя на самом деле member_set.

Обратите внимание, что в данном случае имя модели Member, но это может быть любой другой.

Прямо сейчас, я делаю следующее:

getattr(group, "%s_set" % settings.AUTH_USER_MODEL.split('.')[-1].lower()) 

Да, очень некрасиво.

Вопрос: Любое общее решение или решение bultin для этого?

EDIT с моей окончательной реализацией:

@classmethod 
def _get_user_reverse_field_name(cls): 
    User = get_user_model() 
    cls._user_reverse_field_name = User._meta.get_field('groups').related.get_accessor_name() 

@property 
def members(self): 
    if not hasattr(self, '_user_reverse_field_name'): 
     self._get_user_reverse_field_name() 
    return getattr(self, self._user_reverse_field_name) 
+0

Ahh, я неправильно понял вопрос, удалил свой ответ ... – Ngenator

ответ

3

Это должно быть то, что вы ищете:

In [1]: opts = Group._meta 

In [2]: from django.contrib.auth import get_user_model() 

In [3]: rel = [r for r in opts.get_all_related_many_to_many_objects() 
       if r.model == get_user_model()] 

In [4]: rel 
Out[4]: [<RelatedObject: users:user related to groups>] 

In [5]: user_rel = rel[0] 

In [6]: user_rel.get_accessor_name() 
Out[6]: 'user_set' 
+0

Это выглядит многообещающим. Единственная проблема, которую я вижу, заключается в том, что 'AUTH_USER_MODEL' необязательно должен наследоваться от' User' или 'AbstractUser' или чего-то еще. Любой способ фильтрации 'get_all_related_many_to_many_objects()' с помощью строки 'AUTH_USER_MODEL' (' 'app_label.model_name'')? –

+0

Я скорректировал свой ответ на ваши потребности ... 'get_user_model()' лучше, чем иметь дело с строкой 'AUTH_USER_MODEL' ... –

+0

Да, я вижу. Это дает мне круговые проблемы с импортом:/Моя пользовательская модель 'Group' находится в тех же models.py, что и модель' User' по умолчанию, которую я хочу предоставить в своем приложении. –

2

Вы можете использовать inspect перебрать все приложения и модели, прежде чем они будут загружены. В пределах этого цикла вы можете обезьяну исправить атрибут поля related_name, если модель является подклассом AbstractUser.

Сказав это, было бы довольно штопать противно. Я упоминал, что это не сработает, если у вас есть две модели из AbstractUser, поскольку связанные имена будут сталкиваться?

Ваш текущий способ достичь этого довольно прямолинейный и один лайнер, я бы сохранил его.

Ну, я бы, наверное, предпочел .format() над %

Для того глубокое понимание, как обратные отношения работы имеют вид на дескриптор в related.py

+0

Здравствуйте, THX для ответа. Да, я подумал об исправлении обезьян. Но вскоре отбросил его, когда я заметил, что вы сказали. Это несколько сломает «AbstractUser». И это явно не стоит просто для более четкого кода. Я предполагаю, что может существовать недокументированный атрибут '_meta', внутренний метод или что-то:/ –

+0

Я читаю код в related.py и' get_accessor_name() 'кажется именно тем, что я хотел. Возможно ли иметь доступ к этому методу? –

+2

Ну, я не уверен, что функция будет вызвана до того, как будет получена связь, вы можете исправить метод дескриптора и посмотреть, имеете ли вы дело с моделью, полученной из 'AbstractUser'. В любом случае, вы, вероятно, захотите уклониться от исправления _that_ в глубине ядра Django. –

Смежные вопросы