IntegrityError - Column 'user_id' cannot be null - python

I am trying to register a new user on the site,
class UserInfo(models.Model):
user = models.ForeignKey(User,primary_key=True)#user profile
email_id=models.CharField(max_length=32, null=True, blank=True)
When I am registering the user, I am getting stuck by Integrity Error, please help me to resolve the problem.
def registration(request):
registration_dict = {}
if 1==1 :
#if request.POST:
#username=request.POST['email']
#password=request.POST['password']
username="admin#admin.com"
password='123456'
#try:
UserInfo.objects.get_or_create(email_id=username,user__username=username,user__email=username,user__password=password)
#except:
# registration_dict["status"]="0"
# registration_dict["message"]="Username already present"
# return HttpResponse(simplejson.dumps(registration_dict),content_type="application/json")
registration_dict["status"]="1"
registration_dict["message"]="Thank You for registering"
return HttpResponse(simplejson.dumps(registration_dict),content_type="application/json")
else:
registration_dict["status"]="0"
registration_dict["message"]="Unable to process the request"
return HttpResponse(simplejson.dumps(registration_dict),content_type="application/json")
EDIT 1
I have tried changing
UserInfo.objects.get_or_create(email_id=username,user__username=username,user__email=username,user__password=password,user_id=1)
and then the error changes, to
'Cannot add or update a child row: a foreign key constraint fails (`app_info`.`appdata_userinfo`, CONSTRAINT `user_id_refs_id_b0fd803b` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`))')

From the limited information I would say the problem is
it does not find a UserInfo that matches. It then tries to create a new UserInfo, but it has no User to assign to the User ForeignKey. I would suggest the following:
user = authenticate(username=email, password=password)
if user is None:
user = User(username=email, password=password, email=email)
user_info = UserInfo.objects.get_or_create(user=user, email_id=email)

If the original User object doesn't exist, you'll run into all kinds of problems. So, you need to break the process down into two steps.
Check if a User object exists or not, if it doesn't create it.
Check if a UserInfo object exists for that user, if it doesn't create it.
As there is a ForeignKey, you cannot do it in one step:
username = "admin#admin.com"
password = '123456'
obj, created = User.objects.get_or_create(username=username)
obj.set_password(password) # the proper way to set the password
obj.save()
# Now fetch or create a UserInfo object
info, created = UserInfo.objects.get_or_create(email_id=username,user=obj)

I cant understand why you need UserInfo because email is already there in User.
Issue can be corrected by splitting the fetching process
username = "admin#admin.com"
password = '123456'
user,status = User.objects.get_or_create(username=username, password=password)
user_info = UserInfo.objects.get_or_create(user=user,email_id=username)

Related

How to delete special data from database by python django

For example, I have a table called BlackList in the database which looks like this:
and the model of the table is:
class BlackList(models.Model):
list = models.CharField(max_length=1000, null=True, blank=True)
What I try to do is:
if request.method == "POST":
username = request.POST.get('username') # Get username input first
password = request.POST.get('password')
user = authenticate(request, username=username, password=password)
BL = BlackList.objects.values_list('list', flat=True) # Read all data into array
if username in BL: # Check if the username is in blacklist
# Remove this username from the BlackList table
So my question is how to delete the special data for a special, for example, if 'aaa' try to log in, then 'aaa' will be removed or deleted from the BlackList table.
There is .delete method. I. e. try this:
if BlackList.filter(list=username).exists():
BlackList.objects.get(list=username).delete()
instead of:
BL = BlackList.objects.values_list('list', flat=True) # Read all data into array
if username in BL: # Check if the username is in blacklist
# Remove this username from the BlackList table
You can read more about that on https://docs.djangoproject.com/en/3.0/topics/db/queries/#deleting-objects and https://docs.djangoproject.com/en/3.0/ref/models/querysets/#exists
You can use filter() in combination with first() to get the exist username:
blacklist = Blacklist.objects.filter(username=username).first()
(This will return None if there is no match. If you use get(), you will get DoesNotExist error instead -- which is not preferred.)
After that, you can just delete it:
blacklist.delete()
It will take 2 queries (get and delete) to achieve your goal.
An alternative way is to delete it without getting the object:
Blacklist.objects.filter(username=username).delete()
This statement will be execute with only one query which is equals DELETE FROM ... WHERE username='username'

Not able to get old user instance before edits, and compare fields to get field edited

I have a model for logging changes and is attempting to get the fields edited upon user edit to log it. I'm currently trying to loop through the old user object and the user object after edits to compare the field(s) through object._meta.get_fields(), and the plan is to log/get the field(s) that are different.
One of the problems I have is that when I print the user object under ("#breakpoint 1"), I get the before-edited user object, but when I use it as a parameter in the method ("# breakpoint 2") it prints the edited user object, instead of the before-edited object.
How can I fix this? Alternatively, is there a better way to log the fields edited?
View
def editUser(request, pk):
# Query appropriate user based on pk returned in url
user = User.objects.get(pk = pk)
# Get the EditUserForm and add the user as instance
edit_user_form = EditUserForm(instance = user)
if request.method == 'POST':
# Bind data to the form class, and add the user as instance
edit_user_form = EditUserForm(request.POST, error_class=DivErrorList, instance = user)
old_user_instance = user
# breakpoint 1
print(old_user_instance)
# Validate form inputs
if edit_user_form.is_valid():
# Save edits
edit_user_form.save()
# Log change
ChangeLog.log_user_change(old_user_instance, request.user.id)
# Give the user successful feedback and redirect
messages.success(request, successMessage('Redigering', 'bruker'))
return redirect('user', pk)
else:
# If form inputs is invalid, give user feedback
messages.error(request, 'Error')
context = {
'user': user,
'edit_user_form': edit_user_form,
}
# Render request, template and context
return render(request, 'users/backend/user/user_edit.html', context)
log_user_change method
The method is attached to the ChangeLog model, and is planned to use the log_update constructor defined in a manager to log to DB.
def log_user_change(old_user_instance, request):
user = User.objects.get(pk = old_user_instance.id)
# breakpoint 2
print(old_user_instance)
user_fields = user._meta.get_fields()
old_user_fields = old_user_instance._meta.get_fields()
ct = ContentType.objects.get_for_model(user)
for old_user_fields in user_fields:
if not old_user_fields in user_fields:
"""
ChangeLog.objects.log_updae(
user = request,
content_type = ct.pk,
object_id = user.pk,
changes = user_fields,
)
"""
print('changes: ' + old_user_fields)
else:
print('no changes')
Any input is appreciated. Thank you!
I think it's because when you do old_user_instance = user, you make old_user_instance point to user, instead of making a copy of it. Then when you save your form, both gets modified. Perhaps try old_user_instance = User() with the same parameters as user and see if it changes something.

How to pass hidden variable to a form in django so it could be validated?

I have a registration form that goes through all the usual stuff, but with one bot prevention thing. I made a model SecurityQuestion, that consists of two charfields, Question and Answer. During registration one of them is randomly picked and is supposed to pass an answer to the form so it can be validated there. However, for the reason I'm yet to figure out, it doesn't seem to be passing the answer to the form
So let's start with the code
profile/forms.py
# FORM: Register an account
class UserRegistrationFirstForm(forms.ModelForm):
username = forms.CharField(max_length=20)
password = forms.CharField(widget=forms.PasswordInput)
password_confirm = forms.CharField(widget=forms.PasswordInput)
email = forms.EmailField()
email_confirm = forms.EmailField()
answer = forms.CharField(max_length=50, required=True)
hidden_answer = forms.CharField(widget=forms.HiddenInput)
class Meta:
model = User
fields = [
'username',
'email',
'email_confirm',
'password',
'password_confirm',
'answer',
]
def clean_answer(self):
formated_user_answer = self.cleaned_data.get("answer").lower()
formated_hidden_answer = self.cleaned_data.get("hidden_answer").lower()
if formated_hidden_answer != formated_user_answer:
raise forms.ValidationError("Incorect answer to security question!")
return answer
As you can see, there are two fields, answer and hidden_answer. answer is where users types in their answers, and hidden_answer is supposed to be populated when initializing form and passing init.
profiles/views.py
# VIEW: Register an account
def custom_register(request):
if request.user.is_authenticated():
return redirect(reverse('profile', host='profiles'))
# Grab a random registration security Q&A
qa_count = SecurityQuestion.objects.count() - 1
sec_qa = SecurityQuestion.objects.all()[randint(0, qa_count)]
# Form for users to register an account
form = UserRegistrationForm(request.POST or None, initial={"hidden_answer": sec_qa.answer,})
# Validate the registration form
if form.is_valid():
# Create new account
user = form.save(commit=False)
password = form.cleaned_data.get("password")
user.set_password(password)
user.save()
# Set the default avatar
user.profile.avatar = get_default_avatar()
user.profile.save()
login(request, user)
messages.success(request, "Welcome " + user.username + ", you have successfully registered an account!")
return redirect(reverse('pages:frontpage', host='www'))
# Context dict to return for template
context = {
"title": "Registration",
"form": form,
"question": sec_qa.question,
}
return render(request, 'profiles/register.html', context)
Alright, so in registration view, I randomly pick one of the security questions and then pass it to the form with initial={"hidden_answer": sec_qa.answer,}. However it doesn't seem the be going through, as I'm getting following error:
'NoneType' object has no attribute 'lower'
Exception Location: path/to/profiles/forms.py in clean_answer, line 103
formated_hidden_answer = self.cleaned_data.get("hidden_answer").lower()
OK, so NoneType would mean there's nothing to reference to. I've tried couple different ways to fix this. I tried putting hidden_answer in form's meta field list. I also tried {{ form.hidden_answer.as_hidden }} in the template (which is complete opposite of what I'm trying to achieve here, as the answer is still displayed in value of that hidden input in the page source). Any idea what am I doing wrong with this?
EDIT: If there's an alternative or a simple solution to what I'm trying to do, could you please reference any documentation about it?
Sending a hidden input can't prevent a user from knowing the hidden_answer. It won't be visible in browser but will very well be present in your DOM and accessible to any user. Sending the answer(hidden or not) to the client side is itself a flaw in security.
You should only send the question to the client side(browser) and later verify it in you clean() method.
If I understand you use case correctly(correct me if I'm wrong), you should do something like:
In your views.py, do something like:
def custom_register(request):
if request.user.is_authenticated():
return redirect(reverse('profile', host='profiles'))
if request.method == 'GET':
# Grab a random registration security Q&A
qa_count = SecurityQuestion.objects.count() - 1
sec_qa = SecurityQuestion.objects.all()[randint(0, qa_count)]
#Give the text of your question to sec_qa_title. Do something like the following.
#sec_qa_title = sec_qa.title
#sec_qa_title should now have the question string of the SecurityQuestion model object instance.
form = UserRegistrationForm(initial={'question' : sec_qa_title})
#initialize whatever context variables you want.
#Rest of your code.
#return a suitable response which will display you form with the security question.
#return render(request, 'profiles/register.html', context)
if request.method == 'POST':
#All the data of form submitted by the user is inside request.POST
form = UserRegistrationForm(request.POST)
# Validate the registration form
if form.is_valid():
#Do your stuff. Return a suitable response.
else:
#Do your stuff. Return a suitable response.
Now in your forms.py, do something like:
class UserRegistrationFirstForm(forms.ModelForm):
username = forms.CharField(max_length=20)
password = forms.CharField(widget=forms.PasswordInput)
password_confirm = forms.CharField(widget=forms.PasswordInput)
email = forms.EmailField()
email_confirm = forms.EmailField()
question = forms.CharField(max_length=50, required=True)
#removed hidden_answer field and added a question field.
answer = forms.CharField(max_length=50, required=True)
class Meta:
model = User
fields = [
'username',
'email',
'email_confirm',
'password',
'password_confirm',
#Remove the answer field.
]
def clean_answer(self):
security_question_title = self.cleaned_data.get("question")
#get the question title.
formatted_user_answer = self.cleaned_data.get("answer").lower()
#now get the SecurityQuestion model.
try:
sec_qa = SecurityQuestion.objects.get(title = security_question_title)
#Don't forget to import SecurityQuestion model.
except SecurityQuestion.DoesNotExist:
#If a user changes the question, you don't want it to fiddle with you system.
raise forms.ValidationError("Question was changed. Wrong practice.")
#Finally check the answer.
if formatted_user_answer != sec_qa.answer.lower():
raise forms.ValidationError("Incorrect answer to security question!")
return answer
There are many improvements that you can later try.
For example: Sending a question and an id with it to later extract the question through that id(instead of extracting it from the whole string; slightly unreliable)
I hope you understand the flow and construct it correctly.
There may be some errors since I didn't test the code but I hope you'll fix them.
I hope this guides you in some way. Thanks.

Django - revisiting "change password at next login”

I apologize, as this has been asked before (https://stackoverflow.com/a/5570717/3529404). However, I am having trouble with the accepted answer from user Chris Pratt. The code generally works - I am able to force password reset. However, where I am having trouble is trying to ensure that the new password is different than the old password. As the code is currently written, the same password is allowed.
From Chris's answer:
def password_change_signal(sender, instance, **kwargs):
try:
user = User.objects.get(username=instance.username)
if not user.password == instance.password:
profile = user.get_profile()
profile.force_password_change = False
profile.save()
except User.DoesNotExist:
pass
It seems as this should be checked in the line:
if not user.password == instance.password:
However, when I print user.password and instance.password (despite entering in the same password in both fields), the hashed value is not equal. Oddly enough, if I keep changing the password, the value that is printed for instance.password becomes the value for user.password on the next change.
Basically, all I want to do is use the code from the previous linked answer (https://stackoverflow.com/a/5570717/3529404), but enforce that the new password is different than the old password.
Thank you!!
UPDATE!
As discussed in the comments, I think my main area of frustration at the moment is not understanding exactly how user/instance differ. In particular, when print both user and instance passwords (see below), the hashed values are different even though the same password has been entered in each.
My code is slightly different than #Chris Pratt's because I am not using the depreciated user profile commands. Hopefully I didn't leave anything out!
webapp/models.py
class UserAdditional(models.Model):
user = models.ForeignKey(User, unique=True)
force_password_change = models.BooleanField(default=True)
def create_user_additional_signal(sender, instance, created, **kwargs):
if created:
UserAdditional.objects.create(user=instance)
def password_change_signal(sender, instance, **kwargs):
try:
user = User.objects.get(username=instance.username)
# these hashed values differ, even when the instance password entered is the same as the stored user password
print user.password
print instance.password
if not user.password == instance.password:
useradditional_obj = UserAdditional.objects.get(user=user)
useradditional_obj.force_password_change = False
useradditional_obj.save()
except User.DoesNotExist:
pass
signals.pre_save.connect(password_change_signal, sender=User, dispatch_uid='webapp.models')
signals.post_save.connect(create_user_additional_signal, sender=User, dispatch_uid='webapp.models')
webapp/middleware.py
class PasswordChangeMiddleware:
def process_request(self, request):
if request.user.is_authenticated() and not re.match(r'^/password_change/?', request.path) \
and not re.match(r'^/logout/?', request.path):
useradditional_obj = UserAdditional.objects.get(user=request.user)
if useradditional_obj.force_password_change:
return HttpResponseRedirect('/password_change/')
webapp/forms.py --- for password requirement enforcing
class ValidatingPasswordForm(object):
MIN_LENGTH = 8
def clean_new_password1(self):
password1 = self.cleaned_data.get('new_password1')
# At least MIN_LENGTH long
if len(password1) < self.MIN_LENGTH:
raise forms.ValidationError("The new password must be at least %d characters long." % self.MIN_LENGTH)
# check numbers and special characters
nums = len(set(re.findall(r'[0-9]',password1)))
symb = len(set(re.findall(r'[~!##$%^&\*()_+=-`]',password1)))
if nums <= 0 or symb <= 0:
raise forms.ValidationError("The new password must contain at least one number and one special character [~!##$%^&\*()_+=-`]")
return password1
class ValidatingPasswordChangeForm(ValidatingPasswordForm, auth.forms.PasswordChangeForm):
pass
class ValidatingSetPasswordForm(ValidatingPasswordForm, auth.forms.SetPasswordForm):
pass
It is generally a good practice to require an authenticated user provide the current password when changing passwords. This protects against the case, even if unlikely, that a logged in user leaves the workstation with an active session and some "evil" user attempts to hijack their account by changing the password.
By requiring the user to enter both the old and new passwords you can also prevent password re-use both on the client and server side. This allows for increased usability for your users since you can warn them and disable submission of the form using JavaScript. By capturing the old and new passwords, you can pass both to the server and verify against re-use similar to the answer provided by warath-coder.
Update
As you've mentioned Django saved the hashes and not the actual passwords, and as a further protection the passwords are salted, see the Django docs on how passwords are stored. Because of this, you will not be able to simply compare the hashes. You can test that the old and new passwords match in the clean_new_password1 method of your form using the form data prior to the User object being updated. This can be done by simple comparison or by trying to authenticate with the old password as warath-coder has described.
I would do this:
def password_change_signal(sender, instance, **kwargs):
try:
user = authenticate(username=instance.username, password=instance.password)
if user is None: # means auth failed, which means password is not the same as the current password.
user = User.objects.get(username=instance.username)
user.set_password(instance.password)
user.save()
profile = user.get_profile()
profile.force_password_change = False
profile.save()
except User.DoesNotExist:
pass
basically i try to authenticate the user with the password they supply, and it should fail if the new password is different than the current.

Pyramid: OperationalError 1054, "Unknown column 'XX' in 'field list'

When trying to access the database, I am unable to access the column. I have run the init database script multiple times. Here's the code:
View
from .models import Session, Admin
#view_config(route_name='admin', renderer='templates/admin.pt')
def admin(request):
session = Session()
user = authenticated_userid(request)
admin = bool(session.query(Admin).filter_by(username = user).first())
message = ''
if admin:
if 'form.submitted' in request.params:
session.add(Admin(request.params['admin']))
session.commit()
message = "%s has been added as an Admin!" % request.params['admin']
return dict(url=request.route_url('admin'),
message = message,
)
return dict(message = message)
else:
return HTTPForbidden()
Models
class Admin(Base):
__tablename__='admins'
__table_args__={
'mysql_engine':'InnoDB',
'mysql_charset':'utf8',
}
username = Column(String(255), ForeignKey('users.username'), primary_key=True)
def __init(self, user):
self.username = user
The database script can't add column on an already existing table. If you modified or added the column after running the script, that's where your problem come from.
You have three possibilities :
Delete the database and let the script recreate it the right way
Run the sql to add the column by hand.
Take a look at Alembic, which can help you do that automatically.

Categories

Resources