{"id":75670,"date":"2020-01-20T11:28:48","date_gmt":"2020-01-20T05:58:48","guid":{"rendered":"https:\/\/www.vskills.in\/certification\/tutorial\/?p=75670"},"modified":"2024-04-12T14:17:14","modified_gmt":"2024-04-12T08:47:14","slug":"search","status":"publish","type":"page","link":"https:\/\/www.vskills.in\/certification\/tutorial\/search\/","title":{"rendered":"Search"},"content":{"rendered":"<p>The Web is all about search. Two of the Net\u2019s biggest success stories, Google and Yahoo, built their multi-billion-dollar businesses around search. Nearly every site sees a large percentage of traffic coming to and from its search pages. Often the difference between the success or failure of a site is the quality of its search. So it looks like we\u2019d better add some searching to our fledgling books site, no?<\/p>\n<p>We\u2019ll start by adding the search view to our URLconf (mysite.urls). Recall that this means adding something like (r&#8217;^search\/$&#8217;, &#8216;mysite.books.views.search&#8217;) to the set of URL patterns. Next, we\u2019ll write this search view into our view module (mysite.books.views):<\/p>\n<pre>from django.db.models import Q\nfrom django.shortcuts import render_to_response\nfrom models import Book\n\ndef search(request):\nquery = request.GET.get('q', '')\nif query:\nqset = (\nQ(title__icontains=query) |\nQ(authors__first_name__icontains=query) |\nQ(authors__last_name__icontains=query)\n)\nresults = Book.objects.filter(qset).distinct()\nelse:\nresults = []\nreturn render_to_response(\"books\/search.html\", {\n\"results\": results,\n\"query\": query\n})<\/pre>\n<p>There are a couple of things going on here that you haven\u2019t yet seen. First, there\u2019s request.GET. This is how you access GET data from Django; POST data is accessed through a similar request.POST object. These objects behave exactly like standard Python dictionaries.<\/p>\n<p><strong>What\u2019s GET and POST Data?<\/strong> &#8211; GET and POST are the two methods that browsers use to send data to a server. Most of the time, you\u2019ll see them in HTML form tags:<\/p>\n<pre>&lt;form action=\"\/books\/search\/\" method=\"get\"&gt;<\/pre>\n<p>This instructs the browser to submit the form data to the URL \/books\/search\/ using the GET method. There are important differences between the semantics of GET and POST that we won\u2019t get into right now, but see http:\/\/www.w3.org\/2001\/tag\/doc\/whenToUseGet.html if you want to learn more. So the line:<\/p>\n<pre>query = request.GET.get('q', '')<\/pre>\n<p>looks for a GET parameter named q and returns an empty string if that parameter wasn\u2019t submitted.<\/p>\n<p>Note that we\u2019re using the get() method on request.GET, which is potentially confusing. The get() method here is the one that every Python dictionary has. We\u2019re using it here to be careful: it is not safe to assume that request.GET contains a &#8216;q&#8217; key, so we use get(&#8216;q&#8217;, &#8221;) to provide a default fallback value of &#8221; (the empty string). If we merely accessed the variable using request.GET[&#8216;q&#8217;], that code would raise a KeyError if q wasn\u2019t available in the GET data.<\/p>\n<p>Second, what about this Q business? Q objects are used to build up complex queries \u2014 in this case, we\u2019re searching for any books where either the title or the name of one of the authors matches the search query. Technically, these Q objects comprise a QuerySet.<\/p>\n<p>In these queries, icontains is a case-insensitive search that uses the SQL LIKE operator in the underlying database.<\/p>\n<p>Since we\u2019re searching against a many-to-many field, it\u2019s possible for the same book to be returned more than once by the query (e.g., a book with two authors who both match the search query). Adding .distinct() to the filter lookup eliminates any duplicate results.<br>\nThere\u2019s still no template for this search view, however. This should do the trick:<\/p>\n<pre>&lt;!DOCTYPE HTML PUBLIC \"-\/\/W3C\/\/DTD HTML 4.01\/\/EN\"&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n&lt;title&gt;Search{% if query %} Results{% endif %}&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n&lt;h1&gt;Search&lt;\/h1&gt;\n&lt;form action=\".\" method=\"GET\"&gt;\n&lt;label for=\"q\"&gt;Search: &lt;\/label&gt;\n&lt;input type=\"text\" name=\"q\" value=\"{{ query|escape }}\"&gt;\n&lt;input type=\"submit\" value=\"Search\"&gt;\n&lt;\/form&gt;\n\n{% if query %}\n&lt;h2&gt;Results for \"{{ query|escape }}\":&lt;\/h2&gt;\n\n{% if results %}\n&lt;ul&gt;\n{% for book in results %}\n&lt;li&gt;{{ book|escape }}&lt;\/l1&gt;\n{% endfor %}\n&lt;\/ul&gt;\n{% else %}\n&lt;p&gt;No books found&lt;\/p&gt;\n{% endif %}\n{% endif %}\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p>Hopefully by now what this does is fairly obvious. However, there are a few subtleties worth pointing out: The form\u2019s action is ., which means \u201cthe current URL.\u201d This is a standard best practice: don\u2019t use separate views for the form page and the results page; use a single one that serves the form and search results.<\/p>\n<p>We reinsert the value of the query back into the &lt;input&gt;. This lets readers easily refine their searches without having to retype what they searched for. Everywhere query and book is used, we pass it through the escape filter to make sure that any potentially malicious search text is filtered out before being inserted into the page. It\u2019s vital that you do this with any user-submitted content! Otherwise you open your site up to cross-site scripting (XSS) attacks.<\/p>\n<p>However, we don\u2019t need to worry about harmful content in your database lookups \u2014 we can simply pass the query into the lookup as is. This is because Django\u2019s database layer handles this aspect of security for you.<\/p>\n<p>Now we have a working search. A further improvement would be putting a search form on every page (i.e., in the base template); we\u2019ll let you handle that one yourself.<\/p>\n<p>So why the distinction?<\/p>\n<p>Because both request.GET and request.POST have additional methods that normal dictionaries don\u2019t have. We\u2019ll get into these in a short while. You might have encountered the similar term \u201cfile-like objects\u201d \u2013 Python objects that have a few basic methods, like read(), that let them act as stand-ins for \u201creal\u201d file objects.<\/p>\n<h3>HTML Forms<\/h3>\n<p>HTML forms are the backbone of interactive web sites, from the simplicity of Google\u2019s single search box to ubiquitous blog comment submission forms, to complex custom data-entry interfaces. We will cover how you can use Django to access user-submitted form data, validate it and do something with it. Along the way, we\u2019ll cover HttpRequest and Form objects.<\/p>\n<h3>Getting Data from the Request Object<\/h3>\n<p>Recall that each view function takes an HttpRequest object as its first parameter, as in our hello() view:<\/p>\n<pre>from django.http import HttpResponse\n\ndef hello(request):\nreturn HttpResponse(\"Hello world\")<\/pre>\n<p>HttpRequest objects, such as the variable request here, have a number of interesting attributes and methods that you should familiarize yourself with, so that you know what\u2019s possible. You can use these attributes to get information about the current request (i.e., the user\/web browser that\u2019s loading the current page on your Django-powered site), at the time the view function is executed.<br>\nInformation About the URL<br>\nHttpRequest objects contain several pieces of information about the currently requested URL.<\/p>\n<table>\n<thead>\n<tr>\n<td width=\"213\"><strong>Attribute\/method <\/strong><\/td>\n<td width=\"213\"><strong>Description <\/strong><\/td>\n<td width=\"213\"><strong>Example<\/strong><\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td width=\"213\">request.path<\/td>\n<td width=\"213\">The full path, not including the domain but including the leading slash.<\/td>\n<td width=\"213\">\u201c\/hello\/\u201c<\/td>\n<\/tr>\n<tr>\n<td width=\"213\">request.get_host()<\/td>\n<td width=\"213\">The host (i.e., the \u201cdomain,\u201d in common parlance).<\/td>\n<td width=\"213\">\u201c127.0.0.1:8000\u201d or \u201cwww.example.com\u201c<\/td>\n<\/tr>\n<tr>\n<td width=\"213\">request.get_full_path()<\/td>\n<td width=\"213\">The path, plus a query string (if available).<\/td>\n<td width=\"213\">\u201c\/hello\/?print=true\u201c<\/td>\n<\/tr>\n<tr>\n<td width=\"213\">request.is_secure()<\/td>\n<td width=\"213\">True if the request was made via HTTPS. Otherwise, False.<\/td>\n<td width=\"213\">True or False<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Always use these attributes\/methods instead of hard-coding URLs in your views. This makes for more flexible code that can be reused in other places. A simplistic example:<\/p>\n<pre># BAD!\ndef current_url_view_bad(request):\nreturn HttpResponse(\"Welcome to the page at \/current\/\")\n\n# GOOD\ndef current_url_view_good(request):\nreturn HttpResponse(\"Welcome to the page at %s\"\n% request.path)\n\n<\/pre>\n<h3 class=\"vskills3\"><span lang=\"X-NONE\">Other Information About the Request<\/span><\/h3>\n<p>request.META is a Python dictionary containing all available HTTP headers for the given request \u2013 including the user\u2019s IP address and user agent (generally the name and version of the web browser). Note that the full list of available headers depends on which headers the user sent and which headers your web server sets. Some commonly available keys in this dictionary are:<\/p>\n<ul>\n<li>&nbsp;HTTP_REFERER \u2013 The referring URL, if any. (Note the misspelling of REFERER.)Other Information About the Request<\/li>\n<li>HTTP_USER_AGENT \u2013 The user\u2019s browser\u2019s user-agent string, if any. This looks something like: &#8220;Mozilla\/5.0 (X11; U; Linux&#8220; i686; fr-FR; rv:1.8.1.17) Gecko\/20080829 Firefox\/2.0.0.17&#8221;.<\/li>\n<li>REMOTE_ADDR \u2013 The IP address of the client, e.g., &#8220;12.345.67.89&#8221;. (If the request has passed through any proxies, then this might be a comma-separated list of IP addresses, e.g., &#8220;12.345.67.89,23.456.78.90&#8221;.)<\/li>\n<\/ul>\n<p>Note that because request.META is just a basic Python dictionary, you\u2019ll get a KeyError exception if you try to access a key that doesn\u2019t exist. (Because HTTP headers are external data \u2013 that is, they\u2019re submitted by your users\u2019 browsers \u2013 they shouldn\u2019t be trusted, and you should always design your application to fail gracefully if a particular header is empty or doesn\u2019t exist.) You should either use a try\/except clause or the get() method to handle the case of undefined keys:<\/p>\n<pre># BAD!\ndef ua_display_bad(request):\nua = request.META['HTTP_USER_AGENT'] # Might raise KeyError!\nreturn HttpResponse(\"Your browser is %s\" % ua)\n\n# GOOD (VERSION 1)\ndef ua_display_good1(request):\ntry:\nua = request.META['HTTP_USER_AGENT']\nexcept KeyError:\nua = 'unknown'\nreturn HttpResponse(\"Your browser is %s\" % ua)\n\n# GOOD (VERSION 2)\ndef ua_display_good2(request):\nua = request.META.get('HTTP_USER_AGENT', 'unknown')\nreturn HttpResponse(\"Your browser is %s\" % ua)\n\nI encourage you to write a small view that displays all of the request.META data so you can get to know what\u2019s in there. Here\u2019s what that view might look like:\n\ndef display_meta(request):\nvalues = request.META \nhtml = []\nfor k in sorted(values):\nhtml.append('&lt;tr&gt;&lt;td&gt;%s&lt;\/td&gt;&lt;td&gt;%s&lt;\/td&gt;&lt;\/tr&gt;' % (k, values[k]))\nreturn HttpResponse('&lt;table&gt;%s&lt;\/table&gt;' % '\\n'.join(html))<\/pre>\n<p>Another good way to see what sort of information that the request object contains is to look closely at the Django error pages when you crash the system \u2013 there is a wealth of useful information in there, including all the HTTP headers and other request objects (request.path for example).<\/p>\n<h3>Information About Submitted Data<\/h3>\n<p>Beyond basic metadata about the request, HttpRequest objects have two attributes that contain information submitted by the user: request.GET and request.POST. Both of these are dictionary-like objects that give you access to GET and POST data. POST data generally is submitted from an HTML &lt;form&gt;, while GET data can come from a &lt;form&gt; or the query string in the page\u2019s URL.<\/p>\n<p><strong>Dictionary-like objects<\/strong> &#8211; When I say request.GET and request.POST are \u201cdictionary-like\u201d objects, I mean that they behave like standard Python dictionaries but aren\u2019t technically dictionaries under the hood. For example, request.GET and request.POST both have get(), keys() and values() methods, and you can iterate over the keys by doing for key in request.GET.<\/p>\n<h3>A Simple Django Form-Handling Example<\/h3>\n<p>Continuing the ongoing example of books, authors and publishers, let\u2019s create a simple view that lets users search our book database by title. Generally, there are two parts to developing a form: the HTML user interface and the backend view code that processes the submitted data. The first part is easy; let\u2019s just set up a view that displays a search form. When you used startapp to create your books app, Django created a new views.py file for you in your \\books folder. Go ahead and add a new view to this file:<\/p>\n<pre># \\books\\views.py\n\nfrom django.shortcuts import render\n\ndef search_form(request):\nreturn render(request, 'books\/search_form.html')<\/pre>\n<p>Next step is to create the template, however we first need to create a couple of new folders for the template. If you haven\u2019t changed it, the &#8216;APP_DIRS&#8217; in your settings file is set to True. This means that Django will search all of your apps for a folder named \\templates.<\/p>\n<p>Create a new \\templates folder inside your books app folder. Then go ahead and create another folder inside the new \\templates folder and call it books. Your final folder structure will be books\\templates\\books\\.<\/p>\n<p>This inner books folder is important for namespacing your templates. Because Django will search all apps for a matching template, creating a namespace for the app templates ensures that Django uses the correct template if two apps used the same template name.<\/p>\n<p>Create the following search_form.html file and save it to your new folder:<\/p>\n<pre># mysite_project\\mysite\\books\\templates\\books\\search_form.html\n\n&lt;html&gt;\n&lt;head&gt;\n&lt;title&gt;Search&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n&lt;form action=\"\/search\/\" method=\"get\"&gt;\n&lt;input type=\"text\" name=\"q\"&gt;\n&lt;input type=\"submit\" value=\"Search\"&gt;\n&lt;\/form&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p>Now we need to create a URLconf so Django can find our new view. Inside your books folder, create a new urls.py file (startapp doesn\u2019t create this file). Add the following URL pattern to this new urls.py:<\/p>\n<pre># mysite\\books\\urls.py\n\nfrom django.conf.urls import url\nfrom books import views\n\nurlpatterns = [\nurl(r'^search-form\/\n<p>(Note that we\u2019re importing the views module directly, instead of something like from books.views import search_form, because the former is less verbose.<\/p>\n<p>One last thing \u2013 when Django searches for URL patterns, it will only search the base mysite\\urls.py file, unless we explicitly include the URL patterns from other apps. So let\u2019s go ahead and modify our site urlpatterns:<\/p>\n<pre># mysite\\urls.py\n\nfrom django.conf.urls import include, url\n\nurlpatterns = [\n# ...\nurl(r'^', include('books.urls')),\n]<\/pre>\n<p>This new URL pattern must be added to the end of the urlpatterns list. This is because the r'^' regex sends everything to books.urls, so we want to make sure none of the other patterns match, before sending Django to check books\\urls.py for a matching pattern.<\/p>\n<p>Now, if you run the development server and visit http:\/\/127.0.0.1:8000\/search-form\/, you\u2019ll see the search interface (Figure 6.1). Simple enough.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128604 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/25-1.jpg\" alt=\"\" width=\"1033\" height=\"223\"><\/p>\n<p>Try submitting the form, though, and you\u2019ll get a Django 404 error. The form points to the URL \/search\/, which hasn\u2019t yet been implemented. Let\u2019s fix that with a second view function:<\/p>\n<pre># books\/urls.py\n\nurlpatterns = [\nurl(r'^search-form\/\n<ul>\n<li>The HTML &lt;form&gt; defines a variable q. When it\u2019s submitted, the value of q is sent via GET (method=\"get\") to the URL \/search\/.<\/li>\n<li>The Django view that handles the URL \/search\/ (search()) has access to the q value in request.GET.An important thing to point out here is that we explicitly check that 'q' exists in request.GET. As I pointed out in the request.META section above, you shouldn\u2019t trust anything submitted by users or even assume that they\u2019ve submitted anything in the first place. If we didn\u2019t add this check, any submission of an empty form would raise KeyError in the view:<\/li>\n<\/ul>\n<pre># BAD!\ndef bad_search(request):\n# The following line will raise KeyError if 'q' hasn't been submitted!\nmessage = 'You searched for: %r' % request.GET['q']\nreturn HttpResponse(message)<\/pre>\n<p><strong>Query String Parameters<\/strong> - Because GET data is passed in the query string (e.g., \/search\/?q=django), you can use request.GET to access query string variables. In introduction of Django\u2019s URLconf system, I compared Django\u2019s pretty URLs to more traditional PHP\/Java URLs such as \/time\/plus?hours=3 and said I\u2019d show you how to do the latter.<\/p>\n<p>Now you know how to access query string parameters in your views (like hours=3 in this example) \u2013 use request.GET. POST data works the same way as GET data \u2013 just use request.POST instead of request.GET. What\u2019s the difference between GET and POST?<\/p>\n<p>Use GET when the act of submitting the form is just a request to \u201cget\u201d data. Use POST whenever the act of submitting the form will have some side effect \u2013 changing data, or sending an e-mail, or something else that\u2019s beyond simple display of data. In our book search example, we\u2019re using GET because the query doesn\u2019t change any data on our server.<\/p>\n<p>Now that we\u2019ve verified request.GET is being passed in properly, let\u2019s hook the user\u2019s search query into our book database (again, in views.py):<\/p>\n<pre>from django.http import HttpResponse\nfrom django.shortcuts import render\nfrom books.models import Book\n\ndef search(request):\nif 'q' in request.GET and request.GET['q']:\nq = request.GET['q']\nbooks = Book.objects.filter(title__icontains=q)\nreturn render(request, 'books\/search_results.html',\n{'books': books, 'query': q})\nelse:\nreturn HttpResponse('Please submit a search term.')<\/pre>\n<p>A couple of notes on what we did here:<\/p>\n<ul>\n<li>&nbsp;Aside from checking that 'q' exists in request.GET, we also make sure that request.GET['q'] is a non-empty value before passing it to the database query.<\/li>\n<li>We\u2019re using Book.objects.filter(title__icontains=q) to query our book table for all books whose title includes the given submission. The icontains is a lookup type, and the statement can be roughly translated as \u201cGet the books whose title contains q, without being case-sensitive.\u201d<\/li>\n<\/ul>\n<p>This is a very simple way to do a book search. I wouldn\u2019t recommend using a simple icontains query on a large production database, as it can be slow. (In the real world, you\u2019d want to use a custom search system of some sort. Search the web for open-source full-text search to get an idea of the possibilities.)<\/p>\n<p>We pass books, a list of Book objects, to the template. To get our new search form working, let\u2019s create the search_results.html file:<\/p>\n<pre># \\books\\templates\\books\\search_results.html\n\n&lt;html&gt; \n&lt;head&gt; \n&lt;title&gt;Book Search&lt;\/title&gt; \n&lt;\/head&gt; \n&lt;body&gt; \n&lt;p&gt;You searched for: &lt;strong&gt;{{ query }}&lt;\/strong&gt;&lt;\/p&gt;\n\n{% if books %} \n&lt;p&gt;Found {{ books|length }} book{{ books|pluralize }}.&lt;\/p&gt;\n&lt;ul&gt; \n{% for book in books %} \n&lt;li&gt;{{ book.title }}&lt;\/li&gt; \n{% endfor %} \n&lt;\/ul&gt; \n{% else %} \n&lt;p&gt;No books matched your search criteria.&lt;\/p&gt; \n{% endif %} \n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p>Note usage of the pluralize template filter, which outputs an \u201cs\u201d if appropriate, based on the number of books found.<\/p>\n<p>Now, when you run the development server and visit http:\/\/127.0.0.1:8000\/search-form\/, your search term should return a more useful result below figure.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128600 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/27.jpg\" alt=\"\" width=\"1033\" height=\"283\"><\/p>\n<p><strong>Improving Our Simple Form-Handling Example<\/strong> - As in previous chapters, I\u2019ve shown you the simplest thing that could possibly work. Now I\u2019ll point out some problems and show you how to improve it. First, our search() view\u2019s handling of an empty query is poor \u2013 we\u2019re just displaying a \u201cPlease submit a search term.\u201d message, requiring the user to hit the browser\u2019s back button. This is horrid and unprofessional, and if you ever actually implement something like this in the wild, your Django privileges will be revoked.<\/p>\n<p>It would be much better to redisplay the form, with an error above it, so that the user can try again immediately. The easiest way to do that would be to render the template again, like this:<\/p>\n<pre>from django.shortcuts import render\nfrom django.http import HttpResponse\nfrom books.models import Book\n\ndef search_form(request):\nreturn render(request, 'books\/search_form.html')\n\ndef search(request):\nerror = False\nif 'q' in request.GET:\nq = request.GET['q']\nif not q:\nerror = True\nelse:\nbooks = Book.objects.filter(title__icontains=q)\nreturn render(request, 'books\/search_results.html', {'books': books, 'query': q})\nreturn render(request, 'books\/search_form.html', {'error': error})\n\n\n<\/pre>\n<p class=\"VSKILLbodytext\"><span lang=\"EN-US\">Note that I\u2019ve included search_form() here so you can see both views in one place. Here, we\u2019ve improved search() to render the search_form.html template again, if the query is empty. And because we need to display an error message in that template, we pass a template variable. Now we can edit search_form.html to check for the error variable:<\/span><\/p>\n<pre>&lt;html&gt;\n&lt;head&gt;\n&lt;title&gt;Search&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n{% if error %}\n&lt;p style=\"color: red;\"&gt;Please submit a search term.&lt;\/p&gt;\n{% endif %}\n&lt;form action=\"\/search\/\" method=\"get\"&gt;\n&lt;input type=\"text\" name=\"q\"&gt;\n&lt;input type=\"submit\" value=\"Search\"&gt;\n&lt;\/form&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p>We can still use this template from our original view, search_form(), because search_form() doesn\u2019t pass error to the template \u2013 so the error message won\u2019t show up in that case (Figure 6-4).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128601 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/29.jpg\" alt=\"\" width=\"1033\" height=\"274\"><\/p>\n<p>With this change in place, it\u2019s a better application, but it now begs the question: is a dedicated search_form() view really necessary? As it stands, a request to the URL \/search\/ (without any GET parameters) will display the empty form (but with an error). We can remove the search_form() view, along with its associated URLpattern, as long as we change search() to hide the error message when somebody visits \/search\/ with no GET parameters:<\/p>\n<p>def search(request):<br>\nerror = False<br>\nif 'q' in request.GET:<br>\nq = request.GET['q']\nif not q:<br>\nerror = True<br>\nelse:<br>\nbooks = Book.objects.filter(title__icontains=q)<br>\nreturn render(request, 'books\/search_results.html', {'books': books, 'query': q})<br>\nreturn render(request, 'books\/search_form.html', {'error': error})<\/p>\n<p>In this updated view, if a user visits \/search\/ with no GET parameters, they\u2019ll see the search form with no error message. If a user submits the form with an empty value for 'q', they\u2019ll see the search form with an error message. And, finally, if a user submits the form with a non-empty value for 'q', they\u2019ll see the search results.<\/p>\n<p>We can make one final improvement to this application, to remove a bit of redundancy. Now that we\u2019ve rolled the two views and URLs into one and \/search\/ handles both search-form display and result display, the HTML &lt;form&gt; in search_form.html doesn\u2019t have to hard-code a URL. Instead of this:<\/p>\n<pre>&lt;form action=\"\/search\/\" method=\"get\"&gt;<\/pre>\n<p>It can be changed to this:<\/p>\n<pre>&lt;form action=\"\" method=\"get\"&gt;<\/pre>\n<p>The action=\"\" means \u201cSubmit the form to the same URL as the current page.\u201d With this change in place, you won\u2019t have to remember to change the action if you ever hook the search() view to another URL.<\/p>\n, views.search_form),\n]<\/pre>\n<p>(Note that we\u2019re importing the views module directly, instead of something like from books.views import search_form, because the former is less verbose.<\/p>\n<p>One last thing \u2013 when Django searches for URL patterns, it will only search the base mysite\\urls.py file, unless we explicitly include the URL patterns from other apps. So let\u2019s go ahead and modify our site urlpatterns:<\/p>\n<pre wp-pre-tag-10=\"\"><\/pre>\n<p>This new URL pattern must be added to the end of the urlpatterns list. This is because the r'^' regex sends everything to books.urls, so we want to make sure none of the other patterns match, before sending Django to check books\\urls.py for a matching pattern.<\/p>\n<p>Now, if you run the development server and visit http:\/\/127.0.0.1:8000\/search-form\/, you\u2019ll see the search interface (Figure 6.1). Simple enough.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128604 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/25-1.jpg\" alt=\"\" width=\"1033\" height=\"223\"><\/p>\n<p>Try submitting the form, though, and you\u2019ll get a Django 404 error. The form points to the URL \/search\/, which hasn\u2019t yet been implemented. Let\u2019s fix that with a second view function:<\/p>\n<pre wp-pre-tag-11=\"\"><\/pre>\n<ul>\n<li>The HTML &lt;form&gt; defines a variable q. When it\u2019s submitted, the value of q is sent via GET (method=\"get\") to the URL \/search\/.<\/li>\n<li>The Django view that handles the URL \/search\/ (search()) has access to the q value in request.GET.An important thing to point out here is that we explicitly check that 'q' exists in request.GET. As I pointed out in the request.META section above, you shouldn\u2019t trust anything submitted by users or even assume that they\u2019ve submitted anything in the first place. If we didn\u2019t add this check, any submission of an empty form would raise KeyError in the view:<\/li>\n<\/ul>\n<pre wp-pre-tag-12=\"\"><\/pre>\n<p><strong>Query String Parameters<\/strong> - Because GET data is passed in the query string (e.g., \/search\/?q=django), you can use request.GET to access query string variables. In introduction of Django\u2019s URLconf system, I compared Django\u2019s pretty URLs to more traditional PHP\/Java URLs such as \/time\/plus?hours=3 and said I\u2019d show you how to do the latter.<\/p>\n<p>Now you know how to access query string parameters in your views (like hours=3 in this example) \u2013 use request.GET. POST data works the same way as GET data \u2013 just use request.POST instead of request.GET. What\u2019s the difference between GET and POST?<\/p>\n<p>Use GET when the act of submitting the form is just a request to \u201cget\u201d data. Use POST whenever the act of submitting the form will have some side effect \u2013 changing data, or sending an e-mail, or something else that\u2019s beyond simple display of data. In our book search example, we\u2019re using GET because the query doesn\u2019t change any data on our server.<\/p>\n<p>Now that we\u2019ve verified request.GET is being passed in properly, let\u2019s hook the user\u2019s search query into our book database (again, in views.py):<\/p>\n<pre wp-pre-tag-13=\"\"><\/pre>\n<p>A couple of notes on what we did here:<\/p>\n<ul>\n<li>&nbsp;Aside from checking that 'q' exists in request.GET, we also make sure that request.GET['q'] is a non-empty value before passing it to the database query.<\/li>\n<li>We\u2019re using Book.objects.filter(title__icontains=q) to query our book table for all books whose title includes the given submission. The icontains is a lookup type, and the statement can be roughly translated as \u201cGet the books whose title contains q, without being case-sensitive.\u201d<\/li>\n<\/ul>\n<p>This is a very simple way to do a book search. I wouldn\u2019t recommend using a simple icontains query on a large production database, as it can be slow. (In the real world, you\u2019d want to use a custom search system of some sort. Search the web for open-source full-text search to get an idea of the possibilities.)<\/p>\n<p>We pass books, a list of Book objects, to the template. To get our new search form working, let\u2019s create the search_results.html file:<\/p>\n<pre wp-pre-tag-14=\"\"><\/pre>\n<p>Note usage of the pluralize template filter, which outputs an \u201cs\u201d if appropriate, based on the number of books found.<\/p>\n<p>Now, when you run the development server and visit http:\/\/127.0.0.1:8000\/search-form\/, your search term should return a more useful result below figure.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128600 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/27.jpg\" alt=\"\" width=\"1033\" height=\"283\"><\/p>\n<p><strong>Improving Our Simple Form-Handling Example<\/strong> - As in previous chapters, I\u2019ve shown you the simplest thing that could possibly work. Now I\u2019ll point out some problems and show you how to improve it. First, our search() view\u2019s handling of an empty query is poor \u2013 we\u2019re just displaying a \u201cPlease submit a search term.\u201d message, requiring the user to hit the browser\u2019s back button. This is horrid and unprofessional, and if you ever actually implement something like this in the wild, your Django privileges will be revoked.<\/p>\n<p>It would be much better to redisplay the form, with an error above it, so that the user can try again immediately. The easiest way to do that would be to render the template again, like this:<\/p>\n<pre wp-pre-tag-15=\"\"><\/pre>\n<p class=\"VSKILLbodytext\"><span lang=\"EN-US\">Note that I\u2019ve included search_form() here so you can see both views in one place. Here, we\u2019ve improved search() to render the search_form.html template again, if the query is empty. And because we need to display an error message in that template, we pass a template variable. Now we can edit search_form.html to check for the error variable:<\/span><\/p>\n<pre wp-pre-tag-16=\"\"><\/pre>\n<p>We can still use this template from our original view, search_form(), because search_form() doesn\u2019t pass error to the template \u2013 so the error message won\u2019t show up in that case (Figure 6-4).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128601 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/29.jpg\" alt=\"\" width=\"1033\" height=\"274\"><\/p>\n<p>With this change in place, it\u2019s a better application, but it now begs the question: is a dedicated search_form() view really necessary? As it stands, a request to the URL \/search\/ (without any GET parameters) will display the empty form (but with an error). We can remove the search_form() view, along with its associated URLpattern, as long as we change search() to hide the error message when somebody visits \/search\/ with no GET parameters:<\/p>\n<p>def search(request):<br>\nerror = False<br>\nif 'q' in request.GET:<br>\nq = request.GET['q']\nif not q:<br>\nerror = True<br>\nelse:<br>\nbooks = Book.objects.filter(title__icontains=q)<br>\nreturn render(request, 'books\/search_results.html', {'books': books, 'query': q})<br>\nreturn render(request, 'books\/search_form.html', {'error': error})<\/p>\n<p>In this updated view, if a user visits \/search\/ with no GET parameters, they\u2019ll see the search form with no error message. If a user submits the form with an empty value for 'q', they\u2019ll see the search form with an error message. And, finally, if a user submits the form with a non-empty value for 'q', they\u2019ll see the search results.<\/p>\n<p>We can make one final improvement to this application, to remove a bit of redundancy. Now that we\u2019ve rolled the two views and URLs into one and \/search\/ handles both search-form display and result display, the HTML &lt;form&gt; in search_form.html doesn\u2019t have to hard-code a URL. Instead of this:<\/p>\n<pre wp-pre-tag-17=\"\"><\/pre>\n<p>It can be changed to this:<\/p>\n<pre wp-pre-tag-18=\"\"><\/pre>\n<p>The action=\"\" means \u201cSubmit the form to the same URL as the current page.\u201d With this change in place, you won\u2019t have to remember to change the action if you ever hook the search() view to another URL.<\/p>\n, views.search_form),\nurl(r'^search\/\n<ul>\n<li>The HTML &lt;form&gt; defines a variable q. When it\u2019s submitted, the value of q is sent via GET (method=\"get\") to the URL \/search\/.<\/li>\n<li>The Django view that handles the URL \/search\/ (search()) has access to the q value in request.GET.An important thing to point out here is that we explicitly check that 'q' exists in request.GET. As I pointed out in the request.META section above, you shouldn\u2019t trust anything submitted by users or even assume that they\u2019ve submitted anything in the first place. If we didn\u2019t add this check, any submission of an empty form would raise KeyError in the view:<\/li>\n<\/ul>\n<pre wp-pre-tag-12=\"\"><\/pre>\n<p><strong>Query String Parameters<\/strong> - Because GET data is passed in the query string (e.g., \/search\/?q=django), you can use request.GET to access query string variables. In introduction of Django\u2019s URLconf system, I compared Django\u2019s pretty URLs to more traditional PHP\/Java URLs such as \/time\/plus?hours=3 and said I\u2019d show you how to do the latter.<\/p>\n<p>Now you know how to access query string parameters in your views (like hours=3 in this example) \u2013 use request.GET. POST data works the same way as GET data \u2013 just use request.POST instead of request.GET. What\u2019s the difference between GET and POST?<\/p>\n<p>Use GET when the act of submitting the form is just a request to \u201cget\u201d data. Use POST whenever the act of submitting the form will have some side effect \u2013 changing data, or sending an e-mail, or something else that\u2019s beyond simple display of data. In our book search example, we\u2019re using GET because the query doesn\u2019t change any data on our server.<\/p>\n<p>Now that we\u2019ve verified request.GET is being passed in properly, let\u2019s hook the user\u2019s search query into our book database (again, in views.py):<\/p>\n<pre wp-pre-tag-13=\"\"><\/pre>\n<p>A couple of notes on what we did here:<\/p>\n<ul>\n<li>&nbsp;Aside from checking that 'q' exists in request.GET, we also make sure that request.GET['q'] is a non-empty value before passing it to the database query.<\/li>\n<li>We\u2019re using Book.objects.filter(title__icontains=q) to query our book table for all books whose title includes the given submission. The icontains is a lookup type, and the statement can be roughly translated as \u201cGet the books whose title contains q, without being case-sensitive.\u201d<\/li>\n<\/ul>\n<p>This is a very simple way to do a book search. I wouldn\u2019t recommend using a simple icontains query on a large production database, as it can be slow. (In the real world, you\u2019d want to use a custom search system of some sort. Search the web for open-source full-text search to get an idea of the possibilities.)<\/p>\n<p>We pass books, a list of Book objects, to the template. To get our new search form working, let\u2019s create the search_results.html file:<\/p>\n<pre wp-pre-tag-14=\"\"><\/pre>\n<p>Note usage of the pluralize template filter, which outputs an \u201cs\u201d if appropriate, based on the number of books found.<\/p>\n<p>Now, when you run the development server and visit http:\/\/127.0.0.1:8000\/search-form\/, your search term should return a more useful result below figure.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128600 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/27.jpg\" alt=\"\" width=\"1033\" height=\"283\"><\/p>\n<p><strong>Improving Our Simple Form-Handling Example<\/strong> - As in previous chapters, I\u2019ve shown you the simplest thing that could possibly work. Now I\u2019ll point out some problems and show you how to improve it. First, our search() view\u2019s handling of an empty query is poor \u2013 we\u2019re just displaying a \u201cPlease submit a search term.\u201d message, requiring the user to hit the browser\u2019s back button. This is horrid and unprofessional, and if you ever actually implement something like this in the wild, your Django privileges will be revoked.<\/p>\n<p>It would be much better to redisplay the form, with an error above it, so that the user can try again immediately. The easiest way to do that would be to render the template again, like this:<\/p>\n<pre wp-pre-tag-15=\"\"><\/pre>\n<p class=\"VSKILLbodytext\"><span lang=\"EN-US\">Note that I\u2019ve included search_form() here so you can see both views in one place. Here, we\u2019ve improved search() to render the search_form.html template again, if the query is empty. And because we need to display an error message in that template, we pass a template variable. Now we can edit search_form.html to check for the error variable:<\/span><\/p>\n<pre wp-pre-tag-16=\"\"><\/pre>\n<p>We can still use this template from our original view, search_form(), because search_form() doesn\u2019t pass error to the template \u2013 so the error message won\u2019t show up in that case (Figure 6-4).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128601 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/29.jpg\" alt=\"\" width=\"1033\" height=\"274\"><\/p>\n<p>With this change in place, it\u2019s a better application, but it now begs the question: is a dedicated search_form() view really necessary? As it stands, a request to the URL \/search\/ (without any GET parameters) will display the empty form (but with an error). We can remove the search_form() view, along with its associated URLpattern, as long as we change search() to hide the error message when somebody visits \/search\/ with no GET parameters:<\/p>\n<p>def search(request):<br>\nerror = False<br>\nif 'q' in request.GET:<br>\nq = request.GET['q']\nif not q:<br>\nerror = True<br>\nelse:<br>\nbooks = Book.objects.filter(title__icontains=q)<br>\nreturn render(request, 'books\/search_results.html', {'books': books, 'query': q})<br>\nreturn render(request, 'books\/search_form.html', {'error': error})<\/p>\n<p>In this updated view, if a user visits \/search\/ with no GET parameters, they\u2019ll see the search form with no error message. If a user submits the form with an empty value for 'q', they\u2019ll see the search form with an error message. And, finally, if a user submits the form with a non-empty value for 'q', they\u2019ll see the search results.<\/p>\n<p>We can make one final improvement to this application, to remove a bit of redundancy. Now that we\u2019ve rolled the two views and URLs into one and \/search\/ handles both search-form display and result display, the HTML &lt;form&gt; in search_form.html doesn\u2019t have to hard-code a URL. Instead of this:<\/p>\n<pre wp-pre-tag-17=\"\"><\/pre>\n<p>It can be changed to this:<\/p>\n<pre wp-pre-tag-18=\"\"><\/pre>\n<p>The action=\"\" means \u201cSubmit the form to the same URL as the current page.\u201d With this change in place, you won\u2019t have to remember to change the action if you ever hook the search() view to another URL.<\/p>\n, views.search_form),\n]<\/pre>\n<p>(Note that we\u2019re importing the views module directly, instead of something like from books.views import search_form, because the former is less verbose.<\/p>\n<p>One last thing \u2013 when Django searches for URL patterns, it will only search the base mysite\\urls.py file, unless we explicitly include the URL patterns from other apps. So let\u2019s go ahead and modify our site urlpatterns:<\/p>\n<pre wp-pre-tag-10=\"\"><\/pre>\n<p>This new URL pattern must be added to the end of the urlpatterns list. This is because the r&#8217;^&#8217; regex sends everything to books.urls, so we want to make sure none of the other patterns match, before sending Django to check books\\urls.py for a matching pattern.<\/p>\n<p>Now, if you run the development server and visit http:\/\/127.0.0.1:8000\/search-form\/, you\u2019ll see the search interface (Figure 6.1). Simple enough.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128604 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/25-1.jpg\" alt=\"\" width=\"1033\" height=\"223\"><\/p>\n<p>Try submitting the form, though, and you\u2019ll get a Django 404 error. The form points to the URL \/search\/, which hasn\u2019t yet been implemented. Let\u2019s fix that with a second view function:<\/p>\n<pre wp-pre-tag-11=\"\"><\/pre>\n<ul>\n<li>The HTML &lt;form&gt; defines a variable q. When it\u2019s submitted, the value of q is sent via GET (method=&#8221;get&#8221;) to the URL \/search\/.<\/li>\n<li>The Django view that handles the URL \/search\/ (search()) has access to the q value in request.GET.An important thing to point out here is that we explicitly check that &#8216;q&#8217; exists in request.GET. As I pointed out in the request.META section above, you shouldn\u2019t trust anything submitted by users or even assume that they\u2019ve submitted anything in the first place. If we didn\u2019t add this check, any submission of an empty form would raise KeyError in the view:<\/li>\n<\/ul>\n<pre wp-pre-tag-12=\"\"><\/pre>\n<p><strong>Query String Parameters<\/strong> &#8211; Because GET data is passed in the query string (e.g., \/search\/?q=django), you can use request.GET to access query string variables. In introduction of Django\u2019s URLconf system, I compared Django\u2019s pretty URLs to more traditional PHP\/Java URLs such as \/time\/plus?hours=3 and said I\u2019d show you how to do the latter.<\/p>\n<p>Now you know how to access query string parameters in your views (like hours=3 in this example) \u2013 use request.GET. POST data works the same way as GET data \u2013 just use request.POST instead of request.GET. What\u2019s the difference between GET and POST?<\/p>\n<p>Use GET when the act of submitting the form is just a request to \u201cget\u201d data. Use POST whenever the act of submitting the form will have some side effect \u2013 changing data, or sending an e-mail, or something else that\u2019s beyond simple display of data. In our book search example, we\u2019re using GET because the query doesn\u2019t change any data on our server.<\/p>\n<p>Now that we\u2019ve verified request.GET is being passed in properly, let\u2019s hook the user\u2019s search query into our book database (again, in views.py):<\/p>\n<pre wp-pre-tag-13=\"\"><\/pre>\n<p>A couple of notes on what we did here:<\/p>\n<ul>\n<li>&nbsp;Aside from checking that &#8216;q&#8217; exists in request.GET, we also make sure that request.GET[&#8216;q&#8217;] is a non-empty value before passing it to the database query.<\/li>\n<li>We\u2019re using Book.objects.filter(title__icontains=q) to query our book table for all books whose title includes the given submission. The icontains is a lookup type, and the statement can be roughly translated as \u201cGet the books whose title contains q, without being case-sensitive.\u201d<\/li>\n<\/ul>\n<p>This is a very simple way to do a book search. I wouldn\u2019t recommend using a simple icontains query on a large production database, as it can be slow. (In the real world, you\u2019d want to use a custom search system of some sort. Search the web for open-source full-text search to get an idea of the possibilities.)<\/p>\n<p>We pass books, a list of Book objects, to the template. To get our new search form working, let\u2019s create the search_results.html file:<\/p>\n<pre wp-pre-tag-14=\"\"><\/pre>\n<p>Note usage of the pluralize template filter, which outputs an \u201cs\u201d if appropriate, based on the number of books found.<\/p>\n<p>Now, when you run the development server and visit http:\/\/127.0.0.1:8000\/search-form\/, your search term should return a more useful result below figure.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128600 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/27.jpg\" alt=\"\" width=\"1033\" height=\"283\"><\/p>\n<p><strong>Improving Our Simple Form-Handling Example<\/strong> &#8211; As in previous chapters, I\u2019ve shown you the simplest thing that could possibly work. Now I\u2019ll point out some problems and show you how to improve it. First, our search() view\u2019s handling of an empty query is poor \u2013 we\u2019re just displaying a \u201cPlease submit a search term.\u201d message, requiring the user to hit the browser\u2019s back button. This is horrid and unprofessional, and if you ever actually implement something like this in the wild, your Django privileges will be revoked.<\/p>\n<p>It would be much better to redisplay the form, with an error above it, so that the user can try again immediately. The easiest way to do that would be to render the template again, like this:<\/p>\n<pre wp-pre-tag-15=\"\"><\/pre>\n<p class=\"VSKILLbodytext\"><span lang=\"EN-US\">Note that I\u2019ve included search_form() here so you can see both views in one place. Here, we\u2019ve improved search() to render the search_form.html template again, if the query is empty. And because we need to display an error message in that template, we pass a template variable. Now we can edit search_form.html to check for the error variable:<\/span><\/p>\n<pre wp-pre-tag-16=\"\"><\/pre>\n<p>We can still use this template from our original view, search_form(), because search_form() doesn\u2019t pass error to the template \u2013 so the error message won\u2019t show up in that case (Figure 6-4).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128601 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/29.jpg\" alt=\"\" width=\"1033\" height=\"274\"><\/p>\n<p>With this change in place, it\u2019s a better application, but it now begs the question: is a dedicated search_form() view really necessary? As it stands, a request to the URL \/search\/ (without any GET parameters) will display the empty form (but with an error). We can remove the search_form() view, along with its associated URLpattern, as long as we change search() to hide the error message when somebody visits \/search\/ with no GET parameters:<\/p>\n<p>def search(request):<br>\nerror = False<br>\nif &#8216;q&#8217; in request.GET:<br>\nq = request.GET[&#8216;q&#8217;]\nif not q:<br>\nerror = True<br>\nelse:<br>\nbooks = Book.objects.filter(title__icontains=q)<br>\nreturn render(request, &#8216;books\/search_results.html&#8217;, {&#8216;books&#8217;: books, &#8216;query&#8217;: q})<br>\nreturn render(request, &#8216;books\/search_form.html&#8217;, {&#8216;error&#8217;: error})<\/p>\n<p>In this updated view, if a user visits \/search\/ with no GET parameters, they\u2019ll see the search form with no error message. If a user submits the form with an empty value for &#8216;q&#8217;, they\u2019ll see the search form with an error message. And, finally, if a user submits the form with a non-empty value for &#8216;q&#8217;, they\u2019ll see the search results.<\/p>\n<p>We can make one final improvement to this application, to remove a bit of redundancy. Now that we\u2019ve rolled the two views and URLs into one and \/search\/ handles both search-form display and result display, the HTML &lt;form&gt; in search_form.html doesn\u2019t have to hard-code a URL. Instead of this:<\/p>\n<pre wp-pre-tag-17=\"\"><\/pre>\n<p>It can be changed to this:<\/p>\n<pre wp-pre-tag-18=\"\"><\/pre>\n<p>The action=&#8221;&#8221; means \u201cSubmit the form to the same URL as the current page.\u201d With this change in place, you won\u2019t have to remember to change the action if you ever hook the search() view to another URL.<\/p>\n, views.search),\n]\n\n# books\/views.py\n\nfrom django.http import HttpResponse\n\n# &#8230;\n\ndef search(request):\nif &#8216;q&#8217; in request.GET:\nmessage = &#8216;You searched for: %r&#8217; % request.GET[&#8216;q&#8217;]\nelse:\nmessage = &#8216;You submitted an empty form.&#8217;\nreturn HttpResponse(message)\n\n<span lang=\"EN-US\">For the moment, this merely displays the user\u2019s search term, so we can make sure the data is being submitted to\n Django properly, and so you can get a feel for how the search term flows through the system\n (Figure 6-2).\n\n <img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128602 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/30.jpg\" alt=\"\" width=\"1033\" height=\"250\">\nin short<\/span>\n<ul>\n<li>The HTML &lt;form&gt; defines a variable q. When it\u2019s submitted, the value of q is sent via GET (method=&#8221;get&#8221;) to the URL \/search\/.<\/li>\n<li>The Django view that handles the URL \/search\/ (search()) has access to the q value in request.GET.An important thing to point out here is that we explicitly check that &#8216;q&#8217; exists in request.GET. As I pointed out in the request.META section above, you shouldn\u2019t trust anything submitted by users or even assume that they\u2019ve submitted anything in the first place. If we didn\u2019t add this check, any submission of an empty form would raise KeyError in the view:<\/li>\n<\/ul>\n<pre wp-pre-tag-12=\"\"><\/pre>\n<p><strong>Query String Parameters<\/strong> &#8211; Because GET data is passed in the query string (e.g., \/search\/?q=django), you can use request.GET to access query string variables. In introduction of Django\u2019s URLconf system, I compared Django\u2019s pretty URLs to more traditional PHP\/Java URLs such as \/time\/plus?hours=3 and said I\u2019d show you how to do the latter.<\/p>\n<p>Now you know how to access query string parameters in your views (like hours=3 in this example) \u2013 use request.GET. POST data works the same way as GET data \u2013 just use request.POST instead of request.GET. What\u2019s the difference between GET and POST?<\/p>\n<p>Use GET when the act of submitting the form is just a request to \u201cget\u201d data. Use POST whenever the act of submitting the form will have some side effect \u2013 changing data, or sending an e-mail, or something else that\u2019s beyond simple display of data. In our book search example, we\u2019re using GET because the query doesn\u2019t change any data on our server.<\/p>\n<p>Now that we\u2019ve verified request.GET is being passed in properly, let\u2019s hook the user\u2019s search query into our book database (again, in views.py):<\/p>\n<pre wp-pre-tag-13=\"\"><\/pre>\n<p>A couple of notes on what we did here:<\/p>\n<ul>\n<li>&nbsp;Aside from checking that &#8216;q&#8217; exists in request.GET, we also make sure that request.GET[&#8216;q&#8217;] is a non-empty value before passing it to the database query.<\/li>\n<li>We\u2019re using Book.objects.filter(title__icontains=q) to query our book table for all books whose title includes the given submission. The icontains is a lookup type, and the statement can be roughly translated as \u201cGet the books whose title contains q, without being case-sensitive.\u201d<\/li>\n<\/ul>\n<p>This is a very simple way to do a book search. I wouldn\u2019t recommend using a simple icontains query on a large production database, as it can be slow. (In the real world, you\u2019d want to use a custom search system of some sort. Search the web for open-source full-text search to get an idea of the possibilities.)<\/p>\n<p>We pass books, a list of Book objects, to the template. To get our new search form working, let\u2019s create the search_results.html file:<\/p>\n<pre wp-pre-tag-14=\"\"><\/pre>\n<p>Note usage of the pluralize template filter, which outputs an \u201cs\u201d if appropriate, based on the number of books found.<\/p>\n<p>Now, when you run the development server and visit http:\/\/127.0.0.1:8000\/search-form\/, your search term should return a more useful result below figure.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128600 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/27.jpg\" alt=\"\" width=\"1033\" height=\"283\"><\/p>\n<p><strong>Improving Our Simple Form-Handling Example<\/strong> &#8211; As in previous chapters, I\u2019ve shown you the simplest thing that could possibly work. Now I\u2019ll point out some problems and show you how to improve it. First, our search() view\u2019s handling of an empty query is poor \u2013 we\u2019re just displaying a \u201cPlease submit a search term.\u201d message, requiring the user to hit the browser\u2019s back button. This is horrid and unprofessional, and if you ever actually implement something like this in the wild, your Django privileges will be revoked.<\/p>\n<p>It would be much better to redisplay the form, with an error above it, so that the user can try again immediately. The easiest way to do that would be to render the template again, like this:<\/p>\n<pre wp-pre-tag-15=\"\"><\/pre>\n<p class=\"VSKILLbodytext\"><span lang=\"EN-US\">Note that I\u2019ve included search_form() here so you can see both views in one place. Here, we\u2019ve improved search() to render the search_form.html template again, if the query is empty. And because we need to display an error message in that template, we pass a template variable. Now we can edit search_form.html to check for the error variable:<\/span><\/p>\n<pre wp-pre-tag-16=\"\"><\/pre>\n<p>We can still use this template from our original view, search_form(), because search_form() doesn\u2019t pass error to the template \u2013 so the error message won\u2019t show up in that case (Figure 6-4).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128601 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/29.jpg\" alt=\"\" width=\"1033\" height=\"274\"><\/p>\n<p>With this change in place, it\u2019s a better application, but it now begs the question: is a dedicated search_form() view really necessary? As it stands, a request to the URL \/search\/ (without any GET parameters) will display the empty form (but with an error). We can remove the search_form() view, along with its associated URLpattern, as long as we change search() to hide the error message when somebody visits \/search\/ with no GET parameters:<\/p>\n<p>def search(request):<br>\nerror = False<br>\nif &#8216;q&#8217; in request.GET:<br>\nq = request.GET[&#8216;q&#8217;]\nif not q:<br>\nerror = True<br>\nelse:<br>\nbooks = Book.objects.filter(title__icontains=q)<br>\nreturn render(request, &#8216;books\/search_results.html&#8217;, {&#8216;books&#8217;: books, &#8216;query&#8217;: q})<br>\nreturn render(request, &#8216;books\/search_form.html&#8217;, {&#8216;error&#8217;: error})<\/p>\n<p>In this updated view, if a user visits \/search\/ with no GET parameters, they\u2019ll see the search form with no error message. If a user submits the form with an empty value for &#8216;q&#8217;, they\u2019ll see the search form with an error message. And, finally, if a user submits the form with a non-empty value for &#8216;q&#8217;, they\u2019ll see the search results.<\/p>\n<p>We can make one final improvement to this application, to remove a bit of redundancy. Now that we\u2019ve rolled the two views and URLs into one and \/search\/ handles both search-form display and result display, the HTML &lt;form&gt; in search_form.html doesn\u2019t have to hard-code a URL. Instead of this:<\/p>\n<pre wp-pre-tag-17=\"\"><\/pre>\n<p>It can be changed to this:<\/p>\n<pre wp-pre-tag-18=\"\"><\/pre>\n<p>The action=&#8221;&#8221; means \u201cSubmit the form to the same URL as the current page.\u201d With this change in place, you won\u2019t have to remember to change the action if you ever hook the search() view to another URL.<\/p>\n, views.search_form),\n]\n<p>(Note that we\u2019re importing the views module directly, instead of something like from books.views import search_form, because the former is less verbose.<\/p>\n<p>One last thing \u2013 when Django searches for URL patterns, it will only search the base mysite\\urls.py file, unless we explicitly include the URL patterns from other apps. So let\u2019s go ahead and modify our site urlpatterns:<\/p>\n<pre wp-pre-tag-10=\"\"><\/pre>\n<p>This new URL pattern must be added to the end of the urlpatterns list. This is because the r&#8217;^&#8217; regex sends everything to books.urls, so we want to make sure none of the other patterns match, before sending Django to check books\\urls.py for a matching pattern.<\/p>\n<p>Now, if you run the development server and visit http:\/\/127.0.0.1:8000\/search-form\/, you\u2019ll see the search interface (Figure 6.1). Simple enough.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128604 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/25-1.jpg\" alt=\"\" width=\"1033\" height=\"223\"><\/p>\n<p>Try submitting the form, though, and you\u2019ll get a Django 404 error. The form points to the URL \/search\/, which hasn\u2019t yet been implemented. Let\u2019s fix that with a second view function:<\/p>\n<pre wp-pre-tag-11=\"\"><\/pre>\n<ul>\n<li>The HTML &lt;form&gt; defines a variable q. When it\u2019s submitted, the value of q is sent via GET (method=&#8221;get&#8221;) to the URL \/search\/.<\/li>\n<li>The Django view that handles the URL \/search\/ (search()) has access to the q value in request.GET.An important thing to point out here is that we explicitly check that &#8216;q&#8217; exists in request.GET. As I pointed out in the request.META section above, you shouldn\u2019t trust anything submitted by users or even assume that they\u2019ve submitted anything in the first place. If we didn\u2019t add this check, any submission of an empty form would raise KeyError in the view:<\/li>\n<\/ul>\n<pre wp-pre-tag-12=\"\"><\/pre>\n<p><strong>Query String Parameters<\/strong> &#8211; Because GET data is passed in the query string (e.g., \/search\/?q=django), you can use request.GET to access query string variables. In introduction of Django\u2019s URLconf system, I compared Django\u2019s pretty URLs to more traditional PHP\/Java URLs such as \/time\/plus?hours=3 and said I\u2019d show you how to do the latter.<\/p>\n<p>Now you know how to access query string parameters in your views (like hours=3 in this example) \u2013 use request.GET. POST data works the same way as GET data \u2013 just use request.POST instead of request.GET. What\u2019s the difference between GET and POST?<\/p>\n<p>Use GET when the act of submitting the form is just a request to \u201cget\u201d data. Use POST whenever the act of submitting the form will have some side effect \u2013 changing data, or sending an e-mail, or something else that\u2019s beyond simple display of data. In our book search example, we\u2019re using GET because the query doesn\u2019t change any data on our server.<\/p>\n<p>Now that we\u2019ve verified request.GET is being passed in properly, let\u2019s hook the user\u2019s search query into our book database (again, in views.py):<\/p>\n<pre wp-pre-tag-13=\"\"><\/pre>\n<p>A couple of notes on what we did here:<\/p>\n<ul>\n<li>&nbsp;Aside from checking that &#8216;q&#8217; exists in request.GET, we also make sure that request.GET[&#8216;q&#8217;] is a non-empty value before passing it to the database query.<\/li>\n<li>We\u2019re using Book.objects.filter(title__icontains=q) to query our book table for all books whose title includes the given submission. The icontains is a lookup type, and the statement can be roughly translated as \u201cGet the books whose title contains q, without being case-sensitive.\u201d<\/li>\n<\/ul>\n<p>This is a very simple way to do a book search. I wouldn\u2019t recommend using a simple icontains query on a large production database, as it can be slow. (In the real world, you\u2019d want to use a custom search system of some sort. Search the web for open-source full-text search to get an idea of the possibilities.)<\/p>\n<p>We pass books, a list of Book objects, to the template. To get our new search form working, let\u2019s create the search_results.html file:<\/p>\n<pre wp-pre-tag-14=\"\"><\/pre>\n<p>Note usage of the pluralize template filter, which outputs an \u201cs\u201d if appropriate, based on the number of books found.<\/p>\n<p>Now, when you run the development server and visit http:\/\/127.0.0.1:8000\/search-form\/, your search term should return a more useful result below figure.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128600 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/27.jpg\" alt=\"\" width=\"1033\" height=\"283\"><\/p>\n<p><strong>Improving Our Simple Form-Handling Example<\/strong> &#8211; As in previous chapters, I\u2019ve shown you the simplest thing that could possibly work. Now I\u2019ll point out some problems and show you how to improve it. First, our search() view\u2019s handling of an empty query is poor \u2013 we\u2019re just displaying a \u201cPlease submit a search term.\u201d message, requiring the user to hit the browser\u2019s back button. This is horrid and unprofessional, and if you ever actually implement something like this in the wild, your Django privileges will be revoked.<\/p>\n<p>It would be much better to redisplay the form, with an error above it, so that the user can try again immediately. The easiest way to do that would be to render the template again, like this:<\/p>\n<pre wp-pre-tag-15=\"\"><\/pre>\n<p class=\"VSKILLbodytext\"><span lang=\"EN-US\">Note that I\u2019ve included search_form() here so you can see both views in one place. Here, we\u2019ve improved search() to render the search_form.html template again, if the query is empty. And because we need to display an error message in that template, we pass a template variable. Now we can edit search_form.html to check for the error variable:<\/span><\/p>\n<pre wp-pre-tag-16=\"\"><\/pre>\n<p>We can still use this template from our original view, search_form(), because search_form() doesn\u2019t pass error to the template \u2013 so the error message won\u2019t show up in that case (Figure 6-4).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-128601 size-full\" src=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/29.jpg\" alt=\"\" width=\"1033\" height=\"274\"><\/p>\n<p>With this change in place, it\u2019s a better application, but it now begs the question: is a dedicated search_form() view really necessary? As it stands, a request to the URL \/search\/ (without any GET parameters) will display the empty form (but with an error). We can remove the search_form() view, along with its associated URLpattern, as long as we change search() to hide the error message when somebody visits \/search\/ with no GET parameters:<\/p>\n<p>def search(request):<br>\nerror = False<br>\nif &#8216;q&#8217; in request.GET:<br>\nq = request.GET[&#8216;q&#8217;]\nif not q:<br>\nerror = True<br>\nelse:<br>\nbooks = Book.objects.filter(title__icontains=q)<br>\nreturn render(request, &#8216;books\/search_results.html&#8217;, {&#8216;books&#8217;: books, &#8216;query&#8217;: q})<br>\nreturn render(request, &#8216;books\/search_form.html&#8217;, {&#8216;error&#8217;: error})<\/p>\n<p>In this updated view, if a user visits \/search\/ with no GET parameters, they\u2019ll see the search form with no error message. If a user submits the form with an empty value for &#8216;q&#8217;, they\u2019ll see the search form with an error message. And, finally, if a user submits the form with a non-empty value for &#8216;q&#8217;, they\u2019ll see the search results.<\/p>\n<p>We can make one final improvement to this application, to remove a bit of redundancy. Now that we\u2019ve rolled the two views and URLs into one and \/search\/ handles both search-form display and result display, the HTML &lt;form&gt; in search_form.html doesn\u2019t have to hard-code a URL. Instead of this:<\/p>\n<pre wp-pre-tag-17=\"\"><\/pre>\n<p>It can be changed to this:<\/p>\n<pre wp-pre-tag-18=\"\"><\/pre>\n<p>The action=&#8221;&#8221; means \u201cSubmit the form to the same URL as the current page.\u201d With this change in place, you won\u2019t have to remember to change the action if you ever hook the search() view to another URL.<\/p>\n\n\n<p><a href=\"https:\/\/www.vskills.in\/certification\/tutorial\/certified-django-developer\/\" target=\"_blank\" rel=\"noreferrer noopener\">Back to Tutorial<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Web is all about search. Two of the Net\u2019s biggest success stories, Google and Yahoo, built their multi-billion-dollar businesses around search. Nearly every site sees a large percentage of traffic coming to and from its search pages. Often the difference between the success or failure of a site is the quality of its search&#8230;.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"categories":[8655],"tags":[8768],"class_list":["post-75670","page","type-page","status-publish","hentry","category-django-web-development","tag-search"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Search - Tutorial<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.vskills.in\/certification\/tutorial\/search\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Search - Tutorial\" \/>\n<meta property=\"og:description\" content=\"The Web is all about search. Two of the Net\u2019s biggest success stories, Google and Yahoo, built their multi-billion-dollar businesses around search. Nearly every site sees a large percentage of traffic coming to and from its search pages. Often the difference between the success or failure of a site is the quality of its search....\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.vskills.in\/certification\/tutorial\/search\/\" \/>\n<meta property=\"og:site_name\" content=\"Tutorial\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/vskills.in\/\" \/>\n<meta property=\"article:modified_time\" content=\"2024-04-12T08:47:14+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/25-1.jpg\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"48 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.vskills.in\/certification\/tutorial\/search\/\",\"url\":\"https:\/\/www.vskills.in\/certification\/tutorial\/search\/\",\"name\":\"Search - Tutorial\",\"isPartOf\":{\"@id\":\"https:\/\/www.vskills.in\/certification\/tutorial\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.vskills.in\/certification\/tutorial\/search\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.vskills.in\/certification\/tutorial\/search\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/25-1.jpg\",\"datePublished\":\"2020-01-20T05:58:48+00:00\",\"dateModified\":\"2024-04-12T08:47:14+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.vskills.in\/certification\/tutorial\/search\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.vskills.in\/certification\/tutorial\/search\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.vskills.in\/certification\/tutorial\/search\/#primaryimage\",\"url\":\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/25-1.jpg\",\"contentUrl\":\"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/25-1.jpg\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.vskills.in\/certification\/tutorial\/search\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.vskills.in\/certification\/tutorial\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Search\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.vskills.in\/certification\/tutorial\/#website\",\"url\":\"https:\/\/www.vskills.in\/certification\/tutorial\/\",\"name\":\"Tutorial\",\"description\":\"Vskills - A initiative in elearning and certification\",\"publisher\":{\"@id\":\"https:\/\/www.vskills.in\/certification\/tutorial\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.vskills.in\/certification\/tutorial\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.vskills.in\/certification\/tutorial\/#organization\",\"name\":\"Vskills\",\"url\":\"https:\/\/www.vskills.in\/certification\/tutorial\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.vskills.in\/certification\/tutorial\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.vskills.in\/certification\/tutorial\/wp-content\/uploads\/2017\/07\/vskills-min-logo.jpg\",\"contentUrl\":\"https:\/\/www.vskills.in\/certification\/tutorial\/wp-content\/uploads\/2017\/07\/vskills-min-logo.jpg\",\"width\":73,\"height\":55,\"caption\":\"Vskills\"},\"image\":{\"@id\":\"https:\/\/www.vskills.in\/certification\/tutorial\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/vskills.in\/\",\"https:\/\/x.com\/vskills_in\",\"https:\/\/www.linkedin.com\/company-beta\/1371554\/\",\"https:\/\/www.youtube.com\/channel\/UCMWnscxPwRF_PqXo9B7q_Tw\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Search - Tutorial","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.vskills.in\/certification\/tutorial\/search\/","og_locale":"en_US","og_type":"article","og_title":"Search - Tutorial","og_description":"The Web is all about search. Two of the Net\u2019s biggest success stories, Google and Yahoo, built their multi-billion-dollar businesses around search. Nearly every site sees a large percentage of traffic coming to and from its search pages. Often the difference between the success or failure of a site is the quality of its search....","og_url":"https:\/\/www.vskills.in\/certification\/tutorial\/search\/","og_site_name":"Tutorial","article_publisher":"https:\/\/www.facebook.com\/vskills.in\/","article_modified_time":"2024-04-12T08:47:14+00:00","og_image":[{"url":"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/25-1.jpg","type":"","width":"","height":""}],"twitter_misc":{"Est. reading time":"48 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.vskills.in\/certification\/tutorial\/search\/","url":"https:\/\/www.vskills.in\/certification\/tutorial\/search\/","name":"Search - Tutorial","isPartOf":{"@id":"https:\/\/www.vskills.in\/certification\/tutorial\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.vskills.in\/certification\/tutorial\/search\/#primaryimage"},"image":{"@id":"https:\/\/www.vskills.in\/certification\/tutorial\/search\/#primaryimage"},"thumbnailUrl":"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/25-1.jpg","datePublished":"2020-01-20T05:58:48+00:00","dateModified":"2024-04-12T08:47:14+00:00","breadcrumb":{"@id":"https:\/\/www.vskills.in\/certification\/tutorial\/search\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.vskills.in\/certification\/tutorial\/search\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.vskills.in\/certification\/tutorial\/search\/#primaryimage","url":"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/25-1.jpg","contentUrl":"https:\/\/www.vskills.in\/lms\/wp-content\/uploads\/2016\/07\/25-1.jpg"},{"@type":"BreadcrumbList","@id":"https:\/\/www.vskills.in\/certification\/tutorial\/search\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.vskills.in\/certification\/tutorial\/"},{"@type":"ListItem","position":2,"name":"Search"}]},{"@type":"WebSite","@id":"https:\/\/www.vskills.in\/certification\/tutorial\/#website","url":"https:\/\/www.vskills.in\/certification\/tutorial\/","name":"Tutorial","description":"Vskills - A initiative in elearning and certification","publisher":{"@id":"https:\/\/www.vskills.in\/certification\/tutorial\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.vskills.in\/certification\/tutorial\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.vskills.in\/certification\/tutorial\/#organization","name":"Vskills","url":"https:\/\/www.vskills.in\/certification\/tutorial\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.vskills.in\/certification\/tutorial\/#\/schema\/logo\/image\/","url":"https:\/\/www.vskills.in\/certification\/tutorial\/wp-content\/uploads\/2017\/07\/vskills-min-logo.jpg","contentUrl":"https:\/\/www.vskills.in\/certification\/tutorial\/wp-content\/uploads\/2017\/07\/vskills-min-logo.jpg","width":73,"height":55,"caption":"Vskills"},"image":{"@id":"https:\/\/www.vskills.in\/certification\/tutorial\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/vskills.in\/","https:\/\/x.com\/vskills_in","https:\/\/www.linkedin.com\/company-beta\/1371554\/","https:\/\/www.youtube.com\/channel\/UCMWnscxPwRF_PqXo9B7q_Tw"]}]}},"_links":{"self":[{"href":"https:\/\/www.vskills.in\/certification\/tutorial\/wp-json\/wp\/v2\/pages\/75670","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.vskills.in\/certification\/tutorial\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.vskills.in\/certification\/tutorial\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.vskills.in\/certification\/tutorial\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.vskills.in\/certification\/tutorial\/wp-json\/wp\/v2\/comments?post=75670"}],"version-history":[{"count":4,"href":"https:\/\/www.vskills.in\/certification\/tutorial\/wp-json\/wp\/v2\/pages\/75670\/revisions"}],"predecessor-version":[{"id":83351,"href":"https:\/\/www.vskills.in\/certification\/tutorial\/wp-json\/wp\/v2\/pages\/75670\/revisions\/83351"}],"wp:attachment":[{"href":"https:\/\/www.vskills.in\/certification\/tutorial\/wp-json\/wp\/v2\/media?parent=75670"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.vskills.in\/certification\/tutorial\/wp-json\/wp\/v2\/categories?post=75670"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.vskills.in\/certification\/tutorial\/wp-json\/wp\/v2\/tags?post=75670"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}