I want to test my views functionality for a Wagtail Page that uses the RoutablePageMixin. I've found that Wagtail includes some testing functionality, but I'm trying to check the content on different urls. Although the Wagtail test functions work, testing using self.client.get doesn't work -- I get an 404 response. I'm trying the following test:
def test_subpage(self):
response = self.client.get(
self.page.full_url + self.page.reverse_subpage('subpage')
)
self.assertEqual(response.status_code, 200,
'Request the open positions page')
I assume the error lies in the way in which the page is created. I have used several ways, but cannot find one for which this works. The most intuitive way I found to create the page is the following:
def setUp(self):
self.login()
parent = Page.get_root_nodes()[0] # Home
self.assertCanCreate(parent, MyPage, {
'title': 'Title!',
'title_sv': 'Title2!',
'slug': 'test',
'published': datetime.datetime.now(),
})
self.page = MyPage.objects.get(slug='apply')
The subpages have been manually tested and do seem to work.
The simplest way to create a page within test code (or any other code...) is:
parent = Page.objects.get(url_path='/home/')
page = MyPage(title='Title!', slug='test', body='...')
parent.add_child(instance=page)
Note that Page.get_root_nodes()[0] will not return your site homepage - the tree root is a non-editable placeholder, and site homepages are usually children of it. You can create additional children of the root, but unless you give them a corresponding Site record, they won't be accessible under any URL (which is probably why you're getting a 404 in your test).
Related
I am wondering if someone could help me. I am trying to test some views in a Django restaurant bookings system app I have created. I am doing well with jut testing the views but now I want to test the CRUD functionality of certain pages. In particular the create a booking on the bookings page and then redirect it back to the home page once the booking was successful (as is what happens on the site)
I just can't seem to figure out how to do it. Here is my current code. If someone could point me in the right direction that would be great. Thanks
setUp:
class TestViews(TestCase):
"""
Testing of the views taken from
the views.py file.
All HTTP testing includes the base.html template
as well as the view being tested to make sure everything
is being tested as it would appear for a user
"""
def setUp(self):
testing_user = User.objects.create_user(
username='JohnSmith',
first_name='John',
last_name='Smith',
email='johnsmith#email.com',
password='RandomWord1'
)
Booking.objects.create(
user=testing_user,
name='John Smith',
email_address='johnsmith#email.com',
phone='123654789',
number_of_people='2',
date='2022-10-20',
time='19:00',
table='Window',
occasion='none'
)
test:
def test_add_booking(self):
self.log_in()
response = self.client.post('/bookings', {Booking: Booking})
self.assertRedirects(response, '/')
You can check the status's code of your response, then also compare the response content.
self.assertEqual(response.status_code, 200)
self.assertContains(response, "No bookins.")
Or even to be more precisely self.assertQuerysetEqual(response.context["the name"], ["with real content"]
how to handle two different views witch match for the same url? (Both views need a database call to determine if the called element is available. Changing the url structure is not an option.)
url(r'^', include(wagtail_urls)),
This wagtail url is matching all url's and is raising an 404 if the according page/slug is not in the database.
But, I also have my own view witch behaves similar. How can I tell django to continue with the next url instead of raising a 404?
(I could place my own view before the wagtail view and modify it, but I don't know how to return to the next url?)
This is my solution at the end:
from wagtail.wagtailcore.views import serve
# ... in my view where I normally return a 404 => I set this:
return serve(self.request, self.request.path)
First of all, Sharing same url pattern in diffrent views is bad idea.
The bigger your project, the harder it will be to maintain it.
Nevertheless, there is a way if you want that way.
You can place your own view first in urls.py like your saying,
process some your own logic first and catch 404 exception when there is nothing to show in your view than simply call the "Wagtail" view with request original parameters(page, slug, etc..) with return statement.
Below is example.
This example is based on Django functional based view, but there is the way in class based view something like this.
def your_own_view(request, slugs):
try:
article = get_object_or_404(Article, slugs=slugs)
except Http404:
return some_wagtail_view(request, slugs)
...
return render(request, "article/view.html", context)
This is my basic view using DjangoRestFramework:
class user_list(APIView):
"""
List all users, or create a new user.
"""
def get(self, request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
def post(self, request):
serializer = UserSerializer(data=request.DATA)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
When I go to the URL which calls this view (localhost:8000/CMS/users), DjangoRestFramework already has a frontend which says:
GET /CMS/users
HTTP 200 OK
Vary: Accept
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
[
{
"username": "t",
}
]
How do I customize this? I want to use AngularJS on the frontend. I know that without DjangoRestFramework, I would return an html template which would be located in my Django App directory in a folder called "Templates". I've been told that DjangoRestFramework is useful becauase I can create a RESTful API which returns JSON objects. I tried adding the following to my settings.py file:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
)
}
but I still can't seem to figure out how I can customize the frontend of my Django application. I know that there is:
renderer_classes = (TemplateHTMLRenderer,)
in which I can return an html page in my view, like so:
return Response({'user': self.object}, template_name='user_detail.html')
but doesn't returning actual HTML pages defeat the purpose of DjangoRestFramework's ability to return JSON objects and create a RESTful API?
First off, there are some things to make Django Rest Framework play better with angular. Until I decided to try out ember, I was working through this utorial. To try to provide a useful example for your question:
Typically you want to put your DjangoRestFramework REST Api in something like /api.
If you're doing the seemingly ubiquitiious single page application, you can then have index.html get served up as a template (i.e. just have your default home view be a TemplateView with index.html as the template)
You would then have your angular html templates and your angular/jquery/etc. javsascripts and your css files as normal static files. NB that you generally don't want to have django templates generate angular code. First off, it makes things much more of a pain to debug, and secondly, the default angular and django template syntax use the same <% characters meaning you have to be careful to make sure one doesn't try to interpret the others templating code.
When you load http://yourserver/, this will bring up the index.html page which then pulls down angular stuff, which then starts making RESTful calls to your REST api to generate the data. Note that you can start mixing in stuff to use Django forms etc., but I'd recommend keeping it simple and then reading the linked article (or another) once you have the basics going.
On the index page of my Django site I generate a list of urls that allow the user to navigate to a detail page. Once the detail page appears the navigation is no longer visible.
What I am trying to achieve is that the navigation list appears on the detail page (and every page that is added to my site).
What I have tried is the following: (the first line in each view is duplicated)
def index(request):
**collection_urls = Collection.objects.order_by('the_year')**
return render(request, 'index.html', {'collection_url': collection_urls})
def originalexample(request, collection_id):
**collection_urls = CarCollection.objects.order_by('the_year')**
car = get_object_or_404(CarCollection, pk=collection_id)
return render(request, 'detail.html', {'originalexample': car, 'collection_url': collection_urls})
Whilst this works, I know it is not right as I running the query twice. My next thought would be to perform the query once and then pass it to the pages as they are rendered. If so how would I do that? or is there a more pythonic method?
In case of need I'm using Django 1.6.2 and the list of urls does not change frequently (They can only be changed via the Django admin screens)
Thanks in advance.
You don't want to pass them from one to another, you simply want them to appear everywhere. That is usually done via context processors or custom tags.
I have a view (views.loaditems) which runs some algorithm and passes items to a template(product.html) where the items are loaded, and with each item, I have a "add_to_shortlist" link. On clicking this link, the item is added in the user's shortlist (for which I have a function). I want that on click, the page is not reloaded and has its items, but simply add that item to the user's shortlist. Also, where should I define this shortlist function?
I'm new to Django, and any help would be much appreciated. Thanks.
Update: Here's my code:
views.py
def loaditems(request):
#some code
ourdeals = SDeals.objects.filter(**{agestring3:0})
sorteddeals = ourdeals.order_by('-total_score')
user = request.user
context = {'deals': sorteddeals, 'sl_products':sl_products, 'user':user,}
template='index.html'
return render_to_response(template, context, context_instance=RequestContext(request))
def usersl(request, id, id2):
userslt = User_Shortlist.objects.filter(id__iexact=id)
products = SDeals.objects.filter(id__iexact=id2)
product = products[0]
if userslt:
userslt[0].sdeals.add(product)
sl = userslt[0].sdeals.all()
return render_to_response('slnew.html', {'sl':sl}, context_instance=RequestContext(request))
in my index.html I have:
<div class="slist"></div>
which in urls.py takes me to views.usersl:
url(r'^usersl/(?P<id>\d+)/(?P<id2>\d+)/$', views.usersl),
I don't want to go to slnew.html, instead be on index.html without reloading it, and on click 'slist', just run the function to add to shortlist.
In order to make changes on the server and in a page without navigating with the browser you need to look at JavaScript solutions. Read up about Ajax. In essence you need to use some JavaScript to send the update to the server, and to change the HTML.
JQuery is one popular library that will help you to do this. A more sophisticated example is AngularJS. On the Django side you'll write some views that handle these small update tasks used in the page. Libraries like Django REST framework or Django Slumber will help you with that.