在 django-admin 中使用父外键、祖父母外键搜索

问题描述 投票:0回答:1

我希望能够搜索一个产品,其类别不直接链接到它,但该类别是祖父或曾祖父等等,在实际链接到产品本身的类别的类别树上。 我可以实现的唯一方法是使用以下方法,这当然是不可扩展的和正确的方法:

 def get_queryset(self, request):
        queryset = super().get_queryset(request).select_related('brand', 'category', 'category__parent')
        category_name = request.GET.get('category__name', None)

        if category_name:
            # Filter the queryset to include products with the specified category or its ancestors
            queryset = queryset.filter(
                Q(category__name=category_name) |
                Q(category__parent__name=category_name) |
                Q(category__parent__parent__name=category_name) |
                # Add more levels as needed based on the depth of your category tree
            )

        return queryset

这是目前的代码:

Product``models.py

class Product(models.Model):
    name = models.CharField(max_length=255)
    slug = models.SlugField(blank=True, null=True)
    description = models.TextField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    image = models.ImageField(upload_to='product_images/')
    category = models.ForeignKey(Category, on_delete=models.SET_NULL,related_name='product_category', null=True, blank=True)
    brand = models.ForeignKey(Brand, on_delete=models.SET_NULL, related_name='product_brand', null=True, blank=True)
    tags = GenericRelation(TaggedItem, related_query_name='product')
    ratings = GenericRelation(Rating, related_query_name='product')
    active = models.BooleanField(default=True)
    timestamp = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    state = models.CharField(max_length=2, choices=PublishStateOptions.choices, default=PublishStateOptions.DRAFT)
    publish_timestamp = models.DateTimeField(
        # means that the publish_timestamp field will not automatically be set to the current date and time when a new instance of this model is created.
        auto_now_add=False,
        # means that the publish_timestamp field will not automatically be updated to the current date and time every time the model is saved.
        auto_now=False,
        blank=True,
        null=True
    )

    def __str__(self):
        return self.name

Category``models.py

class Category(models.Model):
    name = models.CharField(max_length=255)
    parent = models.ForeignKey('self', on_delete=models.SET_NULL, related_name='subcategories', null=True, blank=True)

    class Meta:
        verbose_name_plural = 'Categories'

    def __str__(self):
        return self.name

Product``admin.py

class ProductAdmin(admin.ModelAdmin):
    list_display = ['name', 'price', 'brand', 'category']
    inlines = [TaggedItemInline]
    list_filter = [
        'brand__name',
        'category__name' 
    ]
    search_fields = ['name', 'price', 'brand__name', 'category__name', 'category__parent__name']

    def get_queryset(self, request):
        return super().get_queryset(request).select_related('brand', 'category', 'category__parent')

    def get_brand_name(self, obj):
        return obj.brand.name if obj.brand else None
    
    def get_category_name(self, obj):
        return obj.category.name if obj.category else None

    get_brand_name.short_description = 'Brand'
    get_category_name.short_description = 'Category'

    # Define the labels for list filters
    list_filter = (
        ('brand', admin.RelatedOnlyFieldListFilter),
        ('category', admin.RelatedOnlyFieldListFilter),
    )

admin.site.register(Product, ProductAdmin)
python django django-models django-admin django-admin-tools
1个回答
0
投票

一个小时后,我明白了。

def get_search_results(self, request, queryset, search_term):
    queryset, use_distinct = super().get_search_results(request, queryset, search_term)

    # Search for products based on ascendant category names recursively
    matching_categories = self.find_matching_categories(search_term)
    ascendant_categories = self.get_all_ascendant_categories(matching_categories)
    products_with_ascendant_categories = self.model.objects.filter(category__in=ascendant_categories)
    queryset |= products_with_ascendant_categories

    return queryset, use_distinct

def find_matching_categories(self, search_term):
    # Find categories that match the search term
    return Category.objects.filter(name__icontains=search_term)

def get_all_ascendant_categories(self, categories):
    # Recursively retrieve all ascendant categories
    ascendant_categories = list(categories)
    new_ascendants = Category.objects.filter(parent__in=categories)
    if new_ascendants:
        ascendant_categories.extend(self.get_all_ascendant_categories(new_ascendants))
    return ascendant_categories

当用户搜索产品时,他们现在可以使用该产品类别的上级名称。 get_search_results() 方法经过定制以实现此目的。当用户搜索时,代码会识别与搜索词匹配的类别,然后递归地收集这些类别的所有祖先。这可确保搜索结果中包含类别及其后代的产品。

© www.soinside.com 2019 - 2024. All rights reserved.