Django ValueError:无法分配必须是实例
我有以下预先存在的数据库表,其中包含和id以及描述。为了正确关联ForeignKey,我必须在用户表之前加载它Django ValueError:无法分配必须是实例,django,Django,我有以下预先存在的数据库表,其中包含和id以及描述。为了正确关联ForeignKey,我必须在用户表之前加载它 class QVDSSSecurityDimension(models.Model): coid = models.CharField(db_column='CM_Data_Restriction', serialize=False, max_length=10, primary_key = True) # Field name made lowercase. co
class QVDSSSecurityDimension(models.Model):
coid = models.CharField(db_column='CM_Data_Restriction', serialize=False, max_length=10, primary_key = True) # Field name made lowercase.
coid_name = models.CharField(db_column='CM_Company_Name', max_length=50, blank=True, null=True) # Field name made lowercase.
class Meta:
managed = False
db_table = 'QV_DSS_Security_Dimension'
我的自定义用户模型建立在以下基础上:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
username = models.CharField(max_length=7, unique=True)
formattedusername = models.CharField(max_length=11, unique=True, primary_key = True)
first_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=140)
date_joined = models.DateTimeField(default=timezone.now)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_cfo = models.BooleanField(default=False)
facility = models.CharField(max_length=140)
officename = models.CharField(max_length=100)
jobdescription = models.CharField(max_length=140)
positioncode = models.CharField(max_length = 100)
positiondescription = models.CharField(max_length=140)
coid = models.ForeignKey(QVDSSSecurityDimension, null=True, blank = True, db_constraint=False)
streetaddress = models.CharField(max_length=140)
title = models.CharField(max_length=100)
USERNAME_FIELD = 'username'
class Meta:
app_label = 'accounts'
db_table = "user"
def save(self, *args, **kwargs):
self.formattedusername = '{domain}\{username}'.format(
domain='HCA', username=self.username)
super(User, self).save(*args, **kwargs);
def get_short_name(self):
return self.username
# REQUIRED_FIELDS = "username"
def __str__(self):
return '%s - %s %s' % (self.username, self.first_name, self.last_name)
AUTHENTICATION_BACKENDS = (
'django_python3_ldap.auth.LDAPBackend',
'django.contrib.auth.backends.ModelBackend',
)
AUTH_USER_MODEL = "accounts.User"
# The URL of the LDAP server.
LDAP_AUTH_URL = "ldap://ip of server"
# Initiate TLS on connection.
LDAP_AUTH_USE_TLS = False
# The LDAP search base for looking up users.
LDAP_AUTH_SEARCH_BASE = "DC=domainname,DC=corpad,DC=net"
LDAP_AUTH_OBJECT_CLASS = "user"
# User model fields mapped to the LDAP
# attributes that represent them.
LDAP_AUTH_USER_FIELDS = {
"username": "sAMAccountName",
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
"coid": "extensionAttribute10",
"facility": "company",
"officename":"physicalDeliveryOfficeName",
"streetaddress": "streetAddress",
"jobdescription":"corpadNet2001-CORPds-JobCodeDescription",
"positioncode":"corpadNet2001-CORPds-PositionCode",
"positiondescription":"corpadNet2001-CORPds-PositionCodeDescription",
"title":"title",
}
# A tuple of django model fields used to uniquely identify a user.
LDAP_AUTH_USER_LOOKUP_FIELDS = ("username",)
# Path to a callable that takes a dict of {model_field_name: value},
# returning a dict of clean model data.
# Use this to customize how data loaded from LDAP is saved to the User model.
LDAP_AUTH_CLEAN_USER_DATA = "django_python3_ldap.utils.clean_user_data"
# Path to a callable that takes a user model and a dict of {ldap_field_name: [value]},
# and saves any additional user relationships based on the LDAP data.
# Use this to customize how data loaded from LDAP is saved to User model relations.
# For customizing non-related User model fields, use LDAP_AUTH_CLEAN_USER_DATA.
LDAP_AUTH_SYNC_USER_RELATIONS = "django_python3_ldap.utils.sync_user_relations"
# Path to a callable that takes a dict of {ldap_field_name: value},
# returning a list of [ldap_search_filter]. The search filters will then be AND'd
# together when creating the final search filter.
LDAP_AUTH_FORMAT_SEARCH_FILTERS = "django_python3_ldap.utils.format_search_filters"
# Path to a callable that takes a dict of {model_field_name: value}, and returns
# a string of the username to bind to the LDAP server.
# Use this to support different types of LDAP server.
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_active_directory"
一切都与makemigrations和migrate一起工作,但如果数据库中不存在User.coid,则在尝试登录时会出现以下错误:
ValueError: Cannot assign "'08732'": "User.coid" must be a "QVDSSSecurityDimension" instance.
我验证了QVDSSSecurityDimension中确实存在coid,但由于表中没有具有coid的用户,因此抛出了该错误。如果用户使用AD进行验证,并且自定义用户表中还不存在他们的coid,我如何使我的登录仍然正常工作
我尝试了Null=True和Blank=True,db_constraint=True,但似乎没有任何效果。coid将在用户存储到数据库后存在,但此错误发生在发生之前
这是我的个人资料
def profile(request):
owner = User.objects.get (formattedusername=request.user.formattedusername)
reportdetail = QVReportAccess.objects.filter(ntname = owner.formattedusername, active = 1).values('report_name_sc')
reportIds = QVReportAccess.objects.filter(ntname = owner.formattedusername).values_list('report_id', flat=True)
reportaccess = QvReportList.objects.filter(report_id__in= reportIds).values_list('report_name_sc', flat = True).distinct()
reportGroups = QVReportAccess.objects.filter(ntname = owner.formattedusername).values_list('report_group_id', flat=True)
reportlist = QvReportList.objects.filter(~Q(report_id__in= reportIds)).exclude(active=0)
allreports = 'All Reports'
if allreports in reportaccess:
bhreportgrouplist = None
cereportgrouplist = None
finreportgrouplist = None
careportgrouplist = None
pireportgrouplist = None
screportgrouplist = None
dssreportgrouplist = None
psgreportgrouplist = None
othreportgrouplist = None
showbutton = None
else:
bhreportgrouplist = QvReportList.objects.filter(~Q(report_id__in= reportIds)).filter(report_group_id = 200)
cereportgrouplist = QvReportList.objects.filter(~Q(report_id__in= reportIds)).filter(report_group_id = 500)
finreportgrouplist = QvReportList.objects.filter(~Q(report_id__in= reportIds)).filter(report_group_id = 600)
careportgrouplist = QvReportList.objects.filter(~Q(report_id__in= reportIds)).filter(report_group_id = 800)
pireportgrouplist = QvReportList.objects.filter(~Q(report_id__in= reportIds)).filter(report_group_id = 1100)
screportgrouplist = QvReportList.objects.filter(~Q(report_id__in= reportIds)).filter(report_group_id = 1200)
dssreportgrouplist = QvReportList.objects.filter(~Q(report_id__in= reportIds)).filter(report_group_id = 1300)
psgreportgrouplist = QvReportList.objects.filter(~Q(report_id__in= reportIds)).filter(report_group_id = 1400)
othreportgrouplist = QvReportList.objects.filter(~Q(report_id__in= reportIds)).filter(report_group_id = 99999)
showbutton = ""
args = {'user':owner, 'applicationaccess':reportaccess, 'applicationlist':reportlist, 'bhgrouplist':bhreportgrouplist, 'cegrouplist':cereportgrouplist, 'fingrouplist':finreportgrouplist
, 'cagrouplist':careportgrouplist, 'pigrouplist':pireportgrouplist, 'scgrouplist':screportgrouplist, 'dssgrouplist':dssreportgrouplist, 'psggrouplist':psgreportgrouplist
, 'othgrouplist':othreportgrouplist, 'showbutton':showbutton}
return render(request, 'accounts/profile.html', args)
我可以从格式化用户名中获取coid,而不需要DSSecurityDimension,因为它从AD中提取,但是我需要DSSecurityDimension中的描述,这就是为什么我在配置文件模板中提取coid,而不是从User.coid中提取DSSecurityDimension.coid
我的模板中的Coid使用以下li呈现
<li>Coid: {{ user.coid }}</li>
登录在my settings.py中处理
以下是:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
username = models.CharField(max_length=7, unique=True)
formattedusername = models.CharField(max_length=11, unique=True, primary_key = True)
first_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=140)
date_joined = models.DateTimeField(default=timezone.now)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_cfo = models.BooleanField(default=False)
facility = models.CharField(max_length=140)
officename = models.CharField(max_length=100)
jobdescription = models.CharField(max_length=140)
positioncode = models.CharField(max_length = 100)
positiondescription = models.CharField(max_length=140)
coid = models.ForeignKey(QVDSSSecurityDimension, null=True, blank = True, db_constraint=False)
streetaddress = models.CharField(max_length=140)
title = models.CharField(max_length=100)
USERNAME_FIELD = 'username'
class Meta:
app_label = 'accounts'
db_table = "user"
def save(self, *args, **kwargs):
self.formattedusername = '{domain}\{username}'.format(
domain='HCA', username=self.username)
super(User, self).save(*args, **kwargs);
def get_short_name(self):
return self.username
# REQUIRED_FIELDS = "username"
def __str__(self):
return '%s - %s %s' % (self.username, self.first_name, self.last_name)
AUTHENTICATION_BACKENDS = (
'django_python3_ldap.auth.LDAPBackend',
'django.contrib.auth.backends.ModelBackend',
)
AUTH_USER_MODEL = "accounts.User"
# The URL of the LDAP server.
LDAP_AUTH_URL = "ldap://ip of server"
# Initiate TLS on connection.
LDAP_AUTH_USE_TLS = False
# The LDAP search base for looking up users.
LDAP_AUTH_SEARCH_BASE = "DC=domainname,DC=corpad,DC=net"
LDAP_AUTH_OBJECT_CLASS = "user"
# User model fields mapped to the LDAP
# attributes that represent them.
LDAP_AUTH_USER_FIELDS = {
"username": "sAMAccountName",
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
"coid": "extensionAttribute10",
"facility": "company",
"officename":"physicalDeliveryOfficeName",
"streetaddress": "streetAddress",
"jobdescription":"corpadNet2001-CORPds-JobCodeDescription",
"positioncode":"corpadNet2001-CORPds-PositionCode",
"positiondescription":"corpadNet2001-CORPds-PositionCodeDescription",
"title":"title",
}
# A tuple of django model fields used to uniquely identify a user.
LDAP_AUTH_USER_LOOKUP_FIELDS = ("username",)
# Path to a callable that takes a dict of {model_field_name: value},
# returning a dict of clean model data.
# Use this to customize how data loaded from LDAP is saved to the User model.
LDAP_AUTH_CLEAN_USER_DATA = "django_python3_ldap.utils.clean_user_data"
# Path to a callable that takes a user model and a dict of {ldap_field_name: [value]},
# and saves any additional user relationships based on the LDAP data.
# Use this to customize how data loaded from LDAP is saved to User model relations.
# For customizing non-related User model fields, use LDAP_AUTH_CLEAN_USER_DATA.
LDAP_AUTH_SYNC_USER_RELATIONS = "django_python3_ldap.utils.sync_user_relations"
# Path to a callable that takes a dict of {ldap_field_name: value},
# returning a list of [ldap_search_filter]. The search filters will then be AND'd
# together when creating the final search filter.
LDAP_AUTH_FORMAT_SEARCH_FILTERS = "django_python3_ldap.utils.format_search_filters"
# Path to a callable that takes a dict of {model_field_name: value}, and returns
# a string of the username to bind to the LDAP server.
# Use this to support different types of LDAP server.
LDAP_AUTH_FORMAT_USERNAME = "django_python3_ldap.utils.format_username_active_directory"
我想我看到了问题所在。您希望
User
的coid
字段包含相关QVDSSSecurityDimension
对象的实际ID,但默认情况下,ForeignKey
不是这样工作的
User
的coid
字段实际上是一个仅存在于Python逻辑内部的属性。在数据库级别,User
表中没有coid
列。这是因为Django创建了另一个未在模型中显式声明的字段,称为coid\u id
,用于保存相关对象的id。该字段是数据库中实际存在的列和值
换句话说,如果列出用户
对象的属性,您将看到它有两个字段:coid
和coid\u id
coid
将检索QVDSSSecurityDimension
的实例,而coid\u id
将保存您尝试设置的实际值
检查一下这个。还可以帮助您了解coid
和coid\u id
的工作原理
这就是我认为你在寻找的:
class User(AbstractBaseUser, PermissionsMixin):
...
co = models.ForeignKey(QVDSSSecurityDimension, null=True, blank=True)
...
我将coid
重命名为co
,以使其更直观,因为它不包含实际ID。我还删除了db\u constraint=False
,因为默认的db\u constraint=True
应该可以正常工作
当您尝试为您的用户设置QVDSSSecurityDimension
时,您可以执行以下操作之一:
some_user = User.objects.first()
some_dimension = QVDSSSecurityDimension.objects.get(coid='08732')
some_user.co = some_dimension
some_user.save()
或:
或:
看到区别了吗?我希望这有助于把事情弄清楚一点
编辑
具体来说,问题出在设置变量
LDAP\u AUTH\u USER\u FIELDS
,该变量试图将extensionAttribute10
(从LDAP
接收的数据)内的ID映射到用户的coid
字段,该字段应为QVDSSSecurityDimension
实例。解决方案是将LDAP\u AUTH\u USER\u字段中的键从coid
更改为coid\u id
,您可以发布用于登录的视图吗?我在视图中添加了更多细节,但我相信问题在于数据建模的方式,因为我使用了ldap3身份验证。我的登录名是用户的网络登录名和密码,一旦用户被验证,数据库将把记录写入我的用户表,这是用户模型中的保存。然而,它甚至没有达到这一点,因为我的foreignkey的关系在数据库中没有coid。如果我用测试用户的coid手动添加一个虚拟用户,效果很好,因为coid已经存在于我的用户表中。也许我不清楚,但我想你可能误解了。我的用户模型是使用LDAP身份验证从active directory创建的。所有用户都有一个coid。一旦用户通过其网络登录进行验证,他们的AD用户信息将写入数据库表user。如果数据库中存在具有相同coid的现有用户,我当前的逻辑将起作用,但如果没有,它将抛出上面的错误。我只是想用QVDSSSecurityDimension在coid上创建一个关系,这样我就可以提取名称了。嗯,我明白了。但从错误判断,很明显,在某个时刻,您的代码试图将字符串分配给需要QVDSSSecurityDimension
实例的字段。你能编辑你的问题,包括你用来登录的代码吗?或者至少是根据回溯抛出错误的特定行中的代码。对于登录,没有自定义代码,所有代码都是ldap3库的一部分,一旦配置好就可以工作。登录后,我们不会使用DSS Security中的coid,仅在提交时使用,但如果用户中不存在coid,应用程序不会让我走那么远,而在首次部署应用程序时不会。我已经为个人资料添加了我的视图,这发生在登录之后。感谢您的编辑!好的,我的理论是问题出在django-python3-ldap
库的内部。听起来该方法将coid='08732'
作为参数,但实际上应该接收coid\u id='08732'
。你能在第115行前的C:\Users\HFA9592\AppData\Local\Programs\Python36\lib\site packages\django\u python3\u ldap\ldap.py
中设置一个断点,告诉我self.\u connection.response[0]
object?self.\u connection.response[0]是来自AD的用户信息。我把self打印出来。\u connection.response[0]以上。这是大量数据。
some_user = User.objects.first()
some_dimension_id = '08732'
some_user.co_id = some_dimension_id
some_user.save()