Project

General

Profile

Feature #878

Updated by Daniel Curtis about 8 years ago

{{>toc}} 

 This is a condensed version of the Django tutorial found "here":https://docs.djangoproject.com/en/1.10/intro/tutorial01/ 

 h1. Setup The Environment 

 * Setup a python virtual environment to develop in 
 #* For Arch Linux use issue #876 
 #* For FreeBSD use issue #874 

 h1. Install Django 

 * Switch to the pydev user and activate the virtualenv: 
 <pre> 
 su - pydev 
 source ~/venv/new_app/bin/activate 
 </pre> 

 * And install Django: 
 <pre> 
 pip install Django 
 </pre> 

 h1. New Django App 

 * Create a new Django project: 
 <pre> 
 django-admin startproject www_example_com 
 cd www_example_com 
 </pre> 

 * Edit the project settings.py file: 
 <pre> 
 vi www_example_com/settings.py 
 </pre> 
 #* And add the host IP address of the local development machine: 
 <pre> 
 ALLOWED_HOSTS = ['192.168.1.90'] 
 </pre> 

 * Test the new app by running the development test web server: 
 <pre> 
 ./manage.py runserver 0.0.0.0:8000 
 </pre> 
 *NOTE*: Press @CTRL+C@ to stop the test web server 

 * Now create a new simple poll web application: 
 <pre> 
 ./manage.py startapp polls 
 </pre> 

 h2. Initial Framework 

 h3. Create View 

 * Create the initial view: 
 <pre> 
 vi polls/views.py 
 </pre> 
 #* And add the following: 
 <pre> 
 from django.shortcuts import render 
 from django.http import HttpResponse 

 def index(request): 
     return HttpResponse("Hello, world. You're at the polls index.") 
 </pre> 

 * Now map the view to a URL by creating: 
 <pre> 
 vi polls/urls.py 
 </pre> 
 #* And add the following: 
 <pre> 
 from django.conf.urls import url 

 from . import views 

 app_name = 'polls' 
 urlpatterns = [ 
     url(r'^$', views.index, name='index'), 
 ] 
 </pre> 

 * Next, point the root URLconf at the polls.urls module: 
 <pre> 
 vi www_example_com/urls.py 
 </pre> 
 #* And add the URL handler the @polls/@ part of the web application: 
 <pre> 
 from django.conf.urls import include, url 
 from django.contrib import admin 

 app_name = 'polls' 
 urlpatterns = [ 
     url(r'^polls/', include('polls.urls')), 
     url(r'^admin/', admin.site.urls), 
 ] 
 </pre> 

 * Rerun the development test server: 
 <pre> 
 ./manage.py runserver 0.0.0.0:8000 
 </pre> 
 #* And open a web browser and go to http://192.168.1.90/polls 
 *NOTE*: Press @CTRL+C@ to stop the test web server 

 h2. Connect to a Database 

 * Begin by initializing the database: 
 <pre> 
 ./manage.py migrate 
 </pre> 

 h3. Create Model 

 * Create the data model file: 
 <pre> 
 vi polls/models.py 
 </pre> 
 #* And add the following to create two data models, *Question* and *Choice*: 
 <pre> 
 from django.db import models 

 def was_published_recently(self): 
     now = timezone.now() 
     return now - datetime.timedelta(days=1) <= self.pub_date <= now 

 class Question(models.Model): 
     question_text = models.CharField(max_length=200) 
     pub_date = models.DateTimeField('date published') 

     def __str__(self): 
         return self.question_text 

     def was_published_recently(self): 
         return self.pub_date >= timezone.now() - datetime.timedelta(days=1) 


 class Choice(models.Model): 
     question = models.ForeignKey(Question, on_delete=models.CASCADE) 
     choice_text = models.CharField(max_length=200) 
     votes = models.IntegerField(default=0) 

     def __str__(self): 
         return self.choice_text 
 </pre> 

 * Edit the @www_example_com/settings.py@ file: 
 <pre> 
 vi www_example_com/settings.py 
 </pre> 
 #* And add the *@polls.apps.PollsConfig@* dotted path to the INSTALLED_APPS setting to include the app in our project: 
 <pre> 
 INSTALLED_APPS = [ 
     'polls.apps.PollsConfig', 
     'django.contrib.admin', 
     'django.contrib.auth', 
     'django.contrib.contenttypes', 
     'django.contrib.sessions', 
     'django.contrib.messages', 
     'django.contrib.staticfiles', 
 ] 
 </pre> 

 * Now create the polls data model migrations to update the database structure: 
 <pre> 
 ./manage.py makemigrations polls 
 </pre> 

 * Then apply the migration: 
 <pre> 
 ./manage.py migrate 
 </pre> 

 h3. Adding Questions 

 * Start the management shell: 
 <pre> 
 ./manage.py shell 
 </pre> 
 #* And run the following to create a question: 
 <pre> 
 import django 
 from polls.models import Question, Choice 
 from django.utils import timezone 

 q = Question(question_text="What's new?", pub_date=timezone.now()) 
 q.save() 
 </pre> 

 h3. Adding Choices 

 * Start the management shell: 
 <pre> 
 ./manage.py shell 
 </pre> 
 #* And run the following to create two choices: 
 <pre> 
 import django 
 from polls.models import Question, Choice 
 from django.utils import timezone 

 q = Question.objects.get(pk=1) 
 q.choice_set.create(choice_text='Not much', votes=0) 
 q.choice_set.create(choice_text='Same Stuff, Different Day', votes=0) 
 </pre> 

 h2. Admin Interface 

 * Create an admin user: 
 <pre> 
 ./manage.py createsuperuser 
 </pre> 

 * Edit the admin file for the poll app: 
 <pre> 
 vi polls/admin.py 
 </pre> 
 #* And add the following to make the poll app modifiable in the admin interface 
 <pre> 
 from django.contrib import admin 
 from .models import Question 

 admin.site.register(Question) 
 </pre> 

 * Rerun the development test server: 
 <pre> 
 ./manage.py runserver 0.0.0.0:8000 
 </pre> 
 #* And open a web browser and go to http://192.168.1.90/admin 
 *NOTE*: Press @CTRL+C@ to stop the test web server 

 h2. Extending a View With Arguments 

 * Edit the polls/views.py file: 
 <pre> 
 vi polls/views.py 
 </pre> 
 #* Add a few more views to polls/views.py. These views are slightly different, because they take an argument: 
 <pre> 
 # Add after the index function 
 def detail(request, question_id): 
     return HttpResponse("You're looking at question %s." % question_id) 

 def results(request, question_id): 
     response = "You're looking at the results of question %s." 
     return HttpResponse(response % question_id) 

 def vote(request, question_id): 
     return HttpResponse("You're voting on question %s." % question_id) 
 </pre> 

 * Then edit the polls/urls.py file: 
 <pre> 
 vi polls/urls.py 
 </pre> 
 #* And add the new views into the polls.urls module by adding the following url() calls: 
 <pre> 
 from django.conf.urls import url 

 from . import views 

 app_name = 'polls' 
 urlpatterns = [ 
     url(r'^$', views.index, name='index'), 
     url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'), 
     url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'), 
     url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'), 
 ] 
 </pre> 

 h2. Dynamic Views 

 * Edit the views of the polls app: 
 <pre> 
 vi polls/views.py 
 </pre> 
 #* And refactor the index() view to display the latest 5 poll questions in the system, separated by commas, according to publication date: 
 <pre> 
 from django.http import HttpResponse 
 from django.shortcuts import render 
 from .models import Question 

 def index(request): 
     latest_question_list = Question.objects.order_by('-pub_date')[:5] 
     context = {'latest_question_list': latest_question_list} 
     return render(request, 'polls/index.html', context) 

 # Leave the rest of the views (detail, results, vote) unchanged 
 </pre> 

 * Create a directory for templates in the polls app, then another directory in the templates directory for polls: 
 <pre> 
 mkdir polls/templates 
 mkdir polls/templates/polls 
 </pre> 

 * Then create a template for the index of the polls app: 
 <pre> 
 vi polls/templates/polls/index.html 
 </pre> 
 #* And add the following: 
 <pre> 
 {% if latest_question_list %} 
     <ul> 
     {% for question in latest_question_list %} 
         <li><a href="{% url 'polls:detail' question.id %}/">{{ question.question_text }}</a></li> 
     {% endfor %} 
     </ul> 
 {% else %} 
     <p>No polls are available.</p> 
 {% endif %} 
 </pre> 

 h2. 404 Error Page 

 * Edit the polls app views: 
 <pre> 
 vi polls/views.py 
 </pre> 
 #* And refactor the detail() view to display a 404 error for non-existent objects: 
 <pre> 
 from django.shortcuts import get_object_or_404, render 

 from .models import Question 
 # ... 
 def detail(request, question_id): 
     question = get_object_or_404(Question, pk=question_id) 
     return render(request, 'polls/detail.html', {'question': question}) 
 </pre> 

 * And create a template for the detail page in the polls app: 
 <pre> 
 vi polls/templates/polls/detail.html 
 </pre> 
 #* And add the following: 
 <pre> 
 <h1>{{ question.question_text }}</h1> 
 <ul> 
 {% for choice in question.choice_set.all %} 
     <li>{{ choice.choice_text }}</li> 
 {% endfor %} 
 </ul> 
 </pre> 

 h1. Create a Form 

 * Edit the polls app detail template: 
 <pre> 
 vi polls/templates/polls/detail.html 
 </pre> 
 #* And refactor the template to add an HTML @<form>@ element: 
 <pre> 
 <h1>{{ question.question_text }}</h1> 

 {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} 

 <form action="{% url 'polls:vote' question.id %}" method="post"> 
 {% csrf_token %} 
 {% for choice in question.choice_set.all %} 
     <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" /> 
     <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br /> 
 {% endfor %} 
 <input type="submit" value="Vote" /> 
 </form> 
 </pre> 

 * Edit the polls app views handler:  
 <pre> 
 vi polls/views.py 
 </pre> 
 #* And refactor the @vote()@ and @results()@ functions to create the real versions: 
 <pre> 
 from django.shortcuts import get_object_or_404, render 
 from django.http import HttpResponseRedirect, HttpResponse 
 from django.urls import reverse 
 from .models import Choice, Question 

 # ... 
 def results(request, question_id): 
     question = get_object_or_404(Question, pk=question_id) 
     return render(request, 'polls/results.html', {'question': question}) 

 def vote(request, question_id): 
     question = get_object_or_404(Question, pk=question_id) 
     try: 
         selected_choice = question.choice_set.get(pk=request.POST['choice']) 
     except (KeyError, Choice.DoesNotExist): 
         # Redisplay the question voting form. 
         return render(request, 'polls/detail.html', { 
             'question': question, 
             'error_message': "You didn't select a choice.", 
         }) 
     else: 
         selected_choice.votes += 1 
         selected_choice.save() 
         # Always return an HttpResponseRedirect after successfully dealing 
         # with POST data. This prevents data from being posted twice if a 
         # user hits the Back button. 
         return HttpResponseRedirect(reverse('polls:results', args=(question.id,))) 
 </pre> 

 * Create the polls app template for the results: 
 <pre> 
 vi polls/templates/polls/results.html 
 </pre> 
 #* And add the following: 
 <pre> 
 <h1>{{ question.question_text }}</h1> 

 <ul> 
 {% for choice in question.choice_set.all %} 
     <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li> 
 {% endfor %} 
 </ul> 

 <a href="{% url 'polls:detail' question.id %}">Vote again?</a> 
 </pre> 

 h1. Refactoring 

 * Edit the polls app URL handler: 
 <pre> 
 vi polls/urls.py 
 </pre> 
 #* And refactor the second and third URL regexes that handle the Detail and Results views: 
 <pre> 
 from django.conf.urls import url 
 from . import views 

 app_name = 'polls' 
 urlpatterns = [ 
     url(r'^$', views.IndexView.as_view(), name='index'), 
     url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'), 
     url(r'^(?P<pk>[0-9]+)/results/$', views.ResultsView.as_view(), name='results'), 
     url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'), 
 ] 
 </pre> 

 * Then edit the polls app views handler: 
 <pre> 
 vi polls/views.py 
 </pre> 
 * Refactor the old @index()@, @detail()@, and @results()@ views and use Django’s generic views instead: 
 <pre> 
 from django.shortcuts import get_object_or_404, render 
 from django.http import HttpResponseRedirect 
 from django.urls import reverse 
 from django.views import generic 
 from django.utils import timezone 
 from .models import Choice, Question 

 class IndexView(generic.ListView): 
     template_name = 'polls/index.html' 
     context_object_name = 'latest_question_list' 

     def get_queryset(self): 
         """ 
         Return the last five published questions (not including those set to be 
         published in the future). 
         """ 
         return Question.objects.filter( 
             pub_date__lte=timezone.now() 
         ).order_by('-pub_date')[:5] 


 class DetailView(generic.DetailView): 
     model = Question 
     template_name = 'polls/detail.html' 
     def get_queryset(self): 
         """ 
         Excludes any questions that aren't published yet. 
         """ 
         return Question.objects.filter(pub_date__lte=timezone.now()) 


 class ResultsView(generic.DetailView): 
     model = Question 
     template_name = 'polls/results.html' 
 </pre> 

 h1. Static Files 

 h2. CSS 

 * Create a CSS file for the polls app: 
 <pre> 
 vi polls/static/polls/style.css 
 </pre> 
 #* And add the following: 
 <pre> 
 li a { 
     color: green; 
 } 
 </pre> 

 * Next, edit the polls app index.html template: 
 <pre> 
 vi polls/templates/polls/index.html 
 </pre> 
 #* An add the following at the top: 
 <pre> 
 {% load static %} 

 <link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}" /> 
 </pre> 

 h2. Background Image 

 * Create an images subdirectory in the polls/static/polls/ directory: 
 <pre> 
 mkdir polls/static/polls/image 
 </pre> 
 *NOTE*: Inside this directory, put an image called background.gif. 

 * Then, edit the stylesheet created earlier: 
 <pre> 
 vi polls/static/polls/style.css 
 </pre> 
 #* And add the following: 
 <pre> 
 body { 
     background: white url("images/background.gif") no-repeat right bottom; 
 } 
 </pre> 

 * Set the STATIC_ROOT setting to the directory from which you’d like to serve these files: 
 <pre> 
 STATIC_ROOT = "/var/www/example.com/static/" 
 </pre> 

 * Run the collectstatic management command: 
 <pre> 
 ./manage.py collectstatic 
 </pre> 

 h1. Resources 

 * https://docs.djangoproject.com/en/1.10/intro/tutorial01/

Back