Django запрос на основе сквозной таблицы
У меня есть 5 моделей, которые являются Contents, Filters, ContentFilter , Users.
пользователь может просматривать содержимое. содержимое может быть ограничено с помощью фильтров, чтобы пользователи не могли его видеть.
вот модели.
class Content(models.Model):
title = models.CharField(max_length=120)
text = models.TextField()
filters = models.ManyToManyField(to="Filter", verbose_name=_('filter'), blank=True, related_name="filtered_content",through='ContentFilter')
class Filter(models.Model):
name = models.CharField(max_length=255, verbose_name=_('name'), unique=True)
added_user = models.ManyToManyField(to=User, related_name="added_user", blank=True)
ignored_user = models.ManyToManyField(to=User, related_name="ignored_user", blank=True)
charge_status = models.BooleanField(blank=True, verbose_name=_('charge status'))
class ContentFilter(models.Model):
content = models.ForeignKey(Content, on_delete=models.CASCADE)
filter = models.ForeignKey(Filter, on_delete=models.CASCADE)
manual_order = models.IntegerField(verbose_name=_('manual order'), default=0,rst'))
access = models.BooleanField(_('has access'))
Что это означает, что существует 5 содержимых (1,2,3,4,5).
2 пользователя существуют. x,y
Можно создать фильтр с игнорируемым пользователем (x).
Содержание 1,2,3 имеет отношение к фильтру x.
теперь X видит 4,5, а Y видит 1,2,3,4,5
Сейчас я делаю так: на основе того, какой пользователь запросил, нахожу, какие фильтры связаны с ним.
затем запрашиваю сквозную таблицу (ContentFilter
), чтобы найти, какое содержимое пользователь не может видеть, и затем исключаю его из всего содержимого. (это помогает при больших объединениях)
filters = Filter.objects.filter(Q(added_user=user)|(Q(ignored_user=user))
excluded_contents = list(ContentFilter.objects.filter(filter__in=filters).values_list('id',flat=True))
contents = Contents.objects.exclude(id__in=excluded_contents)
Проблема
Мне нужен способ, чтобы фильтры могли иметь порядок и фильтровать набор запросов на основе топ ContentFilter
для каждого пользователя.
например, контент 1 может быть заблокирован для всех пользователей с помощью 1 фильтра (фильтр x, где игнорируемый пользователь имеет всех пользователей).
но в ContentFilter
имеет ручной_порядок 0.
тогда во втором фильтре все пользователи, у которых статус заряда True, могут видеть это содержимое. (фильтр y, где добавленный пользователь имеет всех пользователей и статус заряда True).
и в ContentFilter
имеет manual_order равный 1.
Я думаю, что могу сделать это с помощью цикла for, чтобы проверить все содержимое и выбрать самые верхние ContentFilter
из них на основе фильтров, включающих этого пользователя, но это требует много времени и ресурсов.
и я предпочитаю не использовать необработанный SQL, но я не уверен, есть ли способ сделать это с помощью django orm
Мне удалось решить эту проблему с помощью Subquery.
сначала я создаю список фильтров, в которые входит пользователь.
filters = Filter.objects.filter(Q(added_user=user)|(Q(ignored_user=user))
затем я создаю подзапрос для присвоения каждому содержимому значения доступа (если к нему применен какой-либо фильтр)
current_used_filters = ContentFilter.objects.filter(Q(filter__in=user_filters),content=OuterRef('pk')).order_by('-manual_order')
blocked_content_list = Content.objects.annotate(access=Subquery(current_used_filters.values('access')[:1])).filter(
access=False).values_list('id', flat=True)
в связи с этим возникает проблема
если у любого моего содержимого нет фильтра фильтров, связанных с ним, то оно не будет включено в это.
Поэтому я фильтрую те, которые имеют значение доступа False
.
Это означает, что у этого содержимого есть фильтр с высоким ручным порядком, который блокирует его для этого конкретного пользователя.
Теперь у меня есть список идентификаторов содержимого, которые я могу исключить из всего содержимого.
Таким образом, это будет:
contents = Contents.objects.exclude(id__in=blocked_content_list)