31

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?

4

4 Answers 4

47

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.

Sign up to request clarification or add additional context in comments.

5 Comments

I'm pretty sure this is monkey patching is is generally frowned upon. See discussion here: stackoverflow.com/a/965859/406157
OneToOneField is definitely the way this should be handled.
@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 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".
@DerekAdair You would be hard pressed to find any writing of mine where I have an argument that goes "you should, because you can".
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 

1 Comment

Up Vote. Thanks for enhancing the answer to make it complete.
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) 

1 Comment

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.