Как отфильтровать набор запросов по чужому полю Model CharField?
У меня есть три модели, которые связаны друг с другом. Теперь я хочу запросить набор записей Poller
, который фильтруется categories_selected
пользователем, хранящим строки poller_category
.
# Models
class Category(models.Model):
"""
Holds all available Categories
"""
poller_category = models.CharField(max_length=30)
class UserCategoryFilter(models.Model):
"""
Holds Categories selected by user
"""
user = models.ForeignKey(Account, on_delete=models.CASCADE)
categories_selected = models.CharField(max_length=2000)
class Poller(models.Model):
"""
Holds Poller objects
"""
poller_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
poller_category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
# View
def render_random_poller(request):
# Get user category filter if logged in
if request.user.is_authenticated:
# Get the Users filter as a list of selections
category_filter = UserCategoryFilter.objects.filter(user=request.user).values_list('categories_selected', flat=True)
print(category_filter)
# Get a Category instance filtered by user selection of categories
category_instance = Category.objects.filter(poller_category__in=category_filter)
print(category_instance)
# Apply user's selection to the poller query and take 100 latest pollers
qs_poller = Poller.objects.filter(poller_category__in=category_instance).order_by('-created_on')[:100]
else:
[...]
Однако print(category_instance)
возвращает пустой кверисет, хотя print(category_filter)
возвращает ['category_foo']
. Как лучше построить кверисет?
Я думаю, что ваши варианты именования немного запутаны. Вам следует переименовать ваши поля.
qs_poller = Poller.objects.filter(poller_category__poller_category__in=category_instance).order_by('-created_on')[:100]
Если я правильно понимаю, categories_selected
- это список Python, который вы преобразовали в строку, и, таким образом, является литералом списка.
Мы можем разобрать их обратно в список объектов с помощью:
from ast import literal_eval
qs = UserCategoryFilter.objects.filter(user=request.user)
categories = [
item
for uc in qs.values_list('categories_selected', flat=True)
for item in literal_eval(uc)
]
category_instance = Category.objects.filter(category__in=categories)
Однако я бы посоветовал работать с ManyToManyField
[Django-doc] для привязки аккаунтов к категориям, например:
class UserCategoryFilter(models.Model):
# ⋮
user = models.ForeignKey(Account, on_delete=models.CASCADE)
categories_selected = models.ManyToManyField(Category)
В этом случае вы можете легко фильтровать с помощью:
Category.objects.filter(usercategoryfilter__user=request.user)
Django может таким образом делать JOIN-ы над отношениями "многие ко многим", чтобы переместить работу с уровня Django/Python на уровень базы данных.