REFERENCE :https://docs.djangoproject.com/en/3.0/
Installing Django in steps:
- Install python
- We are going to use the official release of Django , which can be done by $ python -m pip install Django
- Verify the installation by typing , open a python from you shell >>>import django >>>print(django.get_version())
- 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)
- A client side which lets you vote and see the poll
- 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 yourmanage.py
file so that it can be imported as its own top-level module, rather than a sub module ofmysite
. $ 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:
- Create database schema (CREATE TABLE)
- 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) Therender()
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 anHttpResponse
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 givenquestion_text
and published the given number ofdays
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.openpolls/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: openpolls/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?'