我使用IntegerRangeField(PostgreSQL的特定字段)来表示发布bancnote时的年份。
在向db添加对象并使用非空范围(例如1990-2000)进行过滤时工作正常,但是很少有bancnotes,仅在一年内发布,因此该期间例如是2005-2005。设置此范围时,在将对象添加到db。
后,此值将变为“None-None”似乎IntegerRangeField不接受空范围。
我试图将范围设置为'2005-(此处没有值)',空白= True,null = True,这对我来说很好,但在这种情况下,过滤器不能用于此对象。
要更清楚地看下面的例子:
还在考虑使用DateRangeField,但它在输入的同时提供日期和月份,这让人感到很乱。还有其他可行和正确的方法吗?
希望我对自己的情况很清楚,期待任何建议。请随时问,我会提供任何信息。提前谢谢!
这是我的 models.py
from django.contrib.postgres.fields import IntegerRangeField
from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import models
from datetime import datetime
class Bancnote(models.Model):
Dollar= 'Dollar'
Euro= 'Euro'
TYPE_CHOICES = (
(Dollar, 'Dollar'),
(Euro, 'Euro')
)
type = models.CharField(max_length=11, choices=TYPE_CHOICES, default=Dollar)
par = models.PositiveIntegerField()
year = IntegerRangeField(null=True, blank=True)
size = models.CharField(max_length=7)
sign = models.CharField(max_length=20)
desc = models.TextField(max_length=200)
image = models.ImageField(upload_to='bons_images')
def __str__(self):
return str(self.par) + ' ' + self.type + ' ' + str(self.year.lower) + '-' + str(self.year.upper)
filters.py
from django import forms
import django_filters
from .models import Bancnote
class BancnoteFilter(django_filters.FilterSet):
type = django_filters.ChoiceFilter(name='type', choices=Bancnote.TYPE_CHOICES,
widget=forms.RadioSelect(attrs={'class': 'radio'}), empty_label=None)
par_gt = django_filters.NumberFilter(name='par', lookup_expr='gte', widget=forms.Select)
par_lt = django_filters.NumberFilter(name='par', lookup_expr='lte', widget=forms.Select)
year = django_filters.NumericRangeFilter(name='year', lookup_expr='contained_by')
class Meta:
model = Bancnote
fields = ['type', 'par_gt', 'par_lt', 'year']
views.py
from django.shortcuts import render, render_to_response
from django.template import RequestContext
from django.shortcuts import render
from .models import Bancnote
from .filters import BancnoteFilter
def index(request):
bons_list = Bancnote.objects.all().order_by('par')
bons_filter = BancnoteFilter(request.GET, queryset=bons_list)
def image(request):
bons = Bancnote()
variables = RequestContext(request, {
'bons': bons
})
return render_to_response('catalogue/bon_detail.html', variables)
的index.html
{% extends 'catalogue/base.html' %}
{% block title %}Catalogue{% endblock %}
{% load widget_tweaks %}
{% block sidebar %}
<form method="get">
<div class="well bs-sidebar" id="style" style="background-color:#fff">
<ul class="nav nav-pills nav-stacked">
<li>
<a href="#" class="toggle-menu" onclick="showcontent('#money-type')">Bancnote type
<i class="fa fa-chevron-up"></i>
</a>
</li>
<div id="money-type">
<ul class="nav nav-pills nav-stacked">
{% for type in filter.form.type %}
<li><a href="#">
{{ type.tag }}
<label for="{{ type.id_for_label }}">{{ type.choice_label }}</label>
</a></li>
{% endfor %}
</ul>
</div>
<li>
<a href="#" class="toggle-menu" onclick="showcontent('#par')">Bancnote par
<i class="fa fa-chevron-up"></i>
</a>
</li>
<div id="par">
{% render_field filter.form.par_gt id='from' %}{% render_field filter.form.par_lt id='to' %}
</div>
<li><a href="#" class="toggle-menu" onclick="showcontent('#period')">Issue years
<i class="fa fa-chevron-up"></i></a></li>
<div id="period">
<div class="range-input">
{% render_field filter.form.year maxlength='4' %}
</div>
<div class="range-slider">
<input value="1917" min="1917" max="2017" step="1" type="range">
<input value="2017" min="1917" max="2017" step="1" type="range">
</div>
</div>
</ul>
<button type="submit" class="btn btn-primary" style="text-align: center; width: 100%">
<span class="glyphicon glyphicon-search"></span> Search
</button>
</div>
</form>
{% endblock %}
{% block content %}
<div class="container-fluid">
<div class="row">
{# filtering here #}
{% for bon in filter.qs %}
{% if forloop.counter0|divisibleby:"4" %}
</div>
<div class="row">
{% endif %}
<div class="col-sm-3 col-lg-3">
<div style="display: block; text-align: center; margin: 0 auto">
<a href="{{ bon.id }}">
<img src="{{ bon.image.url }}" style="width: 50%; height: 50%"/>
<h5>{{ bon.par }} {{ bon.type }} {{ bon.year.lower}}-{{ bon.year.upper }}</h5>
</a>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
答案 0 :(得分:2)
主要问题是您没有正确存储范围。根据django postgres docs,
所有范围字段都在python中转换为psycopg2 Range objects,但如果不需要边界信息,也会接受元组作为输入。默认值为包含下限,上限排除;也就是
[)
。
简而言之,
tuple(2005, None)
无效,因为postgres有效地将空边界视为无穷大。过滤失败,因为无限远在你的边界检查之外。tuple(2005, 2005)
无效,因为Django默认为排除的上限。 Postgres将[2005,2005)
标准化为空,因为范围实际上并不存在。tuple(2003, 2008)
的范围不正确,因为2008年不包括在内。我很确定如果你过滤了2008年到2010年的范围,它将被排除在外。要表示一年,您要使用:
tuple(2005, 2006)
Range(2005, 2005, bounds='[]')
请注意,bounds是一个字符串文字(psycopg2 docs)。关于过滤,过滤器会执行startwsith
和endswith
检查,我认为这是包含。因此,在2005年到2005年之间过滤您的年份应该从DB中获得预期值。正确存储范围后,过滤器应正常运行。
Postgres范围docs以供参考。第8.17.5节包含有关边界行为和标准化的信息。
示例范围:
from psycopg2.extras import NumericRange
NumericRange(2005, 2008, bounds='[]')