Django User model, adding function
Asked Answered
V

4

30

I want to add a new function to the default User model of Django for retrieveing a related list of Model type.

Such Foo model:

class Foo(models.Model):
    owner = models.ForeignKey(User, related_name="owner")
    likes = models.ForeignKey(User, related_name="likes")

........

    #at some view
    user = request.user
    foos= user.get_related_foo_models()

How can this be achieved?

Voyageur answered 30/5, 2010 at 19:13 Comment(4)
Related: Extending the User model with custom fields in DjangoPenguin
Aware, but I dont want to add custom field, just a function without altering the original User model.Voyageur
docs.djangoproject.com/en/1.2/ref/models/relations/…Labonte
Possible duplicate of Best way to add convenience methods to a Django Auth User model?He
R
46

You can add a method to the User

from django.contrib import auth
auth.models.User.add_to_class('get_related_foo_models', get_related_foo_models)

Make sure, you have this code within the models.py or some other file which gets imported in the startup of django.

Recitative answered 31/5, 2010 at 13:6 Comment(5)
I'm pretty sure this is monkey patching is is generally frowned upon. See discussion here: https://mcmap.net/q/73480/-what-39-s-the-best-way-to-extend-the-user-model-in-djangoForego
OneToOneField is definitely the way this should be handled.Delrosario
@DerekAdair We're talking about adding a method to the class. Using a OneToOneField entails modifying the database, which is 100% unnecessary to attain the goal of adding a method.He
@He Just because you can does not mean you should, there are many side effects to this type of approach. In certain situations those side effects are negligible, but the OneToOneField is the "proper" way to achieve this. I suppose my comment would be better worded like, "Its almost always the way to do it".Delrosario
@DerekAdair You would be hard pressed to find any writing of mine where I have an argument that goes "you should, because you can".He
J
14

This is an update of @Lakshman Prasad's answer. But a full example:

create a file monkey_patching.py in any of your apps::

#app/monkey_patching.py
from django.contrib.auth.models import User 

def get_user_name(self):
    if self.first_name or self.last_name:
        return self.first_name + " " + self.last_name
    return self.username

User.add_to_class("get_user_name",get_user_name)

and import it in app's __init__.py file. ie::

#app/__init__.py
import monkey_patching
Jessalyn answered 10/7, 2014 at 3:53 Comment(1)
Up Vote. Thanks for enhancing the answer to make it complete.Recitative
S
4

It's not unusual to substitute user model as it is stated in the docs: https://docs.djangoproject.com/es/1.9/topics/auth/customizing/#substituting-a-custom-user-model, so, having this into account, it is better to get the user model class with the following code:

from django.contrib.auth import get_user_model
UserModel = get_user_model()

Afterwards, you can use this UserModel to add functionality as @Lakshman Prasad suggests: UserModel.add_to_class('get_related_foo_models', get_related_foo_models).


In order to get the code executed only once I prefer to use Django application config classes (https://docs.djangoproject.com/es/1.9/ref/applications/), so a full working example will be:

# myapp/__init__.py
default_app_config = 'myapp.apps.MyAppConfig'

# myapp/apps.py
from django.apps import AppConfig
from django.contrib.auth import get_user_model


class MyAppConfig(AppConfig):
    name = 'myapp'
    verbose_name = 'MyApp'

    def ready(self):
        # Add some functions to user model:
        def custom_function(self):
            # Do whatsoever
            pass

        UserModel = get_user_model()
        UserModel.add_to_class('custom_function', custom_function)
Shiau answered 8/2, 2016 at 14:9 Comment(1)
New link to custom user docs docs.djangoproject.com/en/3.1/topics/auth/customizing/…Gape
F
2

You can use a proxy model to add methods to the User model without changing it class.

from django.contrib.auth import models

class UserProxy(models.User):
    class Meta:
        proxy = True
    
    def get_related_foo_models(self):
        pass

Get an instance of the proxy model in view - models.UserProxy.objects.get(pk=request.user.pk)

To get an instance of the proxy model immediately from the request, you need to create a custom ModelBackend as described in this question - Django - User proxy model from request.

Source https://docs.djangoproject.com/en/4.1/topics/db/models/#proxy-models

Fiord answered 2/9, 2022 at 10:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.