Как мне написать запрос Django, который использует сложное предложение «on» во внутреннем соединении? - PullRequest
1 голос
/ 21 марта 2019

Я использую Django, Python 3.7 и PostgreSQL 9.5.У меня есть эти модели:

class Article(models.Model):
    ...
    label = models.TextField(default='', null=True)


class Label(models.Model):
    name = models.CharField(max_length=200)

Я хочу написать запрос Django, который извлекает все статьи, чья метка содержит имя из таблицы меток.В PostGres я могу структурировать свой запрос следующим образом:

select a.* from myapp_article a join myapp_label l on a.label ilike '%' || l.name || '%';

, но я не знаю, как осуществить это в Django из-за предложения "on" и "ilike".Как мне это осуществить?

Ответы [ 3 ]

1 голос
/ 28 марта 2019

Если вам нужно выполнить поиск по меткам Article без учета регистра для поиска подходящих имен, вы можете использовать регулярное выражение и передать ему плоский список всех имен меток, например:

Article.objects.filter(label__iregex=r'(' + '|'.join(Label.objects.all().values_list('name', flat=True)) + ')')

То, что делает вышеупомянутый запрос, делает плоский список меток:

['label1' , 'label2', 'label3']

и затем строка соединяется так:

'(label1|label2|label3)'

и используется аналогичный SQL-запрос:

SELECT * from FROM "app_article" WHERE "app_article"."label" ~* (label1|label2|label3)

В противном случае, для подхода с учетом регистра вы можете использовать это:

names_list = Label.objects.all().values_list('name', flat=True)
Article.objects.filter(label__in=names_list)
0 голосов
/ 24 марта 2019

Это не будет переводиться в один и тот же запрос SQL, но даст те же результаты, используя внутренний запрос.

inner_query = Label.objects.annotate(article_label=OuterRef('label')).filter(article_label__icontains=F('name'))
articles = Article.objects.annotate(labels=Subquery(inner_query.values('name')[:1])).filter(labels__isnull=False)

Это должно примерно соответствовать следующему:

select a.* from myapp_article a where exists (select l.* from myapp_label l where a.label ilike '%' || l.name || '%')

Но из-за текущей проблемы в Django относительно использования OuterRef в аннотациях этот подход не 'т работа.Нам нужно использовать обходной путь, предложенный здесь , до тех пор, пока проблема не будет решена, чтобы этот запрос работал, например:

Сначала определите пользовательское выражение

class RawCol(Expression):

    def __init__(self, model, field_name):
        field = model._meta.get_field(field_name)
        self.table = model._meta.db_table
        self.column = field.column
        super().__init__(output_field=CharField())

    def as_sql(self, compiler, connection):
        sql = f'"{self.table}"."{self.column}"'
        return sql, []

Затем выполните сборкуВаш запрос, используя это выражение

articles = Article.objects.all().annotate(
    labels=Subquery(
        Label.objects.all().annotate(
            article_label=RawCol(Article, 'label')
        ).filter(article_label__icontains=F('name')).values('name')[:1]
    )
).filter(labels__isnull=False)

Это должно вернуть экземпляры модели Article, чье поле метки содержит значение из поля имени модели Label

0 голосов
/ 21 марта 2019

В вашем class Article вы должны будете объявить ярлык как Foreignkey для class Label

class Article(models.Model):
    ...
    label = models.ForeignKey(Label, default='', on_delete=models.CASCADE)

И тогда вы можете получить к нему доступ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...