簡體   English   中英

Django TypeError(“'%s'是此函數的無效關鍵字參數”)

[英]Django TypeError(“'%s' is an invalid keyword argument for this function”)

所以我看到了關於這個錯誤的類似問題 ,它們似乎都與存在ManyToMany關系的用例有關。 但是,即使我的模型沒有M2M關系,我也會遇到這個問題,所以我想問一下為什么會發生這種情況。

這是我的模型:

class Course(models.Model):
    name = models.CharField(max_length=64)
    credit = models.IntegerField
    notes = models.CharField(max_length=128)
    resources = models.TextField
    description = models.TextField
    topic = models.CharField(max_length=128)

每當我創建此模型的新實例時,我都會獲得creditresourcesdescription字段的TypeError

我像這樣實例化它:

c = Course(
    name='some name',
    credit='8',
    notes='N/A',
    resources='no resources',
    description='N/A',
    topic='some topic'
)

但是,如果我將受影響的字段更改為models.IntegerField(max_length=8)models.TextField(max_length=8) ,則錯誤消失。

為什么會這樣? 我的印象是max_length參數對於TextField是可選的; 我甚至不知道在IntegerField的上下文中它意味着什么。 有人可以解釋這種行為,和/或我做錯了什么?

您將字段定義為其類的引用,而不是這些類的實例。 它應該是

class Course(models.Model):
    name = models.CharField(max_length=64)
    credit = models.IntegerField()
    notes = models.CharField(max_length=128)
    resources = models.TextField()
    description = models.TextField()
    topic = models.CharField(max_length=128)

如果有人感興趣我認為在django.db.models.ModelBase元類中發生的事情是相當有趣的。 回答這個問題過於冗長? 幾乎可以肯定,我本來不打算發帖,但也許它會有所幫助(假設我擁有的是正確的)。 我總是發現理解為什么有些東西有效而不是為什么沒有用。 我不打算試圖解釋每一個很重要的任務,並堅持與(希望)直接相關的東西。 ¯\\ _(ツ)_ /¯

(另外,我可能在某些地方犯了一些錯誤,我不經常深入研究源代碼,請隨意糾正

讓我們創建一個隨機模型:

class RandomModel(models.Model):

     field1 = models.TextField()

讓我們看看幕后發生了什么,這看起來很簡單。 對於更復雜的東西,它一直是烏龜。

我們有models.Model作為基類,但它有ModelBase因為它是元類。 而且, ModelBase有一個__new__方法:

這個元類的__new__方法的來源相當長。 這是django\\db\\models\\base.py文件內容的鏈接

還有另外兩個類我們將關注django/db/models/options.py -> class Optiondjango/db/models/fields/__init__.py -> class Field

你是打印dir(field1)還是只是在/fields查看__init__.py ,你會看到Field類和一個名為contribute_to_class的方法。

請記住contribute_to_class方法,稍后會詳細介紹。

讓我們分解__new__方法的相關組件。 如果您曾見過元類,那么您應該熟悉新函數的參數。

如果沒有,那么請檢查此鏈接: 什么是Python中的元類

class ModelBase(type):

    def __new__(..., attrs):

         ....

這里的attrs是包含所有方法,屬性等的字典。

事情發生了,然后我們有這條線:

new_class.add_to_class('_meta', Options(meta, app_label))

現在,什么是添加到課堂? 這是這個功能:

def add_to_class(cls, name, value):

    # We should call the contribute_to_class method only if it's bound
    if not inspect.isclass(value) and hasattr(value, 'contribute_to_class'):
        value.contribute_to_class(cls, name)
    else:
        setattr(cls, name, value)

我們可以在這里停下來,我本來可以開始使用這個功能。 這是整個問題。 您永遠不會初始化 models.FieldName,因此它不是傳遞條件hasattr(value, 'contribute_to_class')的實例。 相反,執行else語句,它只是成為一個類屬性(沒有貢獻)。 顧名思義, hasattr只是檢查對象是否具有可用的屬性(作為字符串傳遞給hasattr)。

如果您只使用field1 = models.TextField進行print(field1) ,您會發現它只是對類django.db.models.fields.TextField的引用。

回到正軌,讓我們繼續前進吧! 如果確實超過了這個條件怎么辦? 然后,我們有add_to_class現在調用contribute_to_class與參數clsname

但是,contrib_to_class做contribute_to_class什么?!

這是函數contribute_to_class

def contribute_to_class(self, cls, name, virtual_only=False):

   self.set_attributes_from_name(name)
   self.model = cls
   if virtual_only:
       cls._meta.add_field(self, virtual=True)
   else:
       cls._meta.add_field(self)
    if self.choices:
        setattr(cls, 'get_%s_display' % self.name,
                curry(cls._get_FIELD_display, field=self))

真的,我們在這里關心的是cls._meta.add_field(self)

現在,我們需要回溯。 在這一點上,事情可能開始變得混亂,但希望所有人都會在一瞬間清楚。

回想一下add_to_class行:

new_class.add_to_class('_meta', Options(meta, app_label))

根據您在某些時候使用Django的時間長度,很可能您使用的._meta屬性可能比此_meta.fields

好吧,這是!!

現在,召回(再次)的add_to_class功能Options類的實例有沒有方法contribute_to_class並因此添加到類作為屬性 _meta

我不會在這里粘貼contribute_to_class函數。 它也有點啰嗦,評論相當好。 你可以在這里找到它,大概是下降的1/4。 我建議閱讀評論,一旦你有功能應該或多或少直觀。

所以,現在有了這個新屬性_meta (它是一個Option類實例),它具有可用的類方法add_field

而且,這是add_field方法:

 def add_field(self, field, virtual=False):
    # Insert the given field in the order in which it was created, using
    # the "creation_counter" attribute of the field.
    # Move many-to-many related fields from self.fields into
    # self.many_to_many.
    if virtual:
        self.virtual_fields.append(field)
    elif field.is_relation and field.many_to_many:
        self.local_many_to_many.insert(bisect(self.local_many_to_many, field), field)
    else:
        self.local_fields.insert(bisect(self.local_fields, field), field)
        self.setup_pk(field)

評論再次相當自我解釋。 然而,可能令人困惑的是行bisect(list, thing_in_list) 這里, bisect只是python模塊bisect中bisect.bisect_right的縮寫 正如評論所說,字段按照創建順序插入,這是一個排序順序。 使用bisect允許列表在插入時保留排序順序,它可以找到正確的插入索引。

下一步是什么在__new__的方法ModelBase的元類?

我們有以下幾行:

 # Add all attributes to the class.
 for obj_name, obj in attrs.items():
      new_class.add_to_class(obj_name, obj)

現在,請記住, attrs是包含一切的詞典。 因此,它循環遍歷此字典的對象名稱(鍵)和對象(值)。 而且,正如前面所討論的尋找與實例contribute_to_class方法,否則它只是加入到類實例的屬性(要創建新的類記得__new__實際上這里創建類)。 而且,這個過程有點重復。

然后,接下來的兩行:

    new_fields = chain(
        new_class._meta.local_fields,
        new_class._meta.local_many_to_many,
        new_class._meta.virtual_fields
    )
    field_names = {f.name for f in new_fields}

在這里, itertools.chain將這些列表展平為一個列表並返回一個鏈對象。

然后迭代此鏈對象,從Field類調用屬性name (記住字段實際上是(子類) Field實例)到一個setset只是一個沒有重復的對象集合。

發生了更多的事情,但我只想結束這里,因為我認為此示例的其他所有內容都是相當多余的。

暫無
暫無

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

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