Выполнить SQL JOIN на моделях Django, которые не связаны? - PullRequest
3 голосов
/ 24 февраля 2010

У меня есть 2 модели: пользователь (django.contrib.auth.models.User) и модель с именем Log. Оба содержат поле «электронная почта». В журнале нет ForeignKey, указывающего на модель пользователя. Я пытаюсь выяснить, как я могу выполнить JOIN для этих двух таблиц, используя поле электронной почты в качестве общности.

В основном есть два запроса, которые я хочу выполнить. Базовое соединение для фильтрации

#Get all the User objects that have related Log objects with the level parameter set to 3.
User.objects.filter(log__level=3)

Я также хотел бы сделать несколько агрегатов.

User.objects.all().anotate(Count('log'))

Конечно, было бы неплохо сделать и обратное.

log = Log.objects.get(pk=3)
log.user...

Есть ли способ сделать это с ORM? Может быть, что-то, что я могу добавить в мета-класс модели, чтобы «активировать» отношение?

Спасибо!

Ответы [ 3 ]

3 голосов
/ 25 февраля 2010

Вы можете добавить дополнительный метод в класс User, используя MonkeyPatching / DuckPunching:

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

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

Теперь вы можете запросить пользователя и запросить прикрепленные журналы (например, в виде):

user = request.user
logs = user.logs

Этот тип процесса распространен в мире Ruby, но, похоже, в Python его осуждают.

(Я встретил термин DuckPunching на днях. Он основан на Duck Typing, где нам все равно, что это за класс: если он крякает как утка, то это утка, насколько мы обеспокоены. Если это не крякает, когда вы бьете, продолжайте бить, пока не крякает).

2 голосов
/ 24 февраля 2010

почему бы не использовать extra () ?

пример (не проверено):

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

для части User.objects.filter(log__level=3) здесь эквивалентно extra (не проверено):

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)
0 голосов
/ 24 февраля 2010

Всегда ли значения Log.email соответствуют Пользователю? Если да, то как насчет простого добавления ForeignKey (User) к объекту Log?

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

С FK для пользователя становится довольно просто найти то, что вы хотите:

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

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

log.user

Если значение Log.email не обязательно должно принадлежать пользователю, вы можете попробовать добавить метод в менеджер моделей .

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()

А затем используйте это так:

user = User.objects.get(pk=1)
logs_for_user = Log.objects.for_user(user)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...