在 django 中使用通用外键过滤嵌套模型

问题描述 投票:0回答:1
class Complaint(models.Model):
    complaint_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True)
    user_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, related_name='complaint_user')
    user_id = models.PositiveIntegerField()
    user_object = GenericForeignKey('user_type', 'user_id')
    complaint_date = models.DateTimeField(auto_now_add=True)
    complaint_time = models.TimeField(auto_now_add=True)
    complaint_message = models.CharField(max_length=1000, editable=True)
    type_of_complaint = models.CharField(editable=True, default='public')
    status = models.CharField(editable=True, default='pending')
    complaint_title = models.CharField(max_length=100, editable=True)
    
    # Should be able to assign to multiple models such as Counsellor, ClassTeacher, HOD
    content_type = models.ForeignKey(to=ContentType, on_delete=models.CASCADE, related_name='complaint_owner')
    object_id = models.PositiveIntegerField()
    assigned_to = GenericForeignKey('content_type', 'object_id')
    
    escalated_to = models.ForeignKey(to=EscalationStructure, on_delete=models.CASCADE)

我正在尝试访问 user_object 模型中的字段,该字段是嵌套的 如

user_object.user.username
。我正在访问此搜索向量中的用户名


def search(request):
    query = request.GET.get('query', '')
    vector = SearchVector('complaint_title', 'complaint_message', 'status',
                          'complaint_time', 'complaint_date', 'counsellor_complaints', 'user_object__user__username')
    results = Complaint.objects.annotate(rank=SearchRank(vector, query)).filter(rank__gte=0.001).order_by('-rank')
    return render(request, 'complaint_administration/view_complaints.html', {'complaints': results})

它抛出一个错误说

Field 'user_object' does not generate an automatic reverse relation and therefore cannot be used for reverse querying. If it is a GenericForeignKey, consider adding a GenericRelation.

我没有访问反向关系权利。 我正在通过自我投诉访问??

django full-text-search
1个回答
0
投票

您遇到的错误是因为Django的SearchVector和SearchRank函数不支持通过GenericForeignKey遍历关系。它们旨在处理模型本身的常规外键关系和字段。在您的情况下,由于 user_object 是 GenericForeignKey,因此您不能直接将 user_object__user__username 包含在 SearchVector 中,因为它涉及遍历多个关系。 一种解决方案是使用 Prefetch 对象预取相关的用户对象以及投诉查询集,然后用用户的用户名注释查询集。

#search
from django.db.models import Prefetch, F

def search(request):
    query = request.GET.get('query', '')
    
    # Prefetch the related user object
    complaints_with_user = Complaint.objects.prefetch_related('user_object__user')
    
    # Annotate the queryset with the user's username
    complaints_with_username = complaints_with_user.annotate(
        user_username=F('user_object__user__username')
    )
    
    vector = SearchVector('complaint_title', 'complaint_message', 'status',
                          'complaint_time', 'complaint_date', 'user_username')
    
    results = complaints_with_username.annotate(rank=SearchRank(vector, query)).filter(rank__gte=0.001).order_by('-rank')
    
    return render(request, 'complaint_administration/view_complaints.html', {'complaints': results})

另一种方法是在投诉模型中创建一个非规范化字段来直接存储用户名。

#models.py
class Complaint(models.Model):
    ...
    # field to store the username directly for full-text search
    user_username = models.CharField(max_length=150, blank=True, null=True)

    def save(self, *args, **kwargs):
        if self.user_object:
            self.user_username = self.user_object.user.username
        super().save(*args, **kwargs)

和搜索:

def search(request):
    query = request.GET.get('query', '')
    vector = SearchVector(..., 'assigned_to__user_username')
    results = Complaint.objects.annotate(rank=SearchRank(vector, query)).filter(rank__gte=0.001).order_by('-rank')
    return render(request, 'complaint_administration/view_complaints.html', {'complaints': results})
© www.soinside.com 2019 - 2024. All rights reserved.