When I try to submit, I get a TypeError:
int() argument must be a string or a number, not 'SimpleLazyObject'
My views.py:
def bookmark_save_page(request):
if request.method == 'POST':
form = BookmarkSaveForm(request.POST)
if form.is_valid():
# Create or get link.
link, dummy = Link.objects.get_or_create(
url=form.cleaned_data['url']
)
# Create or get bookmarks.
bookmark, created = Bookmark.objects.get_or_create(
user = request.user,
link = link
)
# Update bookmark title.
bookmark.title = form.cleaned_data['title']
# If the bookmark is being updated, clear old tag list.
if not created:
bookmark.tag_set.clear()
# Create new tag list.
tag_names = form.cleaned_data['tags'].split()
for tag_name in tag_names:
tag, dummy = Tag.objects.get_or_create(name=tag_name)
bookmark.tag_set.add(tag)
# Save bookmark to database.
bookmark.save()
return HttpResponseRedirect(
'/user/%s/' %request.user.username
)
else:
form = BookmarkSaveForm()
variables = RequestContext(request, {'form': form})
return render_to_response('bookmark_save.html',variables)
Please guide me.
Thank you.
request.user, by default is of type SimpleLazyObject. To resolve it,
bookmark, created = Bookmark.objects.get_or_create(
user = request.user,
link = link
)
should be
bookmark, created = Bookmark.objects.get_or_create(
user = request.user.id,
link = link
)
If this does not resolve it, make sure you are logged in.
There is another option, may be this solution will help you fix it.
from django.contrib import auth
bookmark, created = Bookmark.objects.get_or_create(
user = auth.get_user(request),
link = link
)
Related
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.
I am trying to build a system where users can upload their CSV file in the Django backend. But I am facing a problem, I want to display CSV file only to the user who uploaded it. I make a field in the models which can trace the user who uploaded the file. Here are some of my codes.
CSV file model:
class Data(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,default=1)
filename = models.CharField(max_length=40,blank=False)
csv_file = models.FileField(upload_to='documents/',blank=False)
def __str__(self):
return self.filename
And the view logic:
def accounts(request):
user = Data.objects.values('user')
if user == request.user:
main_data = Data.objects.all()
else:
main_data = ""
dict = {'main_data':main_data, 'name' : request.user.username}
return render(request, 'accounts.html',dict)
I am only getting that else value ' main_data=" " '.
Simply we can do:
try:
data = request.user.data
except Data.DoesNotExist:
data = ''
Also, check the example of one-to-one relationships in Django's Official Documentation.
You are trying to compare the request.user to a queryset and then also returning all Data objects. You need to use the request.user and then return only the objects linked to that user.
def accounts(request):
main_data = Data.objects.filter(user=request.user)
dict = {'main_data': main_data}
return render(request, 'accounts.html', dict)
Also you wont need the 'name' : request.user.username in your dict. You can access the username through the main_data
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.
I've set up the Photologue, but I don't know how to let a (normal) user upload an image without using the admin interface.
I'd like to let users upload base64-encoded images from a HTML5 canvas, but in the first step it would be fine to upload files from the user's filesystem.
I guess I could modify this general example on how to upload files to use photologue's photo model. I assume this would mean somehow filling "ImageModel"'s ImageField attribute "image'.
I've actually used the linked file upload guide and adapted it to photologue. From my canvas element I extracted a base64 data url and set a form's field to its value then I could interpret it on Django's server side in a view:
def upload_base64(request):
# Handle file upload
if request.method == 'POST':
form = PhotoCodeForm(request.POST, request.FILES)
if form.is_valid():
uploaded_photo_data = base64.b64decode(request.POST['photocode'])
uploaded_photo_file = ContentFile(uploaded_photo_data)
title_str = "Untitled"
slug = slugify( title_str + str( datetime.now() ) )
uploaded_photo = Photo.objects.create( image = default_storage.save(slug, uploaded_photo_file),
title = slug,
title_slug = slug )
name_upload_gallery = "user-upload-queue"
try:
upload_gallery = Gallery.objects.get(title_slug__exact=name_upload_gallery)
except ObjectDoesNotExist:
return HttpResponseBadRequest('<html><body><p>The gallery "'+name_upload_gallery+'" does not exist.</p></body></html>')
upload_gallery.photos.add(uploaded_photo)
# Redirect to the photo gallery after POST
return HttpResponseRedirect('/canvas/')
else:
return HttpResponseBadRequest('<html><body><p>The entered data was not correct.</p></body></html>')
else:
form = PhotoCodeForm() # A empty, unbound form
return render_to_response(
'photologue_upload/upload_base64.html',
{'form': form},
context_instance=RequestContext(request)
)
upload_base64.html is a very simple form that has a field photocode where the base64 string is pasted to.
I'm brand new to django and fairly new to programming in general. I've done the django tutorial and searched the web for an answer to this question, but to no avail, so now I'm here. I am confused how post works with django. All of the tutorials I've looked at how have a return function in views that displays the webpage. I get that. But then how does a user update data if the page is being rendered from that return statement? After the return there can't be any more updates because the function stops, right? What am I missing here? Any help would be greatly appreciated, I'm getting fairly desperate here.
One pattern for Django views (by no means the only pattern) is to check the request method (GET or POST) at the beginning of the view. If it is POST, then handle the incoming data (before the view returns), and then return either a rendered template, or a redirect.
def view_function(request):
if request.method == 'POST':
if data_is_valid(request.POST):
save_data(request.POST)
return HttpResponseRedirect('/somewhere/good')
else:
return render('template', {'errors': what_went_wrong}
else:
return render('template')
The user updates data in the logic of the view function. That is to say, if the user wishes to update something, you place the update logic in the view function before the return. For example, you would do this:
def update(request):
item = <some model>.objects.get(<something>)
<more code>
return <something>
Usually an edit view function contains two parts -- one for updating data, and the other for displaying the update form. For example,
def user_edit(request):
if request.method == 'POST': # is this a save action?
# save the user data
user_id = request.POST.get('user_id')
username = request.POST.get('username')
description = request.POST.get('description')
user = User.objects.get(id=user_id)
user.username = username
user.description = description
user.save()
return HttpResponseRedirect('/user/') # redirect to index
else:
# show the edit form
user_id = request.GET.get('user_id')
user = User.object.get(id=user_id)
return render_to_response('/user/edit.html', { 'user': user })
There are many different choices for the if request.method == 'POST' line. You can also use if request.POST.get('user_id') to check if specified field is set, to determine if this is a save action.