【问题标题】:How can I access the current instance of my model when in the Admin Change Form?在管理员更改表单中如何访问我的模型的当前实例?
【发布时间】:2019-10-08 10:41:34
【问题描述】:

我正在尝试为想要跟踪销售的发布商创建一个数据管理系统。我有一个存储出版商书籍的书店模型,其中包括每个商店的状态字段(来自状态模型)。状态可能是“未设置”、“已收到订单”或“回电”。

除了能够查看当前状态之外,客户还希望能够在检查管理员更改表单上的书店数据时查看给定商店的状态更改:状态本身、日期和时间状态更改、进行更改的用户和一些有关更改的信息。因此,我创建了一个状态更改模型来存储这些数据,并添加了一个内联函数。

在书店的管理员更改表单中,我禁用了状态字段(以防止用户在不创建状态更改记录的情况下更改状态)并添加了一个“更改状态”链接,该链接将用户带到一个表单以允许他们创建一个新的状态变化记录。我一直无法解决的问题是:如何访问当前书店实例,以便将商店 id 及其当前状态传递给更改状态表单?

我可以从请求中毫无问题地获取用户的 ID。我可以使用 self.model.objects.first() 获取数据库中第一家书店的 ID,但是我无法找到一种方法来获取“更改状态”链接时正在查看的书店的 ID被点击。

在models.py中:

from django.contrib.auth import get_user_model
from django.db import models
from datetime import datetime


class Status(models.Model):
    DEFAULT_PK = 1
    status_name = models.CharField(max_length=255)


class Bookstore(models.Model):
    store_code = models.CharField(max_length=255)
    store_name = models.CharField(max_length=255)
    address = models.CharField(max_length=255, null=True, blank=True)
    phone_number = models.CharField(max_length=255, null=True, blank=True)
    description = models.CharField(max_length=1024, null=True, blank=True)
    status = models.ForeignKey(Status, on_delete=models.SET_DEFAULT, default=Status.DEFAULT_PK)


class StatusChange(models.Model):
    date_time = models.DateTimeField(default=datetime.now)
    user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
    bookstore = models.ForeignKey(Bookstore, on_delete=models.CASCADE)
    status = models.ForeignKey(Status, on_delete=models.CASCADE)
    notes = models.TextField()

在 admin.py 中:

from django.contrib import admin
from django.urls import path
from django.shortcuts import render
from django.utils.safestring import mark_safe
from django.forms.models import BaseInlineFormSet
from .models import Status, Bookstore, StatusChange
from .forms import StatusChangeForm


class StatusChangeFormSet(BaseInlineFormSet):
    def get_queryset(self):
        qs = super(StatusChangeFormSet, self).get_queryset()
        return qs[:5]


class StatusChangeInline(admin.TabularInline):
    model = StatusChange
    formset = StatusChangeFormSet
    fields =  ('date_time', 'user', 'status', 'notes')
    readonly_fields = ('date_time', 'user', 'status')
    can_delete = False
    max_num = 0
    ordering = ('-date_time',)


class BookstoreAdmin(admin.ModelAdmin):
    model = Bookstore
    list_display = ('id', 'store_code', 'store_name', 'status')
    list_display_links = ('id', 'store_code', 'store_name')
    ordering = ('id', )
    fields =('store_code', 'store_name', 'address', 'phone_number', 'description', 'status')
    inlines = [StatusChangeInline, ]

    # Disable the status field except if we are creating a new bookstore
    def get_readonly_fields(self, request, obj=None):
        if obj:
            return self.readonly_fields + ('status',)
        return self.readonly_fields

    def get_urls(self):
        urls = super().get_urls()
        my_urls = [path('statuschange/', self.change_store_status, name='statuschange'), ]
        return my_urls + urls

    def change_store_status(self, request):
        if request.method == 'POST':
            pass

        obj = self.model.objects.first()
        store_id = getattr(obj, 'id')
        store_status = getattr(obj, 'status')
        form = StatusChangeForm(
            initial={'user': request.user, 'bookstore': store_id, 'status': store_status}
        )
        payload = {"form": form}
        return render(request, "admin/change_status.html", payload)

admin.site.register(Bookstore, BookstoreAdmin)

这当然会将用户带到允许他们更改状态的表单...数据库中第一家书店的状态!

【问题讨论】:

    标签: python django


    【解决方案1】:

    目前您有书店更改的路径,例如:
    /admin/<app_name>/bookstore/<pk>/change/
    我建议您保留此 url 配置并为 statuschange 创建 url,例如:
    /admin/<app_name>/bookstore/<pk>/statuschange/

    假设您的StatusChangeForm 是:

    class StatusChangeForm(forms.ModelForm):
        class Meta:
            model = models.StatusChange
            fields = ['user', 'bookstore', 'status',]
    

    因此,为了做到这一点,您需要调整您的 get_urls() 以接受 pkbookstore

    def get_urls(self):
            urls = super().get_urls()
            my_urls = [path('<int:pk>/statuschange/', self.change_store_status, name='statuschange'), ]
            return my_urls + urls
    

    并修改您的 change_store_status() 以接收该 pk 并在获取 bookstore 实例时使用它:

    def change_store_status(self, request, pk):
            obj = self.model.objects.get(pk=pk)
    
            store_id = getattr(obj, 'id')
            store_status = getattr(obj, 'status')
            form = forms.StatusChangeForm(request.POST or None,
                initial={'user': request.user, 'bookstore': store_id, 'status': store_status}
            )
    
            if request.method == 'POST':
                if form.is_valid():
                    form.save()
    
            payload = {"form": form}
            return render(request, "admin/change_status.html", payload)
    

    您还必须在您的书店更改模板中提供pk 以指导用户正确填写表格,例如:
    {% url 'admin:statuschange' pk=original.pk %}

    【讨论】:

    • 非常感谢您的回复!请原谅我的无知——我对 Django 还是很陌生——但是我将如何按照你建议的方式创建 statuschange 的 url?
    • 你说added a "Change status" link, which takes users to a form to allow them to create a new status change record.。我的意思是将href 属性替换为我提供的代码。如果实际的 object 实例名称不同,可能无法正常工作,但请先尝试。
    • 我试过了。但我得到'NoReverseMatch at /admin/stores/bookstore/1/change/ Reverse for 'statuschange' with keyword arguments '{'pk': ''}' not found. 1 pattern(s) tried: ['admin/stores/bookstore/(?P&lt;pk&gt;[0-9]+)/statuschange/$']
    • @MatthewJames 嗯,没有亲自检查,但尝试{% url opts|admin_urlname:'statuschange' opts.id %}
    • 感谢您的建议,但我得到了同样的错误。查看类似的问题/错误,我觉得我真的应该在 urls.py 中提供额外的一行来支持 URL /admin/&lt;app_name&gt;/bookstore/&lt;pk&gt;/statuschange/。我会继续四处寻找......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-08
    • 2015-11-15
    • 1970-01-01
    • 2017-12-01
    • 2019-12-13
    • 1970-01-01
    相关资源
    最近更新 更多