我得到了一个 Invoice 模型,其字段
created_at
为 models.DateTimeField(),其中包含一些包含多个日期的记录:
使用 HTML 表单,我想按日期过滤
<input name="start" class="form-control me-3" type="date" value="{{ min_date|date:'Y-n-d' }}" />
我的结束日期有相同的字段,当我在视图中打印它们时,它们似乎采用正确的格式
2023-11-12 -> 2023-11-17
。
在我看来,我检查这两个字段是否包含值,如果是,我过滤我的 Invoice 查询集:
我尝试了很多组合进行查询集过滤,每次,__lte子句似乎都无法正常工作,并且结果不包括我的end日期。应用
__lt
而不是 __lte
。在我尝试过的这些组合中:
if request.method == 'GET':
# limit records number if no filter
invoices = Invoice.objects.all()[:100]
else:
start = request.POST.get('startDate')
end = request.POST.get('endDate')
invoices = Invoice.objects.all()
if start:
invoices = invoices.filter(created_at__gte=start)
if end:
invoices = invoices.filter(created_at__lt=end)
也尝试过:
[...]
else:
start = request.POST.get('start')
end = request.POST.get('end')
if start and end:
invoices = invoices.filter(
Q(created_at__gte=start) & Q(created_at__lte=end))
if end and not start:
invoices = invoices.filter(Q(created_at__lte=end))
if not end and start:
invoices = invoices.filter(Q(created_at__gte=start))
首先,我认为这是由于关于
("DateTimeField %s received a naive datetime (%s)
的警告,即使有了 make_aware
,问题仍然存在。我尝试在每一行上重建基本查询集。
else:
start = request.POST.get('start')
end = request.POST.get('end')
tz_start = make_aware(datetime.strptime(start, '%Y-%m-%d'))
tz_end = make_aware(datetime.strptime(end, '%Y-%m-%d'))
if not start and end:
invoices = Invoice.objects.filter(Q(created_at__lte=tz_end))
elif start and end:
invoices = Invoice.objects.filter(
Q(created_at__gte=tz_start) & Q(created_at__lte=tz_end))
elif start and not end:
invoices = Invoice.objects.filter(
Q(created_at__gte=tz_start))
else:
invoices = Invoice.objects.all()
messages.error(request, 'no filter applied')
是不是我错过了这么大的事情,以至于我都看不到它?
这个问题与 MySql、时区或其他与环境无关的问题之间没有关系。
经过 3 天的努力寻找解决方案,此页面让我走上了正确的道路
我不知道我的问题是否是因为我试图将日期输入与模型的 DateTimeField 进行比较,或者这种异常是否特定于使用 django_filters 进行日期过滤,但最后,django_filters.DateFromToRangeFilter拯救了我的一天(我应该说,我的一周)。代码减少了 3 倍,我的观点现在可以工作了。
过滤器.py
import django_filters
from websales.models import Invoice
class InvoiceFilter(django_filters.FilterSet):
created_at = django_filters.DateFromToRangeFilter(
field_name='created_at',
)
class Meta:
model = Invoice
fields = ['created_at']
views.py
def invoices_with_filter(request):
invoice_filter = InvoiceFilter(request.GET, queryset=Invoice.objects.all())
context = {}
max_min = invoice_filter.qs.aggregate(Min(
'created_at'), Max('created_at'))
context['max_date'] = max_min['created_at__max']
context['min_date'] = max_min['created_at__min']
context['invoices'] = invoice_filter.qs
context['form'] = invoice_filter.form
if invoice_filter.qs.count() == 0:
messages.error(request, 'Aucune donnée correspondant aux filtres !')
return render(request, 'websales/invoices.html', context=context)
对于那些想要 boostrap5 轻松地在模板中使用表单的人(我也花了时间找到方法),只需将 invoice_filter.form 的输入名称命名如下:
created_at_before
和 created_at_after
<form method="get" action="{% url 'websales:invoices' %}" class="row gy-2 gx-3 align-items-center mb-4">
{% csrf_token %}
<div class="col-auto">
<div class="input-group">
<div class="input-group-text" id="start">Début</div>
<input name="created_at_before" class="form-control me-3" type="date"
value="{{ min_date|date:'Y-n-d' }}" />
</div>
</div>
<div class="col-auto me-4">
<div class="input-group">
<div class="input-group-text" id="end">fin</div>
<input name="created_at_before" class="form-control" type="date" value="{{ max_date|date:'Y-n-d' }}" />
</div>
</div>
<div class="col-auto">
<button class="btn btn-success me-3" role="submit">Filtrer</button>
<a class="btn btn-outline-secondary me-3" role="button" href="{% url 'websales:invoices' %}">Reset</a>
<a class="btn btn-warning" role="button" href="#">Export CSV</a>
</div>
</form>
我还用 value="{{ max_date|date:'Y-n-d' }}"
预填充了输入字段min/max 日期
瞧!