在Django中将基模型实例转换为派生代理模型?

16 投票
2 回答
4548 浏览
提问于 2025-04-17 05:07

我想为Django默认的用户类定义一个代理模型,类似于下面这样:

class MyUser(User):

    def pretty_username(self): 
        if self.first_name:
            return self.first_name
        return self.username 

    class Meta: 
        proxy = True 

而且,我希望能够在视图代码中调用pretty_username(最好还能在模板中调用)。有没有简单的方法可以把一个标准用户模型的实例转换成我的MyUser实例呢?

即使是一些__init__的魔法也可以,只要我能在我的视图代码中这样写:

my_user = MyUser(request.user) 

就可以了。

2 个回答

4

如果你真的想要完整的代理对象,这里有一个简单粗暴的解决办法(不过会多一次数据库查询)

class MyUser(User):

    def pretty_username(self): 
        if self.first_name:
            return self.first_name
        return self.username 

    class Meta: 
        proxy = True


def get_myuser(self):
    try:
        return MyUser.objects.get(pk=self.pk)
    except MyUser.DoesNotExist:
        return None

User.add_to_class('get_myuser', get_myuser)

所以在视图中使用这个,你可以这样写:

request.user.get_myuser().pretty_username()

或者在模板中:

{{ request.user.get_myuser.pretty_username }}

如果你不一定要用代理模型的话,还有一个更好的解决办法:

def pretty_username(self):
    if self.first_name:
        return self.first_name
    return self.username

User.add_to_class('pretty_username', pretty_username)

这样可以实现以下功能:

request.user.pretty_username()

或者

{{ request.user.pretty_username }}
9

我觉得这个问题的真正答案应该是@fhahn在另一个回答中的评论。通过更改class,我们可以避免多一次数据库的调用。下面是一个示例代码:

这是我的代理模型,它会把显示的内容从username改成email,如果设置了的话:

class MyUser(User):
    class Meta:
        proxy = True
        verbose_name = _('my user')
        verbose_name_plural = _('my users')

    def __str__(self):
        return "%s" % (self.email or self.username)

class Wallet(models.Model):
    owner = models.OneToOneField(MyUser, on_delete=models.PROTECT, related_name='wallet')

    def __str__(self):
        return "%s" % self.owner

在命令行中的简单测试:

>>> from django.contrib.auth.models import User
>>> from my_apps.models import MyUser
>>> user = User.objects.get(pk=4)
>>> user
<User: AbKec6rumI9H9UmAC3Bh2kXUHzj4>
>>> user.email
'john@example.com'
>>> user.wallet
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'User' object has no attribute 'wallet'
>>> user.__class__ = MyUser
>>> user
<MyUser: john@example.com>
>>> user.wallet
<Wallet: john@example.com>

撰写回答