int() argument must be a string or a number, not 'SimpleLazyObject' - django

I got, following Error messages,
TypeError at /save/ int() argument must be a string or a number, not
'SimpleLazyObject'
While executing following form.
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 bookmark
bookmark, created = Bookmark.objects.get_or_create(
user=request.user,
link=link
)
# update bookmarks title
bookmarks.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)
I thought I got error because I passed link at
bookmark, created = Bookmark.objects.get_or_create(
user=request.user,
link=link
)
But I can not figure out how to fix it.
How can I fix it?

The likely cause is that you're setting user = request.user, where request.user is not a real User object, but a SimpleLazyObject instance. See django: Purpose of django.utils.functional.SimpleLazyObject? for more details, but using request.user.id should fix your issue.

You have to login when running this piece of code on localhost. Otherwise the request.user will be a SimpleLazyObject, then the errors comes out.

Here you trying to create a Bookmark object based on request.user , but request.user is a SimpleLazyObject , so we can get a more secure user object by :
from django.contrib import auth
current_user = auth.get_user(request)
and further your query should be
bookmark, created = Bookmark.objects.get_or_create(
user=current_user,
link=link
)

Most likely the user who is loading the page is not authenticated. Therefor the error is thrown. If you want to save a request.user reference to the database, you obviously have to ensure that only authenticated users are able to call the function.
In your case there are two possibilities - add the "#login_required" decorator to the function or check if the user is authenticated inside the code. Here are the snippets:
With Decorator:
from django.contrib.auth.decorators import login_required
#login_required
def bookmark_save_page(request):
if request.method == 'POST':
form = BookmarkSaveForm(request.POST)
if form.is_valid():
# Do something
OR - checking inside the code if the user is authenticated:
def bookmark_save_page(request):
if request.method == 'POST' and request.user.is_authenticated():
form = BookmarkSaveForm(request.POST)
if form.is_valid():
# Do something

Thats strange, I had the same problem and the same solution. After trying a bunch of alternatives I went back to user = request.user and it worked

if you want to set an addition request attr, based on user -> you need to use lazy Django function which is correctly handled by Field.get_prep_value
from django.utils.functional import lazy
request.impersonator = lazy(lambda: request.user.id, int)()

Related

Create Form and update using same view in django

I am trying to use same view for creating form and updating any object.
My code is as below, I tried in many ways nothing is working, since I am excluding the shof from form and adding it after form.is_valid() it makes lot of confusion. If I update it creates new object. I have two urls one without ql (create new) and one with ql (update existing), I have a class vdview which provides v.shof which needs to applied in the f.shop in form. please help fix this,
#csrf_protect
#login_required
def addmenu(request, qs, ql=None):
v = vdview(request, qs)
ctgobj = get_object_or_404(v.shopcategs, pk=ql) if ql else None # ctgobj = ShopCtg(shop=v.shof)
if ql:
form = ShopCtgForm(instance=ctgobj) # Tried ShopCtgForm(instance=ctgobj, data=request.POST)
else:
form = ShopCtgForm(data= request.POST)
if request.method == 'POST':
if form.is_valid():
f=form.save(commit=False)
f.shop = v.shof
f.save()
#form.save_m2m()
return redirect('vendor-shop', qs) #thing='%s added' %f.name)
else:
pass
#else:
# form = ShopCtgForm()
return render(request,'vendorshop.html', {'shop':v.shof, 'shopcategs':v.shopcategs, 'form': form,
'heading':'Create New Category', 'createcateg': 'createcateg', 'pkaddmenupk':'y' } )
Use try blocks to handle both scenarios. The simplified example below will look for a given model instance pk and if it doesn't find it, will assume you want to create it. try will prevent django from throwing an error if the model instance doesn't exist. Rather, it will just return the empty model form.
It does this first to render the correct form in the template (the first try block) then again in the second try block after request.method == 'POST': to submit new data or update existing data.
Views.py
from .models import Books
from .forms import BookForm
def create_and_update_book_view(request, pk):
books = Books.objects.get(id=pk)
try: # get pre-populated form with model instance data (for update)
form = BookForm(instance=books.id)
except: # If it doesn't exist, show an empty form (for create)
form = BookForm(request.POST or None)
if request.method == 'POST':
try: # Do the same as above
form = BookForm(instance=books.id)
except: # Same as above
form = BookForm(request.POST or None)
if form.is_valid():
form.save()
return render(request, "create_and_update_book_page.html", {'form':form})

Django CustomTags Using Session

I'm trying to use Django to output an HTML page based on whether the session is set or not.
when I submit my Django Form (via my view) I set the session like this:
def index(request):
users = Users.objects.all()
totalUsers = len(users)
form = CreateUserForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
form = CreateUserForm()
context = {
"form": form,
'users': users,
"totalUsers": totalUsers,
}
request.session.set_expiry(300)
request.session['loggedIn'] = True
return render(request, 'SmartCity/index.html', context)
I know this is successful because I can see the value set in the DB.
In my CustomTags.py file, I want to more or less check the session variable "loggedIn" is set, and if it is, return one thing, otherwise, return something else. This is how I thought to achieve it, but it's not working:
from Django import template
register = template.Library()
#register.inclusion_tag('SmartCity/index.html', takes_context=True)
def hello_world(context):
request = context['request']
loggedInStatus = request.session.get('logged_in', 'False')
if loggedInStatus == True:
return "Hello world"
The error I receive is:
https://preview.ibb.co/dqAe8k/2017_09_19_18_06_57.png
I could totally be on the wrong track... I would appreciate any advice you might be able to give a Django beginner :)

Can't figure out why form.has_changed() is always true?

I'm trying to learn Django and have come up with a situation I can't figure out. I have the following code:
def contact_add(request):
if request.method == 'POST':
form = ContactManageForm(request.POST)
if form.is_valid():
if form.has_changed(): # <-- ALWAYS RETURNS TRUE!
form.clean()
...
elif 'id' in request.GET: # Request to show an existing contact
new_contact_dynamic = contacts.models.PersonDynamic.objects.get(person_static = request.GET['id'],
current_record_fg = True)
form = ContactManageForm(new_contact_dynamic.__dict__, initial=new_contact_dynamic.__dict__)
else: # This must be to add a new contact
form = ContactAddForm()
return render(request, 'contact_manage.html', {'form': form})
So, if I'm sent an ID number, I read a record and display it on the screen. My template gives the user a 'submit changes' button. My problem, as noted above, is that Django always shows that the form has changed, even if the user hasn't changed any data on the screen (i.e. he just hit the submit changes button without changing anything).
So, am I doing something obviously wrong in my code that's creating this situation? Am I misinterpreting how the form.has_changed() method works?
It's my assumption that when I use the initial=parameter after a GET request, Django is storing that data somewhere and knows the context when the user then hits the 'submit data' button, is this wrong?
Yes you need to initialize your Form with initial data.
In your view the GET and POST requests have no common context. You may want to use sessions for that.
But in this case, it is not necessary. You can retrieve the instance on each request:
def contact_add(request):
if 'id' in request.GET:
new_contact_dynamic = contacts.models.PersonDynamic.objects.get(
person_static = request.GET['id'],
current_record_fg = True
)
if request.method == 'POST':
form = ContactManageForm(request.POST, initial=new_contact_dynamic.__dict__)
...
else: # Show an existing contact
form = ContactManageForm(initial=new_contact_dynamic.__dict__)
else:
form = ContactAddForm()
return render(request, 'contact_manage.html', {'form': form})

Using twython-django to authenticate a user when they submit a form

I'm building a Django app and am trying to use twython-django to authenticate a Twitter user when they submit a form. I have tried to edit my views.py, urls.py and models.py files as suggested by this example https://github.com/ryanmcgrath/twython-django/tree/master/twython_django_oauth but I'm simply guessing at it so I'm sure that's why it isn't working.
Could you please help me out with how to get this working? I'm completely new to Twitter wrappers so any help would very much be appreciated.
The flow I'm trying to achieve:
User submits a message through the form
User is asked to authenticate their Twitter account on hitting "Submit" button
User's message, Twitter name, Twitter screen_name, profile_image_url and followers_count are saved in the database (I'm using Heroku Postgres)
User's profile image, name, screen_name and message are printed to index.html in a (Twitter-like) feed.
My views.py:
def logout(request, redirect_url=settings.LOGOUT_REDIRECT_URL):
django_logout(request)
return HttpResponseRedirect(request.build_absolute_uri(redirect_url))
def submit(request):
twitter = Twython(
twitter_token=settings.TWITTER_KEY,
twitter_secret=settings.TWITTER_SECRET,
callback_url=request.build_absolute_uri(reverse('alpha.views.submit'))
)
auth_props = twitter.get_authentication_tokens()
request.session['request_token'] = auth_props
return HttpResponseRedirect(auth_props['auth_url'])
form = MessageForm(request.session.get('message'))
if form.is_valid():
new_message = form.save()
return HttpResponseRedirect('/')
else:
request.session['message'] = request.POST
twitter = Twython(
twitter_token = settings.TWITTER_KEY,
twitter_secret = settings.TWITTER_SECRET,
oauth_token = request.session['request_token']['oauth_token'],
oauth_token_secret = request.session['request_token']['oauth_token_secret'],
)
authorized_tokens = twitter.get_authentication_tokens()
try:
user = User.objects.get(username = authorized_tokens['screen_name'])
except User.DoesNotExist:
user = User.objects.create_user(authorized_tokens['screen_name'], authorized_tokens['oauth_token_secret'])
profile = Message()
profile.user = user
profile.name = name
profile.profile_image_url = profile_image_url
profile.oauth_token = authorized_tokens['oauth_token']
profile.oauth_secret = authorized_tokens['oauth_token_secret']
profile.save()
user = authenticate(
username = authorized_tokens['screen_name'],
password = authorized_tokens['oauth_token_secret']
)
login(request, user)
return HttpResponseRedirect(redirect_url)
Disclaimer: I'm a newbie so the above code is probably a complete mess!
Yes, your use-case is different from that intended by twython-django, but that doesn't mean it's not going to work in your case, and you can use the library as it stands with your flow. After setting up everything as described on the main page, you'll need something like this for your views.py:
from django.shortcuts import redirect, reverse
def submit(request):
# initial form submission, first check if we're authenticated
# if we are, process as normal, otherwise redirect to the login
# page. If you've set up twython-django correctly, it'll redirect
# to twitter for the actual login.
if request.method == "POST":
if request.user.is_authenticated():
form = MessageForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
# Modify this to display validation errors
pass
else:
request.session['message'] = request.POST
# the reverse(submit) resolves this view for redirection
# back from the twitter authentication
return redirect(settings.LOGIN_URL, next=reverse(submit))
# A Get request, where we should first check for the message stored
# We then process the form and remove it from session to prevent
# accidental re-use.
else:
if 'message' in request.session and request.user.is_authenticated():
form = MessageForm(request.session['message'])
del request.session['message']
if form.is_valid():
form.save()
return redirect('/')
else:
# Modify this to display validation errors
pass
else:
# handle the case where this is a get request and the variable
# isn't in session
pass
As for loading their profile image and follower count, those are not currently handled at all by twython django. You can either fork it on github and add them to the TwitterProfile model and add the appropriate code to the thanks view to load those too, or you can add a new model to your code which extends TwitterProfile.
from twython_django_oauth.models import TwitterProfile
from django import models
class ExtendedTwitterProfile(models.Model)
profile = models.OneToOne(TwitterProfile, related_name="extended")
avatar = models.CharField(max_length=255)
followers = models.IntegerField()
And add the code into the submit view to add/update the profile as needed.
extended_profile = ExtendedTwitterProfile.objects.get_or_create(profile=request.user.twitterprofile)
extended_profile.avatar = avatarurl
extended_profile.followers = followercount
extended_profile.save()
You should be able to access those details via
user.twitterprofile.extended.avatar
Although, I have in the past used a url to get the avatar, for example:
# avatar redirection urls
url(r'^avatar/(?P<userid>[0-9A-Za-z_]+)$', redirect_to,
{ 'url' : 'http://api.twitter.com/1/users/profile_image/%(userid)s.json' }, name='avatar' ),
url(r'^avatar/(?P<userid>[0-9A-Za-z_]+)/(?P<size>[a-z]+)$', redirect_to,
{ 'url' : 'http://api.twitter.com/1/users/profile_image?screen_name=%(userid)s&size=%(size)s' } ),
In a template where you want to display the avatar, you simply use and img tag using the url template tag to do the reverse url resolution, like so:
<img src="{% url avatar userid=request.user.username %}" />
As a further pointer, you can also request all of the users' basic details via Twitter's json API
https://twitter.com/users/zandeez.json
For example, will get you my public profile in a form you can use either in python using urllib or even javascript/ajax depending on what you want to do with the follower count.
Hopefully that should get you sorted, if you need any more help fell free to ask.

RequestContext on django redirect()

I'm wondering if there is any way to include the RequestContext in the django redirect function or any other context.
The thing is that I need to add a message after an object is created, but the messages framewotk requires a RequestContext to work or another context that returns the messages. How can I do to return a context?
My view:
#permission_required('spaces.add_space')
def create_space(request):
"""
Returns a SpaceForm form to fill with data to create a new space. There
is an attached EntityFormset to save the entities related to the space. Only
site administrators are allowed to create spaces.
:attributes: - space_form: empty SpaceForm instance
- entity_forms: empty EntityFormSet
:rtype: Space object, multiple entity objects.
:context: form, entityformset
"""
space_form = SpaceForm(request.POST or None, request.FILES or None)
entity_forms = EntityFormSet(request.POST or None, request.FILES or None,
queryset=Entity.objects.none())
if request.user.is_staff:
if request.method == 'POST':
space_form_uncommited = space_form.save(commit=False)
space_form_uncommited.author = request.user
if space_form.is_valid() and entity_forms.is_valid():
new_space = space_form_uncommited.save()
space = get_object_or_404(Space, name=space_form_uncommited.name)
ef_uncommited = entity_forms.save(commit=False)
for ef in ef_uncommited:
ef.space = space
ef.save()
# We add the created spaces to the user allowed spaces
request.user.profile.spaces.add(space)
# This message does not work since there's no context.
messages.info(request, 'Space %s created successfully.' % space.name)
return redirect('/spaces/' + space.url)
return render_to_response('spaces/space_add.html',
{'form': space_form,
'entityformset': entity_forms},
context_instance=RequestContext(request))
else:
return render_to_response('not_allowed.html',
context_instance=RequestContext(request))
Storing a message doesn't require a RequestContext, it's only displaying it that does. In what way doesn't your code work? Your message should be added to the database and available to be displayed after the redirect.
I had the same question and just want to clarify that it 'just works'
def first_view(request)
...
messages.info(request, "Some info message")
...
return redirect('second_view')
def second_view(request)
...
return render_to_response('template.html',{},context_instance=RequestContext(request))
-> will correctly display the message