簡體   English   中英

django 管理站點返回 MultipleObjectsReturned 異常,帶有 inspectdb 導入的遺留數據庫和復合主鍵

[英]django admin site returns MultipleObjectsReturned exception with inspectdb imported legacy database and composite primary key

使用 inspectdb,我在 django 中導入了一個遺留數據庫,其中包含具有復合主鍵的實體。數據庫模式包含大約 200 個不同的實體,inspectdb 在這種情況下非常方便。

這是 mysql 中的架構:

CREATE TABLE `mymodel` (
  `id` bigint(20) unsigned NOT NULL DEFAULT '0',
  `siteid` bigint(20) unsigned NOT NULL DEFAULT '0',
...
  PRIMARY KEY (`siteid`,`id`),
...

在 django 中自動生成 model(使用 python manager.py inspectdb 導入)

class Mymodel(models.Model):
    id = models.PositiveBigIntegerField()
    siteid = models.PositiveBigIntegerField(primary_key=True)
...
    class Meta:
        managed = False
        db_table = 'mymodel'
        unique_together = (('siteid', 'id'),

我已經使用以下方法在管理站點中注冊了所有模型:

from django.contrib import admin
from django.apps import apps

app = apps.get_app_config('appname')

for model_name, model in app.models.items():
    admin.site.register(model)

完成所有工作后,我導航到管理站點並單擊“mymodel”部分中的任意 object,將返回以下異常:

appname.models.Content.MultipleObjectsReturned: get() returned more than one Mymodel-- it returned more than 20!

顯然,(至少在我看來是這樣)管理員正在使用 siteid 獲取 object,但它應該使用 Meta class 中的 unique_together。

關於如何通過一般配置解決此問題並讓管理站點模塊使用 unique_together 進行查詢的任何建議?

是的,你可以解決這個問題,但你付出了更多的努力。

首先你將 model-admin class 分離為 model Mymodel並自定義 model-admin class 方法:

  1. 由於 django admin build 在ChangeList class 中更改了 url,因此我們可以像 MymodelChangelist 一樣創建自定義Changelist class並將id字段值作為查詢參數傳遞。 我們將使用id字段值來獲取 object。

  2. 覆蓋get_object()方法以使用自定義查詢從查詢集中獲取 object

  3. 覆蓋 model-admin 的get_changelist()方法以設置您的自定義更改列表class

  4. 覆蓋save_model()方法以顯式保存 object。

管理員.py

class MymodelChangelist(ChangeList):
    # override changelist class

    def url_for_result(self, result):
        id = getattr(result, 'id')
        pk = getattr(result, self.pk_attname)
        url = reverse('admin:%s_%s_change' % (self.opts.app_label,
                                               self.opts.model_name),
                       args=(quote(pk),),
                       current_app=self.model_admin.admin_site.name)

        # Added `id` as query params to filter queryset to get unique object 
        url = url + "?id=" + str(id)
        return url


@admin.register(Mymodel)
class MymodelAdmin(admin.ModelAdmin):
    list_display = [
        'id', 'siteid', 'other_model_fields'
    ]

    def get_changelist(self, request, **kwargs):
        """
        Return the ChangeList class for use on the changelist page.
        """
        return MymodelChangelist

    def get_object(self, request, object_id, from_field=None):
        """
        Return an instance matching the field and value provided, the primary
        key is used if no field is provided. Return ``None`` if no match is
        found or the object_id fails validation.
        """
        queryset = self.get_queryset(request)
        model = queryset.model
        field = model._meta.pk if from_field is None else model._meta.get_field(from_field)
        try:
            object_id = field.to_python(object_id)
            # get id field value from query params
            id = request.GET.get('id')
            return queryset.get(**{'id': id, 'siteid': object_id})
        except (model.DoesNotExist, ValidationError, ValueError):
            return None

    def save_model(self, request, obj, form, change):
        cleaned_data = form.cleaned_data
        if change:
            id = cleaned_data.get('id')
            siteid = cleaned_data.get('siteid')
            other_fields = cleaned_data.get('other_fields')
            self.model.objects.filter(id=id, siteid=siteid).update(other_fields=other_fields)
        else:
            obj.save()

現在您可以更新任何對象並添加新的 object。但是,在另外一種情況下,您無法siteid which is already added because of primary key validation

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM