6

I have two models that has a manytomany relationship with a 'through' table in some way?

class Bike(models.Model):
   nickname = models.CharField(max_length=40)
   users    = models.ManyToManyField(User, through='bike.BikeUser')

The BikeUser class

class BikeUser(models.Model):
   bike     = models.ForeignKey(Bike)
   user     = models.ForeignKey(User)
   comment  = models.CharField(max_length=140)

I would like to add functionality to the Bike class for working with users, is there a best practice way of doing this. I would like to avoid adding too many methods to the Bike class and rather have some kind of manager to work through

Something like:

bike.bikeusers_set.commonMethod()

or

bike.bikeusers.commonMethod()

What would be the best way to accomplish this?

Joelbitar
  • 3,520
  • 4
  • 28
  • 29

3 Answers3

8

Once you have the BikeUser model, you can add a custom manager to the model.

Something like:

class BikeUserManager(models.Manager):
    def commonMethod():
        pass

class BikeUser(models.Model):
   bike     = models.ForeignKey(Bike)
   user     = models.ForeignKey(User)
   comment  = models.CharField(max_length=140)
   objects  = BikeUserManager()

But you can only use it from the BikeUser Model:

BikeUser.objects.commonMethod()

What you want is to use this manager as a related manager: http://docs.djangoproject.com/en/dev/topics/db/managers/#controlling-automatic-manager-types

Add the use_for_related_fields=True to the manager class.

class MyManager(models.Manager):
    use_for_related_fields = True
OmerGertel
  • 2,573
  • 1
  • 19
  • 27
  • Thank you, that worked like a charm! Howerver, if I call the manager from an instanciated Bike bike.bikeuser_set.commonMethod() How do I access the "bike" from within the commonMethod? (should this be a new question?) – Joelbitar Oct 02 '10 at 18:19
  • I think you'll have to add it as a function parameter: def commonMethod(self,bike) and use it like this: bike.bikeuser_set.commonMethod(bike) – OmerGertel Oct 02 '10 at 18:57
  • Thats what I'v been doing, I thought there was a better way, thank you very much anyways! – Joelbitar Oct 02 '10 at 20:38
1

use_for_related_fields is deprecated from django 2.0. Use for related fields is possible via base manager.

old:

class CustomManager(models.Model):
    use_for_related_fields = True

class Model(models.Model):
    custom_manager = CustomManager()

new:

class Model(models.Model):
    custom_manager = CustomManager()

    class Meta:
        base_manager_name = 'custom_manager'

source of example

Remember about restrictions for get_queryset() method.

MartinP
  • 527
  • 5
  • 17
0

Code

def m2m_with_manager(related_manager, model_manager_name: str):
    """Replaces the default model manager tied to related manager with defined"""
    model_manager = getattr(related_manager.model, model_manager_name)
    return model_manager.filter(**related_manager.core_filters)

Example

for class Author and Books, where one Author can have multiple books

class Book:
   author = FK(Author, related_name='books')
   best = models.Manager(...)

Usage

wanted_qs = m2m_with_manager(author.books, model_manager_name='best')
pymen
  • 5,737
  • 44
  • 35