I have the following pre-existing database table which contains and id and a description. I have to load this prior to my User table in order to associate the ForeignKey correctly.
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'
My custom user model is built on the following:
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)
Everything works with the makemigrations and migrate, but if a User.coid doesn't exist in the database I get the following error while attempting to login:
ValueError: Cannot assign "'08732'": "User.coid" must be a "QVDSSSecurityDimension" instance.
I verified that coid does exists in the QVDSSSecurityDimension, but since there are no Users in the table with the coid it's throwing that error. How can I make my login still function properly if a user verifies with AD and their coid doesn't exist in the custom user table yet?
I've tried Null = True and Blank = True with db_constraint = True and nothing seems to work. The coid will exist after the user is stored to the database, but this error happens prior to that occurring.
Here is my profile view
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)
I'm able to get the coid from my formattedusername without the DSSSecurityDimension because it pulls it from AD, however i need the description from DSSSecurityDimension, which is why I pull in the coid on the profile template, but from User.coid not DSSSecurityDimension.coid.
Coid in my template is rendered with the following li
<li>Coid: {{ user.coid }}</li>
The line of code is in the python libraries and not my view, which tells me its an issue with my model:
Traceback (most recent call last):
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\core\handlers\exception.py", line 41, in inner
response = get_response(request)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\core\handlers\base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\core\handlers\base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\contrib\auth\views.py", line 54, in inner
return func(*args, **kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\contrib\auth\views.py", line 150, in login
)(request)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\views\generic\base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\utils\decorators.py", line 67, in _wrapper
return bound_func(*args, **kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\views\decorators\debug.py", line 76, in sensitive_post_parameters_w
rapper
return view(request, *args, **kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\utils\decorators.py", line 63, in bound_func
return func.__get__(self, type(self))(*args2, **kwargs2)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\utils\decorators.py", line 67, in _wrapper
return bound_func(*args, **kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\utils\decorators.py", line 149, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\utils\decorators.py", line 63, in bound_func
return func.__get__(self, type(self))(*args2, **kwargs2)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\utils\decorators.py", line 67, in _wrapper
return bound_func(*args, **kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\views\decorators\cache.py", line 57, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\utils\decorators.py", line 63, in bound_func
return func.__get__(self, type(self))(*args2, **kwargs2)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\contrib\auth\views.py", line 90, in dispatch
return super(LoginView, self).dispatch(request, *args, **kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\views\generic\base.py", line 88, in dispatch
return handler(request, *args, **kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\views\generic\edit.py", line 182, in post
if form.is_valid():
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\forms\forms.py", line 183, in is_valid
return self.is_bound and not self.errors
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\forms\forms.py", line 175, in errors
self.full_clean()
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\forms\forms.py", line 385, in full_clean
self._clean_form()
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\forms\forms.py", line 412, in _clean_form
cleaned_data = self.clean()
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\contrib\auth\forms.py", line 187, in clean
self.user_cache = authenticate(self.request, username=username, password=password)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\contrib\auth\__init__.py", line 70, in authenticate
user = _authenticate_with_backend(backend, backend_path, request, credentials)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\contrib\auth\__init__.py", line 115, in _authenticate_with_backend
return backend.authenticate(*args, **credentials)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django_python3_ldap\auth.py", line 23, in authenticate
return ldap.authenticate(*args, **kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django_python3_ldap\ldap.py", line 205, in authenticate
return c.get_user(**kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django_python3_ldap\ldap.py", line 115, in get_user
return self._get_or_create_user(self._connection.response[0])
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django_python3_ldap\ldap.py", line 67, in _get_or_create_user
**user_lookup
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\db\models\query.py", line 482, in update_or_create
obj, created = self._create_object_from_params(lookup, params)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\db\models\query.py", line 498, in _create_object_from_params
obj = self.create(**params)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\db\models\query.py", line 392, in create
obj = self.model(**kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\contrib\auth\base_user.py", line 68, in __init__
super(AbstractBaseUser, self).__init__(*args, **kwargs)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\db\models\base.py", line 554, in __init__
_setattr(self, field.name, rel_obj)
File "C:\Users\HFA9592\AppData\Local\Programs\Python\Python36\lib\site-packages\django\db\models\fields\related_descriptors.py", line 216, in __set__
self.field.remote_field.model._meta.object_name,
ValueError: Cannot assign "'08732'": "User.coid" must be a "QVDSSSecurityDimension" instance.
The login is handled in my settings.py
with the following:
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"
I think I see the problem. You expect User
's coid
field to contain the actual ID of the related QVDSSSecurityDimension
object, but this is not how ForeignKey
works by default.
The User
's coid
field is actually an attribute that exists only inside your Python logic. At database level, there's no coid
column in the User
table. This is because Django creates another field that is not explicitly declared in your model, called coid_id
, to hold the related object's ID. That is the column and value that actually exists in the database.
In other words, if you list the attributes of a User
object, you'll see it has two fields: coid
and coid_id
. coid
will retrieve an instance of QVDSSSecurityDimension
, while coid_id
will hold the actual value you're trying to set.
Check Django's documentation regarding this. This post can also help you understand how coid
and coid_id
work.
This is what I think you're looking for:
class User(AbstractBaseUser, PermissionsMixin):
...
co = models.ForeignKey(QVDSSSecurityDimension, null=True, blank=True)
...
I renamed coid
to co
to make it more intuitive, since it doesn't hold the actual ID. I also removed db_constraint=False
, since the default db_constraint=True
should work just fine.
And when you try to set the QVDSSSecurityDimension
to your user, you can either do:
some_user = User.objects.first()
some_dimension = QVDSSSecurityDimension.objects.get(coid='08732')
some_user.co = some_dimension
some_user.save()
or:
some_user = User.objects.first()
some_dimension = QVDSSSecurityDimension.objects.get(coid='08732')
some_user.co_id = some_dimension.coid
some_user.save()
or:
some_user = User.objects.first()
some_dimension_id = '08732'
some_user.co_id = some_dimension_id
some_user.save()
See the difference? I hope this helps clear things up a bit. :)
EDIT
Specifically, the problem was in the settings variable LDAP_AUTH_USER_FIELDS
, that was trying to map the ID inside extensionAttribute10
(data received from LDAP
) to the User
's coid
field, which expected a QVDSSSecurityDimension
instance. The solution was to change that key in the LDAP_AUTH_USER_FIELDS
from coid
to coid_id
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With