'str' object does not support item assignment - with Django - django

I have created a custom template tag in django
disqusTag.py:
register = template.Library()
#register.inclusion_tag('polls/questionDetail.html', takes_context=True)
def disqus_sso(context):
DISQUS_SECRET_KEY = getattr(settings, 'DISQUS_SECRET_KEY', None)
if DISQUS_SECRET_KEY is None:
return "<p>You need to set DISQUS_SECRET_KEY before you can use SSO</p>"
DISQUS_PUBLIC_KEY = getattr(settings, 'DISQUS_PUBLIC_KEY', None)
if DISQUS_PUBLIC_KEY is None:
return "<p>You need to set DISQUS_PUBLIC_KEY before you can use SSO</p>"
user = context['user']
if user.is_anonymous():
return ""
data = json.dumps({
'id': user.id,
'username': user.username,
'email': user.email,
})
# encode the data to base64
message = base64.b64encode(data.encode('utf-8'))
# generate a timestamp for signing the message
timestamp = int(time.time())
key = DISQUS_SECRET_KEY.encode('utf-8')
msg = ('%s %s' % (message, timestamp)).encode('utf-8')
digestmod = hashlib.sha1
# generate our hmac signature
sig = hmac.HMAC(key, msg, digestmod).hexdigest()
return dict(
message=message,
timestamp=timestamp,
sig=sig,
pub_key=DISQUS_PUBLIC_KEY,
)
t = get_template('polls/questionDetail.html')
register.inclusion_tag(t)(disqus_sso)
and i am loading the same in my template questionDetail.html as
{% load disqusTag %}
{% disqus_sso %}
but i am getting this error : 'str' object does not support item assignment
can anybody help me why? I know similar questions have been asked on stack overflow, but i went through all of them and none of them helped.

You should have provided the full traceback.
However, I think the issue is with your if user.is_anonymous check - if it's true, you return an empty string. But the return value of an inclusion tag must always be a context dict. You should return an empty dict there instead.

Related

why do i keep getting this error( Field 'id' expected a number but got ''. ) each time users like a post from their own timeline?

Here is the users account view that i want users to be able to like their post from, it has really been a challenge to get my head around this. would appreciate some help.
def account_view(request, *args, **kwargs):
"""
- Logic here is kind of tricky
is_self
is_friend
-1: NO_REQUEST_SENT
0: THEM_SENT_TO_YOU
1: YOU_SENT_TO_THEM
"""
context = {}
user_id = kwargs.get("user_id")
try:
account = User.objects.get(pk=user_id)
except:
return HttpResponse("Something went wrong.")
if account:
context['id'] = account.id
context['username'] = account.username
context['bio'] = account.bio
context['get_full_name'] = account.get_full_name
context['email'] = account.email
context['profile_pic'] = account.profile_pic.url
context['cover_image'] = account.cover_image.url
context['city'] = account.city
context['country'] = account.country
context['gender'] = account.gender
context['hide_email'] = account.hide_email
post_id = request.GET.get("likeId", "")
try:
post_id = Post.objects.get(pk=post_id)
except Post.DoesNotExist:
liked= False
like = Like.objects.filter(user=user, post=post)
try:
post_list = Post.objects.filter(user_name=account)
except Post.DoesNotExist:
post_list = Post(user_name=account)
save.post_list()
posts = post_list.all()
context['posts'] = posts
Here is my like view, how best do can i accomplish users and friends being able to like their post from their own profile or timeline ?
def like(request):
post_id = request.GET.get("likeId", "")
user = request.user
post = Post.objects.get(pk=post_id)
liked= False
like = Like.objects.filter(user=user, post=post)
if like:
like.delete()
else:
liked = True
Like.objects.create(user=user, post=post)
resp = {
'liked':liked
}
response = json.dumps(resp)
return HttpResponse(response, content_type = "application/json")
The error occurs either because likeId is sent as an empty string http://url_path?likeId=, or (most probably) because it is not present in the request.GET at all.
You've used request.GET.get("likeId", "") so the default value is "".
Next, the code runs post_id = Post.objects.get(pk=post_id). It's forbidden to pass a string to a pk filter, hence the error. So, verify the likeId is indeed send in the request and it's an actual Post.id (check for possible typos). Maybe you've meant to use request.POST instead?
Generally, it's a bad idea to use dict.get() where you clearly DO need an value. Also, there should be some validation in place to ensure the likeId is a valid integer. I would recommend validating input parameters with a form, but a manual approach is also possible.
try:
post_id = int(request.GET["likeId")
except (KeyError, ValueError):
return HttpResponse('Provide a valid likeId integer', status_code=404)

Django view gives error of Forbidden (CSRF token missing or incorrect.) while using the userdefined decorator for checking the session

I created the userdefined decorator to check the session is active or not. Following is the function defination
def session_required(func):
"""
Decorator to check the session is active or not for logged in user
:param func: Name of function for which you have to check the session is active or not
:return:
"""
def wrap(request, *args, **kwargs):
"""
Wrapper function for the decorator
:param request: request parameter for called URL
:return:
"""
if not request.session.get("admin_id"):
return redirect("/")
func_return = func(request, *args, **kwargs)
return func_return
return wrap
I am using this decorator on the respective function based view. At some places it works absolutely fine but when I do some POST or PUT operation then it gives me error
Forbidden (CSRF token missing or incorrect.):
My function based view is like
#csrf_exempt
#session_required
def mover_profile_handler(request):
"""
mover profile handler function for viewing and editing the details
:param request:
:return:
"""
try:
if request.method == "GET":
login_id = request.session.get("admin_id")
login_info_obj = Login.objects.get(id=login_id)
mover_info_obj = Mover.objects.get(fk_login=login_info_obj)
country_obj = Country.objects.all()
currency_obj = CurrencyType.objects.all()
subscription_detail = SubscriptionMoverDetail.objects.filter(fk_mover=mover_info_obj).order_by("-id")
# Extracting data for showing the subscription package details
current_subscription_detail = {}
subscription_detail_history = []
for index, item in enumerate(subscription_detail):
subscription_master_detail = SubscriptionMaster.objects.get(id=item.fk_subscription_master_id)
subscription_detail_json = {
"plan_name": subscription_master_detail.subscription_plan_name,
"subscription_start_date": item.subscription_date,
"subscription_end_date": item.subscription_end_date,
"amount_paid": item.amount_paid,
"users": subscription_master_detail.customer_permitted_count
}
if index == 0:
current_subscription_detail = subscription_detail_json
else:
subscription_detail_history.append(subscription_detail_json)
return render(request, "mover_profile.html", {
"mover_info_obj": mover_info_obj,
"country_obj": country_obj,
"currency_obj": currency_obj,
"login_info_obj": login_info_obj,
"current_subscription_detail": current_subscription_detail,
"subscription_detail_history": subscription_detail_history
})
elif request.method == "PUT":
request = convert_method_put_to_post(request)
mover_id = request.POST.get("id")
if Mover.objects.filter(id=mover_id).exists():
mover_info_obj = Mover.objects.get(id=mover_id)
mover_info_obj.mover_name = request.POST.get("name")
mover_info_obj.address = request.POST.get("address")
mover_info_obj.phone_no = request.POST.get("phone")
mover_info_obj.mover_size = request.POST.get("size")
mover_info_obj.reg_no = request.POST.get("reg_no")
mover_info_obj.website = request.POST.get("website")
mover_info_obj.fk_country_id = request.POST.get("country")
mover_info_obj.fk_currency_id = request.POST.get("currency")
operational_countries = request.POST.getlist("operational_countries[]")
mover_info_obj.countries_in_operation.set(operational_countries)
mover_info_obj.save()
return HttpResponse("success")
except Exception as e:
error_save(str(traceback.format_exc()))
return redirect('error_handler_500')
I tried with
#csrf_protect #csrf_exempt
in view and also tried {% csrf_token %} in html file
without using #session_required code is working absolutely fine.
So please tell me what is wrong with this stuff!!

Django Context TypeError: context must be a dict rather than Context [duplicate]

Just received the Sentry error TypeError context must be a dict rather than Context. on one of my forms. I know it has something to do with Django 1.11, but I am not sure what to change to fix it.
Offending line
message = get_template('email_forms/direct_donation_form_email.html').render(Context(ctx))
Entire View
def donation_application(request):
if request.method == 'POST':
form = DirectDonationForm(data=request.POST)
if form.is_valid():
stripe.api_key = settings.STRIPE_SECRET_KEY
name = request.POST.get('name', '')
address = request.POST.get('address', '')
city = request.POST.get('city', '')
state = request.POST.get('state', '')
zip = request.POST.get('zip', '')
phone_number = request.POST.get('phone_number', '')
support = request.POST.get('support', '')
agree = request.POST.get('agree', '')
email_address = request.POST.get('email_address', '')
number = request.POST.get('number', '')
cvc = request.POST.get('cvc', '')
exp = request.POST.get('exp', '')
# token = form.cleaned_data['stripe_token'],
# exp_m = int(request.POST.get('exp_month', ''))
# exp_y = int(request.POST.get('exp_year', ''))
exp_month = exp[0:2]
exp_year = exp[5:9]
subject = 'New Donation'
from_email = settings.DEFAULT_FROM_EMAIL
recipient_list = ['deniselarkins#/////\\\\\.com',
'charles#/////\\\\\.net',
'marcmunic#/////\\\\\.com',
]
token = stripe.Token.create(
card={
'number': number,
'exp_month': exp_month,
'exp_year': exp_year,
'cvc': cvc
},
)
customer = stripe.Customer.create(
email=email_address,
source=token,
)
total_support = decimal.Decimal(support) / 100
total_charge = decimal.Decimal(int(support)) / 100
# Charge the user's card:
charge = stripe.Charge.create(
amount=total_charge,
currency='usd',
description='Donation',
customer=customer.id
)
ctx = {
'name': name,
'address': address,
'city': city,
'state': state,
'zip': zip,
'phone_number': phone_number,
'email_address': email_address,
'agree': agree,
'charge': charge,
'customer': customer,
'total_support': total_support,
'total_charge': total_charge
}
message = get_template('email_forms/direct_donation_form_email.html').render(Context(ctx))
msg = EmailMessage(subject, message, from_email=from_email, to=recipient_list)
msg.content_subtype = 'html'
msg.send(fail_silently=True)
return redirect(
'/contribute/donation-support-thank-you/?name=' + name +
'&address=' + address +
'&state=' + state +
'&city=' + city +
'&zip=' + zip +
'&phone_number=' + phone_number +
'&email_address=' + email_address +
'&total_support=' + str(total_support) +
'&total_charge=' + str(total_charge)
)
context = {
'title': 'Donation Pledge',
}
return render(request, 'contribute/_donation-application.html', context)
In Django 1.8+, the template's render method takes a dictionary for the context parameter. Support for passing a Context instance is deprecated, and gives an error in Django 1.10+.
In your case, just use a regular dict instead of a Context instance:
message = get_template('email_forms/direct_donation_form_email.html').render(ctx)
You may prefer to use the render_to_string shortcut:
from django.template.loader import render_to_string
message = render_to_string('email_forms/direct_donation_form_email.html', ctx)
If you were using RequestContext instead of Context, then you would pass the request to these methods as well so that the context processors run.
message = get_template('email_forms/direct_donation_form_email.html').render(ctx, request=request)
message = render_to_string('email_forms/direct_donation_form_email.html', ctx, request=request)
Migrated from Django 1.8 to Django 1.11.6
Wherever i had a RequestContext class, there is a method flatten() wich return the result as a dict.
So if the class is RequestContext....
return t.render(context)
becomes
return t.render(context.flatten())
And in a case wich the context is is wrapped by Context(), just remove it. Because Context() is deprecated.
return t.render(Context(ctx))
becomes
return t.render(ctx)
For django 1.11 and after, context must be dict.
You can use:
context_dict = get_context_dict(context)
return t.render(context_dict)
or
context_dict = context.flatten()
return t.render(context_dict)
I got here because I had the same issue. I'm learning Django with Django Unleashed by Andrew Pinkham. It's a book from 2015.
I found in the official documentation, that a dictionary must be passed to the context parameter and not a Context instance (from django.template.Context).
#Alasdair suggested to use render_to_string, but, at least in Django 3.2 the
render method use render_to_string method intrinsically.
def render(request, template_name, context=None, content_type=None, status=None, using=None):
"""
Return a HttpResponse whose content is filled with the result of calling
django.template.loader.render_to_string() with the passed arguments.
"""
content = loader.render_to_string(template_name, context, request, using=using)
return HttpResponse(content, content_type, status)
so, using just the render method could be better. I provide this answer because was the one that I was looking for and it may help some one reaching this Stack Overflow question.

Django form Inserts record instead of update

when i use the id parameter from the url to edit a record, and the "elif 'id' in request.GET:" is being used, it doesn't update the record but it creates a new row
#login_required
def login_save_page(request):
if request.method == 'POST':
form = LoginSaveForm(request.POST)
if form.is_valid():
# Create or get login.
login1 = _login_save(request, form)
return HttpResponseRedirect(
'/user/%s/' % request.user.username
)
elif 'id' in request.GET:
id2 = request.GET['id']
name=''
url=''
Login_username =''
notes= ''
password=''
try:
login1 = login.objects.get(
id = id2,
username=request.user
)
name = login1.name
url = login1.loginUrl
Login_username = login1.login_username
notes = login1.notes
password = login1.password
except (login.DoesNotExist):
pass
form = LoginSaveForm({
'id': id2,
'name': name,
'url': url,
'Login_username': Login_username,
'notes': notes,
'password': password
})
else:
form = LoginSaveForm()
variables = RequestContext(request, {
'form': form
})
return render_to_response('login_save_page.html', variables)
I'm assuming your form is submitting via POST thus the elif "get" will never fire when you need it to (when the user submits the form).
You'll need to put that logic inside of the if request.method == 'POST' block.
Think about when that "elif" block will trigger. Only when the request method is not POST, which means only on page load.

Pass a URL argument with render_to_response

i use this code to views.py file
#login_required
def login_edit_page(request):
#bla bla
return render_to_response('login_edit_page.html', variables)
the problem is that when the return is called, i have to also pass an argument from the URL. for example the URL is /edit?id=1 in this example i want to pass the id. how can i do that?
this is the full code
#login_required
def login_edit_page(request):
if request.method == 'POST':
form = LoginEditForm(request.POST)
if form.is_valid():
login1 = _login_edit(request, form, request.GET['id'])
return HttpResponseRedirect(
'/user/%s/' % request.user.username
)
id2 = request.GET['id']
name=''
url=''
Login_username =''
notes= ''
password=''
try:
login1 = login.objects.get(
id = id2,
username=request.user
)
name = login1.name
url = login1.loginUrl
Login_username = login1.login_username
notes = login1.notes
password = login1.password
except (login.DoesNotExist):
pass
form = LoginEditForm({
'name': name,
'url': url,
'Login_username': Login_username,
'notes': notes,
'password': password
})
variables = RequestContext(request, {
'form': form
})
login1 = _login_edit(request, form, id2)
return render_to_response('login_edit_page.html', variables)
You can not use render_to_response the way you do. Try this:
...
id2 = request.GET.get('id', None)
...
ctx = {
'form': form,
'id': id2
}
return render_to_response('login_edit_page.html', ctx,
context_instance=RequestContext(request))
Note, that I added id2 to context dict, so it is reachable in the template by {{ id }}. Also, checkout how I retrieve url parameter from request.
Another approach is to reach url parameter directly from request (as Mark Lavin noticed, it will work in case of django.core.context_processors.request is present in TEMPLATE_CONTEXT_PROCESSORS):
{{ request.GET.id }}
But sincerely, you should make your view more clean, there are a lot of problems in it.