Skip to main content
 首页 » 编程设计

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

2024年12月31日21bhlsheji

在 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' 

难道不能用这种方式用__getattr__方法扩展User模型吗?

请您参考如下方法:

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

Specification

The __getattr__ function at the module level should accept one argument which is the name of an attribute and return the computed value or raise an AttributeError:

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

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

def  user_getattr(self, attr): 
    ... 
 
User.add_to_class('__getattr__', user_getattr) 

This is mentioned in PEP 562:

This PEP may break code that uses module level (global) names __getattr__ and __dir__. (But the language reference explicitly reserves all undocumented dunder names, and allows "breakage without warning")

And here's what the language reference says:

Any use of __*__ names, in any context, that does not follow explicitly documented use, is subject to breakage without warning.

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