所以这是 ads/views.py 还有 ads/models.py、ads/forms、ads/urls.py 和其他文件,但评分器抱怨的是这个 views.py...
检索到 3806 个 HTML 字符 测试已完成:在页面顶部发现菜单栏 搜索 "HHGTTG_42 1717639962 "时发现多个广告。 您可能需要在 views.py 中的 select_related().distinct() 后添加 distinct() ...
因此,我尝试使用 select_related().distinct(),并在其后添加它,但没有奏效:(
)views.py 部分导致的问题:
class AdListView(ListView):
model = Ad # 指定你正在使用的模型
template_name = "ads/ad_list.html";
def get(self, request):
strval = request.GET.get("search", False)
if strval:
query = Q(title__icontains=strval) | Q(text__icontains=strval) | Q(tags__name__in=[strval])
#query.add(Q(text__icontains=strval), Q.OR)
#query.add(Q(tags__name__in=[strval]), Q.OR)
ad_list = Ad.objects.filter(query).order_by('-updated_at').distinct()
否则
ad_list = Ad.objects.all().order_by('-updated_at')
# 明确应用 distinct()
# ad_list = ad_list.distinct()
for obj in ad_list:
obj.natural_updated = naturaltime(obj.updated_at)
if request.user.is_authenticated:
favorites = list(request.user.favorite_ads.values_list('id', flat=True))
否则
favorites = []
ctx = {'ad_list': ad_list, 'favorites': favorites, 'search': strval}
return render(request, self.template_name, ctx)
models.py
models.py
from django.db import models
from django.conf import settings
from django.core.validators import MinLengthValidator
from taggit.managers import TaggableManager
类 Ad(models.Model):
title = models.CharField(
max_length=200、
validators=[MinLengthValidator(2, "标题必须大于 2 个字符")] )
)
price = models.DecimalField( max_digits=7, decimal_places=2, null=True)
text = models.TextField()
标签 = TaggableManager(blank=True)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='tagme_owner')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
图片 = models.BinaryField(null=True, blank=True, editable=True)
content_type = models.CharField(max_length=256, null=True, blank=True, help_text='The MIMEType of the file')
comments = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Comment', related_name='tagme_comments')
favorites = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Fav', related_name='favorite_ads')
def __str__(self):
return self.title
类 Comment(models.Model):
text = models.TextField(
validators=[MinLengthValidator(3, "注释必须大于 3 个字符")] )
)
ad = models.ForeignKey(Ad, on_delete=models.CASCADE)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.text[:20] + ("..." if len(self.text) > 20 else "")
类 Fav(models.Model):
ad = models.ForeignKey(Ad, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.user.username + " likes " + self.ad.title
forms.py
from django import forms
from ads.models import Ad, Comment
from django.core.files.uploadedfile import InMemoryUploadedFile
from ads.humanize import naturalsize
from taggit.forms import TagWidget
类 CreateForm(forms.ModelForm):
max_upload_limit = 2 * 1024 * 1024
max_upload_limit_text = naturalsize(max_upload_limit)
picture = forms.FileField(required=False, label='要上传的文件 <= '+max_upload_limit_text)
upload_field_name = '图片
类 元
model = Ad
fields = ['title', 'price', 'text', 'tags', 'picture'] 字段
小部件 = {
标签TagWidget()、
}
def clean(self):
cleaned_data = super().clean()
pic = cleaned_data.get('picture')
if pic and isinstance(pic, InMemoryUploadedFile):
if pic.size > self.max_upload_limit:
self.add_error('picture', "File must be < " + self.max_upload_limit_text + " bytes")
返回 cleaned_data
def save(self, commit=True):
instance = super(CreateForm, self).save(commit=False)
f = instance.picture
if isinstance(f, InMemoryUploadedFile):
bytearr = f.read()
instance.content_type = f.content_type
实例.图片 = bytearr
如果提交
instance.save()
self.save_m2m() # 保存多对多字段,如标签
返回实例
class CommentForm(forms.ModelForm):
类 Meta:
model = Comment
fields = ['text]
类
问题出在对 Django ORM 中
distinct()
的使用上。 尽管错误消息建议在
select_related().distinct()
之后添加
distinct()
,但这实际上不是正确的解决方案,而且
select_related().distinct()
本身就是不正确的。
正在基于与
tags__name
字段相关的查询来获取重复的广告。 这就是
distinct()
看起来无效的原因 - 它正在工作,但它正在返回基于数据库中唯一行的不同
组合
,这些组合仍然包含重复的广告。
可以通过以下两种方法之一解决此问题:
1. 使用
set
在 Python 中移除重复项:
这是在检索查询集后移除重复项的最简单方法。 可以在的视图中修改
AdListView
的
get()
方法,如下所示:
```python class AdListView(ListView): model = Ad template_name = "ads/ad_list.html"
def get(self, request):
# ...(的其他代码)...
if strval:
# ...(的查询代码)...
# 使用 set 从查询集中移除重复的广告
ad_list = list(set(Ad.objects.filter(query).order_by('-updated_at')))
else:
# ...(的其他代码)...
# ...(的其他代码)...
```
此代码首先像以前一样运行的查询。 然后,它使用
set(ad_list)
创建一个唯一的
Ad
对象集。 最后,它使用
list()
将集合转换回列表。 这会保留原始查询的顺序。
2. 使用
Subquery
优化的数据库查询:
对于更大的数据集,更有效的解决方案是在数据库级别防止重复项。 可以使用 Django 的
Subquery
表达式来实现此目的。 这会告诉数据库仅选择具有不同
id
的广告:
```python from django.db.models import OuterRef, Subquery
class AdListView(ListView): # ...(的其他代码)...
def get(self, request):
# ...
if strval:
query = Q(title__icontains=strval) | Q(text__icontains=strval) | Q(tags__name__in=[strval])
ad_list = Ad.objects.filter(query).annotate(
newest=Subquery(
Ad.objects.filter(id=OuterRef('id')).order_by('-updated_at').values('updated_at')[:1]
)
).order_by('-newest').distinct()
# ...(的其他代码)...
```
此代码在数据库级别创建了一个对
Ad
的子查询,获取每个广告的最新
updated_at
,并将结果注释回主查询集。 然后,它使用注释的
newest
字段进行排序和区分,从而确保仅检索唯一的广告。
注意:
此方法假设
Ad
模型具有
id
作为主键。
选择哪种解决方案取决于的应用程序的具体需求和数据集的大小。 如果的数据集很小,则在 Python 中移除重复项可能更容易。 但是,对于更大的数据集,优化数据库查询将更有效率。
标签:python,mysql,django,distinct,django-select-related From: 78584218