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?

Ondrej Slinták
  • 31,386
  • 20
  • 94
  • 126
Hellnar
  • 62,315
  • 79
  • 204
  • 279
  • 1
    Related: [Extending the User model with custom fields in Django](http://stackoverflow.com/questions/44109/extending-the-user-model-with-custom-fields-in-django) – miku May 30 '10 at 19:19
  • 2
    Aware, but I dont want to add custom field, just a function without altering the original User model. – Hellnar May 30 '10 at 19:24
  • 1
    http://docs.djangoproject.com/en/1.2/ref/models/relations/#django.db.models.fields.related.RelatedManager – alex vasi May 30 '10 at 19:49
  • Possible duplicate of [Best way to add convenience methods to a Django Auth User model?](http://stackoverflow.com/questions/1818223/best-way-to-add-convenience-methods-to-a-django-auth-user-model) – Louis Nov 09 '15 at 13:39

4 Answers4

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.

lprsd
  • 84,407
  • 47
  • 135
  • 168
  • 6
    I'm pretty sure this is monkey patching is is generally frowned upon. See discussion here: http://stackoverflow.com/a/965859/406157 – Jeremy Blanchard Mar 06 '12 at 05:07
  • 1
    OneToOneField is definitely the way this should be handled. – Derek Adair Nov 20 '14 at 22:12
  • 1
    @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. – Louis Nov 05 '15 at 16:44
  • @Louis 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". – Derek Adair Nov 07 '15 at 20:06
  • 1
    @DerekAdair You would be hard pressed to find any writing of mine where I have an argument that goes "you should, because you can". – Louis Nov 09 '15 at 13:12
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
suhailvs
  • 20,182
  • 14
  • 100
  • 98
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)
jgsogo
  • 706
  • 1
  • 9
  • 18
  • New link to custom user docs https://docs.djangoproject.com/en/3.1/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project – Peter Chaula Nov 22 '20 at 04:36
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

bkarpov
  • 21
  • 4