Ensuring all Django views return 200? - python

I have lots of simple Django views that look like this:
#team_leader_required
def view_all_teams(request):
teams = Team.objects.all()
template_vars = {'toolbar': 'teams',
'teams': teams}
return render(request, "all_teams.html", template_vars)
I end up writing lots of unit tests of the form:
def test_view_all_teams_renders(self):
user = self.create_team_leader()
self.log_in(user)
response = self.client.get(reverse('all_teams'))
self.assertHttp200(response)
Despite my convenience methods for creating users (e.g. .create_team_leader) and various convenience assertions (e.g. .assertHttp200), I still end up with a lot of duplication in my tests.
(My tests are simple since I can't see anything else useful to assert about these views -- TestCase.assertTemplateUsed breaks if you rename templates, even if the view is correct.)
It's easy to miss a test, which gives me little confidence when I rename templates. Is there any way I can automatically generate test cases? Something like (psuedo-code):
for every view in urls:
if view doesn't take extra arguments:
test that view returns 200 when a logged in superuser does a GET
EDIT
Here's a representative snippet from my urls.py:
urlpatterns = patterns('',
url(r'^teams/$', 'teams.views.view_all_teams', name='all_teams'),
url(r'^teams/major/$', 'teams.views.view_major_teams', name='major_teams'),
url(r'^teams/minor/$', 'teams.views.view_minor_teams', name='minor_teams'),
url(r'^teams/(?P<team_id>\d+)/$', 'teams.views.view_team', name='view_team'),
url(r'^teams/(?P<team_id>\d+)/edit$', 'teams.views.edit_team', name='edit_team'),
url(r'^teams/(?P<team_id>\d+)/delete$', 'teams.views.delete_team', name='delete_team'),
I'd like to automatically test the first three views in this list.

from django.core import urlresolvers
from django.test import TestCase
class SimpleTest(TestCase):
def test_simple_views(self):
url_names = [
'all_teams',
'major_teams',
'minor_teams',
'view_team',
'edit_team',
]
user = self.create_team_leader()
self.log_in(user)
for url_name in url_names:
try:
url = urlresolvers.reverse(url_name, args=(), kwargs={})
except urlresolvers.NoReverseMatch:
#print('Pass {}'.format(url_name))
continue
#print('Try {}'.format(url_name))
response = self.client.get(url)
self.assertHttp200(response)
If all url patterns have their name, You can use follow code to define url_names:
url_names = [p.name for p in teams.urls.urlpatterns]
Known Issues
If view function fail, you will not know which view failed.
Views next to failed view will not be tested.
Another version that handle above issues.
import unittest
from django.core import urlresolvers
from django.test import TestCase
from teams.urls import urlpatterns
class SimpleTest(TestCase):
...
def setUp(self):
user = self.create_team_leader()
self.log_in(user)
url_names = [p.name for p in urlpatterns]
vs = vars()
def make_test_function(idx, url_name, url):
def t(self):
response = self.client.get(url)
self.assertHttp200(response)
t.__name__ = 'test_' + idx
t.__doc__ = 'simple get test for ' + url_name
return t
for i, url_name in enumerate(url_names):
i = str(i)
try:
url = urlresolvers.reverse(url_name, args=(), kwargs={})
vs['test_' + i] = make_test_function(i, url_name, url)
except urlresolvers.NoReverseMatch as e:
vs['test_' + i] = unittest.skip(url_name + ' requires parameter(s) or view not found')(lambda: 0)
del url_names, vs, make_test_function,

Related

django admin - custom action template

Im trying to add a custom action to django app, where i can redirect to a html (pdf) page i created.
currently the html page only shows 1 result, What im trying to do is to show as many as objects i selcet from the action bar in django admin. here is my code.. admin.py
def print_pdf(modelAdmin, request, queryset, **kwargs):
from django.template.loader import get_template
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
from calculator.models import Transaction
from xhtml2pdf import pisa
chp = []
for f in queryset:
chp.append(f.chp_reference)
for q in range(len(queryset)): # here im trying to know how many times to iterate based on how many qs selected
transaction = get_object_or_404(Transaction, chp_reference=chp[1]) #im not sure what to do here
template_path = 'report-pdf.html'
context = {"transactions":transactions}
response = HttpResponse(content_type='Application/pdf')
response['Content-Disposition'] = 'filename="report.pdf'
template = get_template(template_path)
html = template.render(context)
pisa_status = pisa.CreatePDF(html, dest=response)
if pisa_status.err:
return HttpResponse('we had some errors' + html )
return response
print_pdf.short_description = 'bulk pdf'
actions = [export_csv, print_pdf]
here is my code. I know its messed up, but i been trying to figure out to do this but im lost.
help would be appreciated
What's happening is the difference between the following snipppets.
def f(x)
for i in range(x):
return i ** 2
f(5) # 0 ONLY
def f(x):
results = []
for i in range(x):
results.append(i ** 2)
return results
You're returning after the first iteration, so the loop finishes after the first iteration.
You need to do the same as the second one, collect all results and return them at the end.
To illustrate what you want, this is based on your reply here. You can use something like
transactions = Transaction.objects.none()
for pk in chp:
qs = Transaction.objects.filter(chp_reference=i)
transactions = transaction.union(qs)
return transactions
and improve it based on your needs

Running specific views.py method on django_cron

I have this method on my views.py file:
def getHistoricRates():
""" Here we have the function that will retrieve the historical rates from fixer.io, since last month """
rates = {}
response = urlopen('http://data.fixer.io/api/2018-12-31?access_key=c2f5070ad78b0748111281f6475c0bdd')
data = response.read()
rdata = json.loads(data.decode(), parse_float=float)
rates_from_rdata = rdata.get('rates', {})
for rate_symbol in ['USD', 'GBP', 'HKD', 'AUD', 'JPY', 'SEK', 'NOK', 'EUR']:
try:
rates[rate_symbol] = rates_from_rdata[rate_symbol]
except KeyError:
logging.warning('rate for {} not found in rdata'.format(rate_symbol))
pass
return rates
#require_http_methods(['GET', 'POST'])
def historical(request):
date_str = "2018-12-31"
if datetime.datetime.strptime(date_str,"%Y-%m-%d").weekday()<5:
rates = getHistoricRates()
fixerio_rates = [Fixerio_rates(currency=currency, rate=rate)
for currency, rate in rates.items()]
Fixerio_rates.objects.bulk_create(fixerio_rates)
return render(request, 'historical.html')
I want to run historical at 9am every day, except for weekends.
Now, I can't find any example on how to run an existent method, or how to call it from the cron.py file whatsoever.
I do have everything configured, for django_cron but I just can't figure out how to "use" this method from my cron file to run it at specific times.
This is my cron.py file so far:
from django_cron import CronJobBase, Schedule
from .views import historical
class MyCronJob(CronJobBase):
RUN_AT_TIMES = ['9:00']
schedule = Schedule(run_at_times=RUN_AT_TIMES)
code = 'fixerio.my_cron_job' # a unique code
def do(self):
pass # do your thing here
The name fixerio is my app's name.
Any ideas on this?
Since you're looking to call the getHistoricRates() and the bulk_create() logic from both your historical() view and also a cron job, it would be better to first refactor that common code from the view into a separate module - for example into helpers.py that lives alongside the views.py and cron.py.
helpers.py
from .models import Fixerio_rates
def create_rates():
rates = getHistoricRates()
fixerio_rates = [Fixerio_rates(currency=currency, rate=rate)
for currency, rate in rates.items()]
Fixerio_rates.objects.bulk_create(fixerio_rates)
def getHistoricRates():
...
You can then invoke that from your cron job:
cron.py
from .helpers import create_rates
class MyCronJob(CronJobBase):
RUN_AT_TIMES = ['9:00']
schedule = Schedule(run_at_times=RUN_AT_TIMES)
code = 'fixerio.my_cron_job' # a unique code
def do(self):
create_rates()
And also from your view:
views.py
from .helpers import create_rates
#require_http_methods(['GET', 'POST'])
def historical(request):
date_str = "2018-12-31"
if datetime.datetime.strptime(date_str,"%Y-%m-%d").weekday()<5:
create_rates()
return render(request, 'historical.html')

How to render output of cartridge API's on custom HTML page?

I am working on a cartridge project. I have created custom html templates for better visual and now I want to render all data which is coming through cartridge's built in APIs on my custom html pages. For.ex. I have a product.html, on which I want to show all products stored in db (category wise).
Actually, I tried to explore url,
url("^shop/", include("cartridge.shop.urls")),
I am not getting that on which API or Function, this url is hitting.
urls.py file of shop app looks like this, I tested it, none of those url get called,
from __future__ import unicode_literals
from django.conf.urls import url
from mezzanine.conf import settings
from cartridge.shop import views
_slash = "/" if settings.APPEND_SLASH else ""
urlpatterns = [
url("^product/(?P<slug>.*)%s$" % _slash, views.product,
name="shop_product"),
url("^wishlist%s$" % _slash, views.wishlist, name="shop_wishlist"),
url("^cart%s$" % _slash, views.cart, name="shop_cart"),
url("^checkout%s$" % _slash, views.checkout_steps, name="shop_checkout"),
url("^checkout/complete%s$" % _slash, views.complete,
name="shop_complete"),
url("^invoice/(?P<order_id>\d+)%s$" % _slash, views.invoice,
name="shop_invoice"),
url("^invoice/(?P<order_id>\d+)/resend%s$" % _slash,
views.invoice_resend_email, name="shop_invoice_resend"),
]
These are cartridge's views for '/shop/product', '/shop/wishlist' and '/shop/cart'
from __future__ import unicode_literals
from future.builtins import int, str
from json import dumps
from django.contrib.auth.decorators import login_required
from django.contrib.messages import info
from django.core.urlresolvers import reverse
from django.db.models import Sum
from django.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404, redirect
from django.template import RequestContext
from django.template.defaultfilters import slugify
from django.template.loader import get_template
from django.template.response import TemplateResponse
from django.utils.translation import ugettext as _
from django.views.decorators.cache import never_cache
from mezzanine.conf import settings
from mezzanine.utils.importing import import_dotted_path
from mezzanine.utils.views import set_cookie, paginate
from mezzanine.utils.urls import next_url
from cartridge.shop import checkout
from cartridge.shop.forms import (AddProductForm, CartItemFormSet,
DiscountForm, OrderForm)
from cartridge.shop.models import Product, ProductVariation, Order
from cartridge.shop.models import DiscountCode
from cartridge.shop.utils import recalculate_cart, sign
try:
from xhtml2pdf import pisa
except (ImportError, SyntaxError):
pisa = None
HAS_PDF = pisa is not None
# Set up checkout handlers.
handler = lambda s: import_dotted_path(s) if s else lambda *args: None
billship_handler = handler(settings.SHOP_HANDLER_BILLING_SHIPPING)
tax_handler = handler(settings.SHOP_HANDLER_TAX)
payment_handler = handler(settings.SHOP_HANDLER_PAYMENT)
order_handler = handler(settings.SHOP_HANDLER_ORDER)
def product(request, slug, template="shop/product.html",
form_class=AddProductForm, extra_context=None):
"""
Display a product - convert the product variations to JSON as well as
handling adding the product to either the cart or the wishlist.
"""
published_products = Product.objects.published(for_user=request.user)
product = get_object_or_404(published_products, slug=slug)
fields = [f.name for f in ProductVariation.option_fields()]
variations = product.variations.all()
variations_json = dumps([dict([(f, getattr(v, f))
for f in fields + ["sku", "image_id"]]) for v in variations])
to_cart = (request.method == "POST" and
request.POST.get("add_wishlist") is None)
initial_data = {}
if variations:
initial_data = dict([(f, getattr(variations[0], f)) for f in fields])
initial_data["quantity"] = 1
add_product_form = form_class(request.POST or None, product=product,
initial=initial_data, to_cart=to_cart)
if request.method == "POST":
if add_product_form.is_valid():
if to_cart:
quantity = add_product_form.cleaned_data["quantity"]
request.cart.add_item(add_product_form.variation, quantity)
recalculate_cart(request)
info(request, _("Item added to cart"))
return redirect("shop_cart")
else:
skus = request.wishlist
sku = add_product_form.variation.sku
if sku not in skus:
skus.append(sku)
info(request, _("Item added to wishlist"))
response = redirect("shop_wishlist")
set_cookie(response, "wishlist", ",".join(skus))
return response
related = []
if settings.SHOP_USE_RELATED_PRODUCTS:
related = product.related_products.published(for_user=request.user)
context = {
"product": product,
"editable_obj": product,
"images": product.images.all(),
"variations": variations,
"variations_json": variations_json,
"has_available_variations": any([v.has_price() for v in variations]),
"related_products": related,
"add_product_form": add_product_form
}
context.update(extra_context or {})
templates = [u"shop/%s.html" % str(product.slug), template]
return TemplateResponse(request, templates, context)
#never_cache
def wishlist(request, template="shop/wishlist.html",
form_class=AddProductForm, extra_context=None):
"""
Display the wishlist and handle removing items from the wishlist and
adding them to the cart.
"""
if not settings.SHOP_USE_WISHLIST:
raise Http404
skus = request.wishlist
error = None
if request.method == "POST":
to_cart = request.POST.get("add_cart")
add_product_form = form_class(request.POST or None,
to_cart=to_cart)
if to_cart:
if add_product_form.is_valid():
request.cart.add_item(add_product_form.variation, 1)
recalculate_cart(request)
message = _("Item added to cart")
url = "shop_cart"
else:
error = list(add_product_form.errors.values())[0]
else:
message = _("Item removed from wishlist")
url = "shop_wishlist"
sku = request.POST.get("sku")
if sku in skus:
skus.remove(sku)
if not error:
info(request, message)
response = redirect(url)
set_cookie(response, "wishlist", ",".join(skus))
return response
# Remove skus from the cookie that no longer exist.
published_products = Product.objects.published(for_user=request.user)
f = {"product__in": published_products, "sku__in": skus}
wishlist = ProductVariation.objects.filter(**f).select_related("product")
wishlist = sorted(wishlist, key=lambda v: skus.index(v.sku))
context = {"wishlist_items": wishlist, "error": error}
context.update(extra_context or {})
response = TemplateResponse(request, template, context)
if len(wishlist) < len(skus):
skus = [variation.sku for variation in wishlist]
set_cookie(response, "wishlist", ",".join(skus))
return response
#never_cache
def cart(request, template="shop/cart.html",
cart_formset_class=CartItemFormSet,
discount_form_class=DiscountForm,
extra_context=None):
"""
Display cart and handle removing items from the cart.
"""
cart_formset = cart_formset_class(instance=request.cart)
discount_form = discount_form_class(request, request.POST or None)
if request.method == "POST":
valid = True
if request.POST.get("update_cart"):
valid = request.cart.has_items()
if not valid:
# Session timed out.
info(request, _("Your cart has expired"))
else:
cart_formset = cart_formset_class(request.POST,
instance=request.cart)
valid = cart_formset.is_valid()
if valid:
cart_formset.save()
recalculate_cart(request)
info(request, _("Cart updated"))
else:
# Reset the cart formset so that the cart
# always indicates the correct quantities.
# The user is shown their invalid quantity
# via the error message, which we need to
# copy over to the new formset here.
errors = cart_formset._errors
cart_formset = cart_formset_class(instance=request.cart)
cart_formset._errors = errors
else:
valid = discount_form.is_valid()
if valid:
discount_form.set_discount()
# Potentially need to set shipping if a discount code
# was previously entered with free shipping, and then
# another was entered (replacing the old) without
# free shipping, *and* the user has already progressed
# to the final checkout step, which they'd go straight
# to when returning to checkout, bypassing billing and
# shipping details step where shipping is normally set.
recalculate_cart(request)
if valid:
return redirect("shop_cart")
context = {"cart_formset": cart_formset}
context.update(extra_context or {})
settings.use_editable()
if (settings.SHOP_DISCOUNT_FIELD_IN_CART and
DiscountCode.objects.active().exists()):
context["discount_form"] = discount_form
return TemplateResponse(request, template, context)
When you hit shop url, your application will try to use an empty url from your cartridge.shop.urls file. So basically when you would like to check which API / view is called, go to this file and look for something similar to this:
url(r'^$', 'your-view', name='your-view'),
ok after posting your second urls file you have following options:
you call:
/shop/wishlist/ - you are executing a view named wishlist
/shop/cart/ - you are executing a view named cart
/shop/checkout/complete/ - you are executing a view named complete
so just find your views.py file, and all those views are going to be there

Unable to retrieve HTTP Post data from external API in django

I am getting error : 'str' object has no attribute 'method' . See my code below :
#csrf_exempt
def completepayment(request):
varerr =''
plist = []
if request.method == 'POST':
try:
nid = request.POST['txnref']
except MultiValueDictKeyError:
varerr ="Woops! Operation failed due to server error. Please try again later."
return render(request, 'uportal/main.html', {'varerr':varerr})
# Fetching member details
trym = Transactions.objects.get(TransRef=nid)
amount = trym.Amount
famt = int(amount * 100)
product_id = 48
salt = '4E6047F9E7FDA5638D29FD'
hash_object = hashlib.sha512(str(product_id)+str(nid)+str(famt))
hashed = hash_object.hexdigest()
url = 'https://bestng.com/api/v1/gettransaction.json?productid=pdid&transactionreference=nid&amount=famt'
raw = urllib.urlopen(url)
js = raw.readlines()
#js_object = simplejson.loads(js)
res = simplejson.dumps(js)
for item in res:
rcode = item[0]
#rdesc = item[1]
#preff = item[2]
thisresp = completepayment(rcode)
plist.append(thisresp)
else:
varerr ="Woops! Operation failed due to server error. Please try again later."
return render(request, 'uportal/main.html', {'plist':plist, 'varerr':varerr, 'completepayment':'completepayment'})
In summary I am trying to accept and use HTTP POST value from an external API. Value is showing when I inspect element but DJANGO not retrieving. Please help.
Here is my urls.py
from django.conf.urls import patterns, url
from views import *
from django.views.generic import RedirectView
urlpatterns = patterns('myproject.prelude.views',
# Home:
url(r'^$', 'home', name='home'),
#login
url(r'^login/$', 'login', name='login'),
url(r'^welcome/$', 'welcome', name='welcome'),
# Registration Portal
# Registration Portal
url(r'^uportal/$', 'uportal', name='uportal'),
url(r'^uportal/ugreg/find/$', 'findmember', name='findmember'),
url(r'^uportal/ugreg/search/$', 'searchmember', name='searchmember'),
url(r'^uportal/ugreg/$', 'ugreg', name='ugreg'),
url(r'^uportal/ugreg/initiate-payment/$', 'initiatepayment', name='initiatepayment'),
url(r'^uportal/ugreg/verifypayment/$', 'verifypayment', name='verifypayment'),
url(r'^uportal/ugreg/proceedpayment/$', RedirectView.as_view(url='https://bestng.com/pay'), name='remote_admin'),
url(r'^uportal/ugreg/completepayment/$', completepayment, name='completepayment'),
Thank you
It appears that your problem is that request is an str object rather than a request object.
Please produce urls.py and views.py.
For readability, let’s rewrite the part below:
url = 'https://bestng.com/api/v1/gettransaction.json'
params = '?productid={product_id}&transactionreference={nid}&amount={famt}'
raw = urllib.urlopen(url + params.format(**locals()))
Or even, like so:
url = 'https://bestng.com/api/v1/gettransaction.json'
params = '?productid={product_id}&transactionreference={nid}&amount={famt}'
request = url + params.format(**locals())
raw = urllib.urlopen(request)
Also, the try block is not what I would use. Instead, I would use the get method of the POST dict and return a flag value:
nid = request.POST.get('tnxref', False)
I am unable to reproduce the error you are getting. With a slightly different project-level urls.py (very simplified), the ‘completepayment’ view works fine for me. Here is urls.py.
from django.conf.urls import patterns, url
from app.views import completepayment
# The app is simply called app in my example.
urlpatterns = patterns('',
# I remove the prefix
url(r'^uportal/ugreg/completepayment/$', completepayment, name='completepayment'),
)
# This last parenthesis might be missing in your code.

I don't understand tests in Django, Can you help me please?

I am having a hard time with tests in Django and Python, for my final project I am making a forums website, but I don't really have any idea how or what my tests should be. Here is the views page from mysite file. Could someone please walk me through what I should test for besides if a user is logged in.
from django.core.urlresolvers import reverse
from settings import MEDIA_ROOT, MEDIA_URL
from django.shortcuts import redirect, render_to_response
from django.template import loader, Context, RequestContext
from mysite2.forum.models import *
def list_forums(request):
"""Main listing."""
forums = Forum.objects.all()
return render_to_response("forum/list_forums.html", {"forums":forums}, context_instance=RequestContext(request))
def mk_paginator(request, items, num_items):
"""Create and return a paginator."""
paginator = Paginator(items, num_items)
try: page = int(request.GET.get("page", '1'))
except ValueError: page = 1
try:
items = paginator.page(page)
except (InvalidPage, EmptyPage):
items = paginator.page(paginator.num_pages)
return items
def list_threads(request, forum_slug):
"""Listing of threads in a forum."""
threads = Thread.objects.filter(forum__slug=forum_slug).order_by("-created")
threads = mk_paginator(request, threads, 20)
template_data = {'threads': threads}
return render_to_response("forum/list_threads.html", template_data, context_instance=RequestContext(request))
def list_posts(request, forum_slug, thread_slug):
"""Listing of posts in a thread."""
posts = Post.objects.filter(thread__slug=thread_slug, thread__forum__slug=forum_slug).order_by("created")
posts = mk_paginator(request, posts, 15)
thread = Thread.objects.get(slug=thread_slug)
template_data = {'posts': posts, 'thread' : thread}
return render_to_response("forum/list_posts.html", template_data, context_instance=RequestContext(request))
def post(request, ptype, pk):
"""Display a post form."""
action = reverse("mysite2.forum.views.%s" % ptype, args=[pk])
if ptype == "new_thread":
title = "Start New Topic"
subject = ''
elif ptype == "reply":
title = "Reply"
subject = "Re: " + Thread.objects.get(pk=pk).title
template_data = {'action': action, 'title' : title, 'subject' : subject}
return render_to_response("forum/post.html", template_data, context_instance=RequestContext(request))
def new_thread(request, pk):
"""Start a new thread."""
p = request.POST
if p["subject"] and p["body"]:
forum = Forum.objects.get(pk=pk)
thread = Thread.objects.create(forum=forum, title=p["subject"], creator=request.user)
Post.objects.create(thread=thread, title=p["subject"], body=p["body"], creator=request.user)
return HttpResponseRedirect(reverse("dbe.forum.views.forum", args=[pk]))
def reply(request, pk):
"""Reply to a thread."""
p = request.POST
if p["body"]:
thread = Thread.objects.get(pk=pk)
post = Post.objects.create(thread=thread, title=p["subject"], body=p["body"],
creator=request.user)
return HttpResponseRedirect(reverse("dbe.forum.views.thread", args=[pk]) + "?page=last")
First read the Django testing documentation. You might also want to read this book. It's dated in some areas, but testing is still pretty much the same now as it was in 1.1.
It's a bit much of a topic to cover in an SO answer.
Well, you could test:
If you have the right number of pages for the objects you're paginating.
If the page you're viewing contains the right object range. If trying to access a page that doesn't exist returns the
appropriate error.
If your views for listing objects and object detail return the correct HTTP status code (200)
For starters. Hope that helps you out.

Categories