Как отфильтровать набор запросов по чужому полю 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 на уровень базы данных.

Вернуться на верх