Django 2.0 Admin" Add&#34 ;,在另一个模型中按外键过滤

时间:2018-03-03 04:32:34

标签: django

在我正在为一间小型办公楼工作的项目中,我的模型包括FloorsLocations,一直到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并不是我想要使用的内容。

我在这里错过了什么概念?非常感谢提前。

1 个答案:

答案 0 :(得分:1)

经过另外几个小时的谷歌搜索,我变得顽固并想通了。因此,为了全人类寻求这个的好处,我将把它全部发布。这是一种非常黑客攻击的方法,但毫无疑问可以重构DRY。 (注:我在Reddit上的同一个问题中交叉发布)。

我的课程与上面几乎相同,但我将楼宇作为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的旧帖子。

有了这个,我应该吃点东西。