在我正在为一间小型办公楼工作的项目中,我的模型包括Floors
,Locations
,一直到Assets
(工作站和打印机等) :
class Floor(models.Model):
name = models.CharField(max_length=20)
floornumber = models.IntegerField(default=1)
class Location(models.Model):
fkfloor = models.ForeignKey(Floor, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
isroom = models.BooleanField(default=False)
class Asset(models.Model):
name = models.CharField(max_length=50)
serialnumber = models.CharField(max_length=50)
class Workstation(models.Model):
name = models.CharField(max_length=30)
owner = models.CharField(max_length=20)
asset = models.OneToOneField(Asset, on_delete=models.CASCADE, primary_key=True)
在管理界面中,我需要一种在{1}}的OneToOne关系中添加Workstations
的方法,但需要使用该位置的过滤器以便整个Assets
列表(包括建筑物各部分的非工作站不会显示在Assets
的管理员添加/更改表单中。
我已经阅读了两本书并搜索了SO和Django文档,了解了我能想到的每一个术语组合,我想要完成的是什么,而我还没有找到一个有效的解决方案。我可以正常使用Workstations
来显示现有项目,但这并不会延续到管理添加/更改表单中。 list_filter
并不是我想要使用的内容。
我在这里错过了什么概念?非常感谢提前。
答案 0 :(得分:1)
我的课程与上面几乎相同,但我将楼宇作为FK添加到楼层。并不是说我有一个以上的建筑物IRL,但它有助于缩小测试数据的范围。 “ef”只是我“额外字段”的前缀。
#admin.py
class AssetForm(forms.ModelForm):
efbuilding = forms.ModelChoiceField(queryset=Building.objects.all(),label=u"Building",required=False)
effloor = forms.ModelChoiceField(queryset=Floor.objects.all(),label=u"Floor",required=False)
class Meta:
fields = '__all__'
model = Asset
class AssetAdmin(admin.ModelAdmin):
form = AssetForm
fieldsets = (
(None, {
'fields': ('efbuilding','effloor','fklocation'), #The 'extra' fields from above
}),
('Asset Information', {
'fields': ('name','serialnumber', ...)
}),
)
字段集会强制管理表单顶部的额外字段,它们看起来非常自然。 views.py和urls.py是下一个:
#views.py
from django.core import serializers
def addasset(request): # <-- note that this only needed "request". Putting something else there screwed things up.
if request.method == 'GET':
building_id = request.GET.get('id')
json_floor = serializers.serialize("json", Floor.objects.filter(fkbuilding_id=building_id))
return HttpResponse(json_floor) #Probably a terrible practice here with not using JsonResponse, but this worked
else:
return HttpResponse("No-go")
#urls.py (Needed to hook view, the URL doesn't really matter)
urlpatterns = [
...
path('addasset/', views.addasset), #<-- later versions of Django use "path" instead of regular expressions
]
我没有扩展Admin change_form.html,而是将其复制到自己的文件夹中以替换它以进行此测试,这样我就不会失去对模板标签的影响。由于这可以更清楚地记录下来,所以这正是如何工作的:
从虚拟环境目录中,复制:
/env_assettracker/Lib/site-packages/django/contrib/admin/templates/admin/change_form.html
...到项目内的新目录(我的项目目录在env目录中):
/env_assettracker/assettracker/templates/admin/assetapp/asset/change_form.html
一般而言,/ PROJECT / template / admin / APP / MODEL / change_form.html。这无需在settings.py文件中添加任何额外的DIRS。
在脚本之后的最后一个块change_form.htmlin的新副本的底部,我添加了自己的脚本:
#change_form.html
{% block admin_change_form_document_ready %}
<script>...the existing script for in the template...</script>
//add jQuery from my static files
<script src="{% static "jquery-3.3.1.min.js" %}"></script>
首先,由于这将使用AJAX,我使用了Django推荐的cookie方法。他们的文件的确切措辞工作得很好。我几乎将他们的代码块复制粘贴到我的新脚本部分的开头。
现在好的部分:
#change_form.html
$(function() {
...all the csfr token / cookie stuff...
$('#id_efbuilding').on('change', function(e) {
e.preventDefault();
$.getJSON("http://127.0.0.1:8000/addasset/",{id: $(this).val()}, function(j) {
var options = '<option value="">---??---</option>'; //blank value when nothing is selected in the box
for (var i = 0; i < j.length; i++) {
options += '<option value="' + parseInt(j[i].pk) + '">' + j[i].fields['name'] + '</option>';
}
$("#id_effloor").html(options);
$("#id_effloor option:first").attr('selected', 'selected');
});
$("id_efbuilding").attr('selected', 'selected');
});
});
这是我第一次使用jQuery和AJAX,所以我确信请求在Http和JSON之间混乱。远非优雅,但它的作品。现在选择建筑物会改变楼层中的可用选择,然后我想我会在那之后再做一个更改位置,然后相同的方法应该适用于一对一模型(尽管我会尝试把代码放在一个更可重用的地方)。
这些是从多个来源编译而来的。最有帮助的是来自2009 here的旧帖子。
有了这个,我应该吃点东西。