[Django]-Perform a SQL JOIN on Django models that are not related?

3๐Ÿ‘

โœ…

You can add an extra method onto the User class, using MonkeyPatching/DuckPunching:

def logs(user):
    return Log.objects.filter(email=user.email)

from django.contrib.auth.models import User
User.logs = property(logs)

Now, you can query a User, and ask for the logs attached (for instance, in a view):

user = request.user
logs = user.logs

This type of process is common in the Ruby world, but seems to be frowned upon in Python.

(I came across the DuckPunching term the other day. It is based on Duck Typing, where we donโ€™t care what class something is: if it quacks like a duck, it is a duck as far as we are concerned. If it doesnโ€™t quack when you punch it, keep punching until it quacks).

2๐Ÿ‘

why not use extra()?

example (untested):

User.objects.extra(
    select={
        'log_count': 'SELECT COUNT(*) FROM myapp_log WHERE myapp_log.email = auth_user.email'
    },
)

for the User.objects.filter(log__level=3) portion here is the equivalent with extra (untested):

User.objects.extra(
    select={
        'log_level_3_count': 'SELECT COUNT(*) FROM myapp_log WHERE (myapp_log.email = auth_user.email) AND (myapp_log.level=3)'
    },
).filter(log_level_3_count__gt=0)
๐Ÿ‘คJiaaro

0๐Ÿ‘

Do the Log.email values always correspond to a User? If so, how about just adding a ForeignKey(User) to the Log object?

class Log(models.Model):
    # ...
    user = models.ForeignKey(User)

With the FK to User, it becomes fairly straight forward to find what you want:

User.objects.filter(log__level=3)
User.objects.all().anotate(Count('log'))

user.log_set.all()
user.log_set.count()

log.user

If the Log.email value does not have to belong to a user you can try adding a method to a model manager.

class LogManager(models.Manager):
    def for_user(self, user):
        return super(LobManager, self).get_query_set().filter(email=user.email)

class Log(models.Model):
    # ...
    objects = LogManager()

And then use it like this:

user = User.objects.get(pk=1)
logs_for_user = Log.objects.for_user(user)
๐Ÿ‘คistruble

Leave a comment