![](/img/trans.png)
[英]Django TypeError: 'bar' is an invalid keyword argument for this function
[英]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)
每當我創建此模型的新實例時,我都會獲得credit
, resources
和description
字段的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 Option
和django/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
與參數cls
和name
。
但是,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
實例)到一個set
, set
只是一個沒有重復的對象集合。
發生了更多的事情,但我只想結束這里,因為我認為此示例的其他所有內容都是相當多余的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.