Django Basics and Implementation of a basic App

REFERENCE :https://docs.djangoproject.com/en/3.0/

Installing Django in steps:

  1. Install python
  2. We are going to use the official release of Django , which can be done by $ python -m pip install Django
  3. Verify the installation by typing , open a python from you shell >>>import django >>>print(django.get_version())
  4. That’s it

Difference between Projects and apps

An app is a Web application that does something 
A project is a collection of configuration and apps for a particular website
A project can contain multiple apps, an app can be in multiple projects .

First Django app ,part1(Basic poll application)

  1. A client side which lets you vote and see the poll
  2. An admin , lets you add,change and delete polls
1.From command line go into a directory , where you would like to store your code,then run      $ django-admin startproject mysite .
This will create a myssite directory in your current directory.Try not using built in Python or Django components

2.Development Server : Change into the outer mysite directory, then run
$ python manage.py runserver
Starting development server at http://127.0.0.1:8000/

3. Creating the Polls app : we’ll create our poll app right next to your manage.py file so that it can be imported as its own top-level module, rather than a sub module of mysite.
$ python manage.py startapp polls

4.First view: open the file polls/views.py  , put the following python code :

from django.http import HttpResponse
 def index(request):
     return HttpResponse("Hello, world. You're at the polls index.")

5.Create a file called urls.py

from django.urls import path
 from . import views
 urlpatterns = [
     path('', views.index, name='index'),

6.Add an import for django.urls.include  and insert an include() inmystite/urls.py
from django.contrib import admin
 from django.urls import include, path
 urlpatterns = [
     path('polls/', include('polls.urls')),
     path('admin/', admin.site.urls),
 ]

7.You have now wired an index view into the URL configuration :
$ python manage.py runserver

The path function passed four arguments:

1.path(route) : it contains a URL pattern .When processing a request , Django starts at the first pattern in urlpatterns  and makes its way down the list,until it finds one that matches.

2.path(view): When Django finds a matching pattern, it calls the specified view function with HttpRequest object as the first argument and any capture values from the route as keyword arguments.

3.path(kwargs): 

4path(name):allows you to make global changes to the UL pattern of your project while only touching a single file

Django follows the DRY Principle , the goal is to define your data model in one place and automatically derive things from it

We'll create two models:Question and Choice ,
1.Question : a question plus a publication date
2.Choice :text of the choice and vote tally
Each choice is associated with a question 

Each model is represented by a class that subclasses django.db.models.Model , Each model has a number of class variables ,each of which represents a database field in the model.Each field is represented by an instance of Field class ,this tells Django what type of data each field holds .The name of each Field instance is the field’s name ,in machine-friendly format .A field can also have various optional arguments , in this case ,we’ve set the default value of votes to 0. A relationship is defined ,using ForeignKey.

Activating models:

  1. Create database schema (CREATE TABLE)
  2. Create a python database-access API for accessing Question and Choice objects

Django app, part 2

Setup the database , create your first model , and get a quick introduction to Django’s automatically generated admin site.

1.Database setup:

1.Open mysite/settings.py , by default we use SQLite , you could chang it to PostgreSQL ,MYSQL or Oracle 
Also set the time zone to your time zone ,then run the command:
$ python manage.py migrate
By default, INSTALLED_APPS contains the following apps, all of which come with Django:
 django.contrib.admin – The admin site. You’ll use it shortly.
 django.contrib.auth – An authentication system.
 django.contrib.contenttype – A framework for content types.
 django.contrib.sessions – A session framework.
 django.contrib.messages – A messaging framework.
 django.contrib.staticfiles – A framework for managing static files. 

2.Creating models

1.In our polls app we create 2 models :Question and Choice, 
   Question question and a publication date
   Choice  text of the choice and vote tally.
Open polls/model.py
from django.db import models
 class Question(models.Model):
     question_text = models.CharField(max_length=200)
     pub_date = models.DateTimeField('date published')
 class Choice(models.Model):
     question = models.ForeignKey(Question, on_delete=models.CASCADE)
     choice_text = models.CharField(max_length=200)
     votes = models.IntegerField(default=0)

2.Each field is represented by an instance of a Field class, tells Django what type of data each field holds
The name of each Field instance is the field's name in machine friendly format .
Finally a relationship is  defined using ForeignKey ,that tells Django each choice is related to a single question

3.Activating models:

1. Create a database schema for this app
2.Create a Python database-access API for accessing Question and Choice 

3.Before that we have to include app in our project ,open mysite/settings.py , and under INSTALLED_APPS=[add     'polls.apps.PollsConfig' , ]

4. Now to use Migrations , to tell django we have committed changes , we use command
$ python manage.py makemigrations polls

5.The sqlmigrate takes migration names and returns their SQL
$ python manage.py sqlmigrate polls 0001
This doesn't actually run the migration on your database -instead ,it prints it to the screen so that you can see what SQL Django this is require
Now run $ python manage.py migrate

4.Playing with the API

1.Invoke the python shell ,$ python manage.py shell

  >>> from polls.models import Choice, Question 
 >>> Question.objects.all()
 <QuerySet []>
 >>> from django.utils import timezone 
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
>>> q.save()  
>>> q.id 
1 
 >>> q.question_text 
"What's new?" 
>>> q.pub_date 
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>) 
 >>> q.question_text = "What's up?" 
>>> q.save() 
 >>> Question.objects.all()
 <QuerySet [<Question: Question object (1)>]>#this is not helpful hence we add a __str__()function in polls/model.py

Open polls/models.py

from django.db import models
 class Question(models.Model):
     # …
     def str(self):
         return self.question_text
 class Choice(models.Model):
     # …
     def str(self):
         return self.choice_text #now we add a custom method to this model

Open polls/models.py

import datetime
 from django.db import models
 from django.utils import timezone
 class Question(models.Model):
     # …
     def was_published_recently(self):
         return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

We start a new Python interactive shell by running python manage.py shell again

>>> from polls.models import Choice, Question
 # Make sure our __str__() addition worked. 
>>> Question.objects.all() 
<QuerySet [<Question: What's up?>]> 
 >>> Question.objects.filter(id=1) 
<QuerySet [<Question: What's up?>]
>>> Question.objects.filter(question_text__startswith='What') 
<QuerySet [<Question: What's up?>]
 >>> from django.utils import timezone 
>>> current_year = timezone.now().year 
>>> Question.objects.get(pub_date__year=current_year)
 <Question: What's up?> 
>>> Question.objects.get(id=2) 
Traceback (most recent call last):   
  ...
 DoesNotExist: Question matching query does not exist
.>>> Question.objects.get(pk=1) 
<Question: What's up?> 
 >>> q = Question.objects.get(pk=1) 
>>> q.was_published_recently() True #
 >>> q = Question.objects.get(pk=1)
 >>> q.choice_set.all()
 <QuerySet []> # Create three choices. 
>>> q.choice_set.create(choice_text='Not much', votes=0) 
<Choice: Not much> 
>>> q.choice_set.create(choice_text='The sky', votes=0) 
<Choice: The sky> 
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)
 >>> c.question 
<Question: What's up?> 
>>> q.choice_set.all() 
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> >>> q.choice_set.count() 3
 >>> Choice.objects.filter(question__pub_date__year=current_year)
 <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> 
 >>> c = q.choice_set.filter(choice_text__startswith='Just hacking') 
>>> c.delete() 

4.Introducing the Django Admin

1.Creating an admin user ,$ python manage.py   createsuperuser
     Username:
      Password:
      Password(again):
Superuser created successfully 

2.Start the development serer : Django admin site is activated by default , so run the server : $ python manage.py runserver

3.Open polls/admin.py
from django.contrib import admin
 from .models import Question
 admin.site.register(Question)

Django app part 3:

1.Writing more views:

 Open polls/views.py
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)

Now add the path() calls in polls/urls.py

from django.urls import path
 from . import views
 urlpatterns = [
     # ex: /polls/
     path('', views.index, name='index'),
     # ex: /polls/5/
     path('/', views.detail, name='detail'),
     # ex: /polls/5/results/
     path('/results/', views.results, name='results'),
     # ex: /polls/5/vote/
     path('/vote/', views.vote, name='vote'),
 ]

2.Write views that actually do something
Each view is responsible for doing one of two things  either returning an Http Response or raising an exception.

3.By convention DjangoTemplates looks for a "templates" subdirectory in each of the INSTALLED_APPS.
create a directory's -> polls/templates/polls/index.html 
{% if latest_question_list %}
     
     {% for question in latest_question_list %}         {{ question.question_text }}     {% endfor %}     

 {% else %}
     No polls are available.

 {% endif %}

4.Update our index view in in polls/views.py
from django.http import HttpResponse
 from django.template import loader
 from .models import Question
 def index(request):
     latest_question_list = Question.objects.order_by('-pub_date')[:5]
     template = loader.get_template('polls/index.html')
     context = {
         'latest_question_list': latest_question_list,
     }
     return HttpResponse(template.render(context, request))

5.A shortcut:render() ,open polls/views.py

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)

The render() function takes the request object as its first argument, a template name as its second argument and a dictionary as its optional third argument. It returns an HttpResponse object of the given template rendered with the given context.

6.Raising a 404 error:open polls/views.py

from django.http import Http404
 from django.shortcuts import render
 from .models import Question
 …
 def detail(request, question_id):
     try:
         question = Question.objects.get(pk=question_id)
     except Question.DoesNotExist:
         raise Http404("Question does not exist")
     return render(request, 'polls/detail.html', {'question': question})

7.A shortcut:get_object_or_404(): open polls/views.py

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})

8.Use the template system: Back to the detail() view , open polls/templates/polls/detail.html
<h1>{{ question.question_text }}</h1>
<u1>
 {% for choice in question.choice_set.all %}  
       <li>{{ choice.choice_text }}</li>
 {% endfor %} 
</ul>

9.Removing hard coded URLs in templates: open polls/index.html,replace this with hard coded string .
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

open polls/urls.py: added the word 'specifics'
 path('specifics/<int:question_id>/', views.detail, name='detail'),

10.Name spacing URL names: open polls/urls.py ,

from django.urls import path
 from . import views
 app_name = 'polls'
 urlpatterns = [
     path('', views.index, name='index'),
     path('/', views.detail, name='detail'),
     path('/results/', views.results, name='results'),
     path('/vote/', views.vote, name='vote'),
 ]

Django app ,part 4:

1.Write a minimal form : Open polls/templates/polls/detail.html 

<h1>{{ question.question_text }}</h1>
 {% if  error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
 {% 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> 

2.Create a Django view ,  that handles the submitted data and does something with it , we create a URL conf for the application

3.Create a dummy implementation of vote() function,open polls/views.py

from django.http import HttpResponse, HttpResponseRedirect
 from django.shortcuts import get_object_or_404, render
 from django.urls import reverse
 from .models import Choice, 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,)))

4.Creating the vote function: open polls/views.py

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

5.Create a polls/results.html

<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> 

Generic views: introduce new views based on Django’s generic views

1.Amend URL conf: open polls/urls.py

from django.urls import path
 from . import views
 app_name = 'polls'
 urlpatterns = [
     path('', views.IndexView.as_view(), name='index'),
     path('/', views.DetailView.as_view(), name='detail'),
     path('/results/', views.ResultsView.as_view(), name='results'),
     path('/vote/', views.vote, name='vote'),
 ]

2. Amend views:open polls/views.py 

from django.http import HttpResponseRedirect
 from django.shortcuts import get_object_or_404, render
 from django.urls import reverse
 from django.views import generic
 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."""          
               return Question.objects.order_by('-pub_date')[:5]
 class DetailView(generic.DetailView):
     model = Question
     template_name = 'polls/detail.html'
 class ResultsView(generic.DetailView):
     model = Question
     template_name = 'polls/results.html'
 def vote(request, question_id):
     … # same as above, no changes needed.

Django app ,part 5:

1.Automated tests :
 There was a bug in function Question.was_published_recently() that if a field is in the future , then also  it shows True for , future_question.was_published_recently()

2.Create a test :open polls/tests.py
import datetime
 from django.test import TestCase 
from django.utils import timezone 
from .models import Question 
class QuestionModelTests(TestCase):    
 def test_was_published_recently_with_future_question(self): 
        """         was_published_recently() returns False for questions whose pub_date                        
                       is in the future.         """         
time = timezone.now() + datetime.timedelta(days=30)        
 future_question = Question(pub_date=time)         self.assertIs(future_question.was_published_recently(), False)

3.Fixing the bug: open polls/models.py , change the function,
def was_published_recently(self):
     now = timezone.now()
     return now - datetime.timedelta(days=1) <= self.pub_date <= now

4.More comprehensive tests: open polls/tests.py

def test_was_published_recently_with_old_question(self):
     """
     was_published_recently() returns False for questions whose pub_date
     is older than 1 day.
     """
     time = timezone.now() - datetime.timedelta(days=1, seconds=1)
     old_question = Question(pub_date=time)
     self.assertIs(old_question.was_published_recently(), False)
 def test_was_published_recently_with_recent_question(self):
     """
     was_published_recently() returns True for questions whose pub_date
     is within the last day.
     """
     time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
     recent_question = Question(pub_date=time)
     self.assertIs(recent_question.was_published_recently(), True)

5.Django test client : $ python manage.py shell

>>>from django.test.utils  import setup_test_environment
>>>setup_test_environment()
>>>from django.test import Client
>>>client=Client()
>>>response=client.get('/')
Not Found
>>>response.status_code
404
>>>from django.urls import reverse 
>>>response=client.get(reverse('polls:index'))
>>>response.status_code
200
>>>response.content
b'\n    <ul>\n    \n        <li><a href="/polls/1/">What's up?</a></li>\n    \n    </ul>\n\n'
>>>response.context['latest_question_list']
<QuerySet [,Question:What's up?]>

6.Improving our view:open polls/views.py

from django.utlis import timezone
def get_queeryset(self):
        return Question.objects.filter(pub_date_lte=timezone.now()).order_by('-pub_date')[:5]

7.Testing our new view:open polls/tests.py

from django.urls import reverse
def create_question(question_text, days):
     """
     Create a question with the given question_text and published the
     given number of days offset to now (negative for questions published
     in the past, positive for questions that have yet to be published).
     """
     time = timezone.now() + datetime.timedelta(days=days)
     return Question.objects.create(question_text=question_text, pub_date=time)
 class QuestionIndexViewTests(TestCase):
     def test_no_questions(self):
         """
         If no questions exist, an appropriate message is displayed.
         """
         response = self.client.get(reverse('polls:index'))
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, "No polls are available.")
         self.assertQuerysetEqual(response.context['latest_question_list'], [])
 def test_past_question(self):    
 """     Questions with a pub_date in the past are
    displayed on the     index page.     """     

create_question(question_text="Past question.", days=-30)     response = self.client.get(reverse('polls:index'))     self.assertQuerysetEqual(response.context['latest_question_list'],         ['<Question: Past question.>']     )
def test_future_question(self):
     """     Questions with a pub_date in the future aren't displayed on     the index page.     """     create_question(question_text="Future question.", days=30)     response = self.client.get(reverse('polls:index'))     self.assertContains(response, "No polls are available.")     self.assertQuerysetEqual(response.context['latest_question_list'], [])
def test_future_question_and_past_question(self):  
"""     Even if both past and future questions exist, only past questions     are displayed.     """     create_question(question_text="Past question.", days=-30)     create_question(question_text="Future question.", days=30)     response = self.client.get(reverse('polls:index'))     self.assertQuerysetEqual(         response.context['latest_question_list'],         ['<Question: Past question.>']     ) 
def test_two_past_questions(self): 
"""     The questions index page may display multiple questions.     """    
 create_question(question_text="Past question 1.", days=-30)     
create_question(question_text="Past question2.", days=-5)     response = self.client.get(reverse('polls:index'))     self.assertQuerysetEqual(response.context['latest_question_list'],         
['<Question: Past question 2.>', '<Question: Past question 1.>']     )

8.Testing DetailView  : open polls/views.py 

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

now open polls/tests.py

class QuestionDetailViewTests(TestCase):
     def test_future_question(self):
         """
         The detail view of a question with a pub_date in the future
         returns a 404 not found.
         """
         future_question = create_question(question_text='Future question.', days=5)
         url = reverse('polls:detail', args=(future_question.id,))
         response = self.client.get(url)
         self.assertEqual(response.status_code, 404)
 def test_past_question(self):    
 """     The detail view of a question with a pub_date in the past     displays the question's text.     """      past_question = create_question(question_text='Past  Question.', days=-5)  
url = reverse('polls:detail', args=(past_question.id,))     response = self.client.get(url)     self.assertContains(response, past_question.question_text)

Django app ,part 6:

 1.Customizing : open(polls/static/polls/style.css):
li a {
     color: green;
 }

2.open polls/templates/polls/index.html:
on top of the existing code 
{% load static %}
 <link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}">
 
3.Add a background -image: open polls/static/polls/style.css

body {
     background: white url("images/background.gif") no-repeat;
 }

Django app, part7:

1.Customizing the admin form : open polls/admin.py

from django.contrib import admin
 from .models import Question
 class QuestionAdmin(admin.ModelAdmin):
     fields = ['pub_date', 'question_text']
 admin.site.register(Question, QuestionAdmin)

2.form up into fieldsets: open polls/admin.py

from django.contrib import admin
 from .models import Question
 class QuestionAdmin(admin.ModelAdmin):
     fieldsets = [
         (None,               {'fields': ['question_text']}),
         ('Date information', {'fields': ['pub_date']}),
     ]
 admin.site.register(Question, QuestionAdmin)

3.Adding related objects : open polls/admin.py

from django.contrib import admin
 from .models import Choice, Question
 class ChoiceInline(admin.StackedInline):
     model = Choice
     extra = 3
 class QuestionAdmin(admin.ModelAdmin):
     fieldsets = [
         (None,               {'fields': ['question_text']}),
         ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
     ]
     inlines = [ChoiceInline]
 admin.site.register(Question, QuestionAdmin)

4.To reduce time ,instead of StackedInLine change it to TabularInLine

5.Customize the admin change list: open polls/admin.py

class QuestionAdmin(admin.ModelAdmin):   
  # ...    
 list_display = ('question_text', 'pub_date', 'was_published_recently') 
 list_filter = ['pub_date']


then open polls/models.py :

class Question(models.Model):
     # …
     def was_published_recently(self):
         now = timezone.now()
         return now - datetime.timedelta(days=1) <= self.pub_date <= now
     was_published_recently.admin_order_field = 'pub_date'
     was_published_recently.boolean = True
     was_published_recently.short_description = 'Published recently?'

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s