使用__getattr__方法扩展Django用户模型

时间:2018-07-31 18:36:02

标签: python django

在Django项目中,我有一个扩展Django用户模型的模块,类似于以下内容:

from django.contrib.auth.models import User

def __str__(self):
    return "%s %s" % (self.first_name, self.last_name)

User.add_to_class('__str__', __str__)

我注意到这可行。但是,如果我尝试定义__getattr__方法,如下所示:

def __getattr__(self, attr):
    if self.user_profile:
        return getattr(self.user_profile, attr)
    else:
        raise AttributeError(f"'{self.__class__.__name__}' has no attribute '{attr}'")

导入模块时出现以下错误:

(lucy-web-CVxkrCFK) bash-3.2$ python manage.py shell
Traceback (most recent call last):
  File "manage.py", line 28, in <module>
    execute_from_command_line(sys.argv)
  File "/Users/kurtpeek/.local/share/virtualenvs/lucy-web-CVxkrCFK/lib/python3.7/site-packages/django/core/management/__init__.py", line 371, in execute_from_command_line
    utility.execute()
  File "/Users/kurtpeek/.local/share/virtualenvs/lucy-web-CVxkrCFK/lib/python3.7/site-packages/django/core/management/__init__.py", line 347, in execute
    django.setup()
  File "/Users/kurtpeek/.local/share/virtualenvs/lucy-web-CVxkrCFK/lib/python3.7/site-packages/django/__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "/Users/kurtpeek/.local/share/virtualenvs/lucy-web-CVxkrCFK/lib/python3.7/site-packages/django/apps/registry.py", line 112, in populate
    app_config.import_models()
  File "/Users/kurtpeek/.local/share/virtualenvs/lucy-web-CVxkrCFK/lib/python3.7/site-packages/django/apps/config.py", line 198, in import_models
    self.models_module = import_module(models_module_name)
  File "/Users/kurtpeek/.local/share/virtualenvs/lucy-web-CVxkrCFK/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/lucy_web/models/__init__.py", line 25, in <module>
    from .user import User
  File "<frozen importlib._bootstrap>", line 1019, in _handle_fromlist
TypeError: __getattr__() missing 1 required positional argument: 'attr'

是否无法通过User方法扩展__getattr__模型?

1 个答案:

答案 0 :(得分:1)

在python 3.7中,如果定义了名为__getattr__ it will be a module level attribute getter function的模块级函数,类似于类getattr。好像您是无意中那样。模块级别__getattr__具有与类方法不同的调用签名。

  

规格

     

模块级别的__getattr__函数应   接受一个作为属性名称的参数,然后返回   计算值或引发AttributeError:

def __getattr__(name: str) -> Any: ...

尝试将函数重命名为其他名称,以避免无意间隐藏保留的函数名称。

def  user_getattr(self, attr):
    ...

User.add_to_class('__getattr__', user_getattr)

This is mentioned in PEP 562:

  

此PEP可能会破坏使用模块级别(全局)名称的代码   __getattr____dir__。 (但该语言参考明确保留了所有未记录的dunder名称,并允许“   警告”)

And here's what the language reference says:

  

在任何情况下都不得使用__*__名称   明确记录在案的使用,如有损坏,恕不另行通知。

因此,您还应该重命名自定义的__str__函数,以避免将来可能发生损坏。