├── polls ├── __init__.py ├── static │ └── polls │ │ └── style.css ├── templates │ └── polls │ │ ├── results.html │ │ ├── index.html │ │ └── detail.html ├── urls.py ├── admin.py ├── models.py ├── views.py └── tests.py ├── .idea ├── .name ├── vcs.xml ├── modules.xml ├── misc.xml └── first_steps.iml ├── first_steps ├── __init__.py ├── urls.py ├── wsgi.py └── settings.py ├── manage.py └── playing_with_API.py /polls/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | first_steps -------------------------------------------------------------------------------- /first_steps/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /polls/static/polls/style.css: -------------------------------------------------------------------------------- 1 | li a { 2 | color: green; 3 | } 4 | body { 5 | background: white url("images/background.gif") no-repeat right bottom; 6 | } -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "first_steps.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /polls/templates/polls/results.html: -------------------------------------------------------------------------------- 1 |

{{ question.question_text }}

2 | 3 | 8 | 9 | Vote again? -------------------------------------------------------------------------------- /polls/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | url(r'^$', views.IndexView.as_view(), name='index'), 7 | url(r'^(?P[0-9]+)/$', views.DetailView.as_view(), name='detail'), 8 | url(r'^(?P[0-9]+)/results/$', views.ResultsView.as_view(), name='results'), 9 | url(r'^(?P[0-9]+)/vote/$', views.vote, name='vote'), 10 | ] -------------------------------------------------------------------------------- /first_steps/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | 3 | from django.contrib import admin 4 | admin.autodiscover() 5 | 6 | urlpatterns = patterns('', 7 | # Examples: 8 | # url(r'^$', 'first_steps.views.home', name='home'), 9 | # url(r'^blog/', include('blog.urls')), 10 | 11 | url(r'^polls/', include('polls.urls', namespace='polls')), 12 | url(r'^admin/', include(admin.site.urls)), 13 | ) 14 | -------------------------------------------------------------------------------- /polls/templates/polls/index.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 4 | 5 | {% if latest_question_list %} 6 | 11 | {% else %} 12 |

No polls are available.

13 | {% endif %} -------------------------------------------------------------------------------- /first_steps/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for first_steps project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "first_steps.settings") 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /polls/templates/polls/detail.html: -------------------------------------------------------------------------------- 1 |

{{ question.question_text }}

2 | 3 | {% if error_message %}

{{ error_message }}

{% endif %} 4 | 5 |
6 | {% csrf_token %} 7 | {% for choice in question.choice_set.all %} 8 | 9 |
10 | {% endfor %} 11 | 12 |
13 | -------------------------------------------------------------------------------- /polls/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Question, Choice 4 | 5 | 6 | class ChoiceInline(admin.StackedInline): 7 | model = Choice 8 | extra = 3 9 | 10 | 11 | class QuestionAdmin(admin.ModelAdmin): 12 | # fields = ['pub_date', 'question_text'] 13 | 14 | fieldsets = [ 15 | (None, {'fields': ['question_text']}), 16 | ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}), 17 | ] 18 | 19 | list_display = ('question_text', 'pub_date', 'was_published_recently') 20 | list_filter = ['pub_date'] 21 | search_fields = ['question_text'] 22 | 23 | admin.site.register(Question, QuestionAdmin) 24 | admin.site.register(Choice) 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /polls/models.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from django.db import models 4 | from django.utils import timezone 5 | 6 | 7 | class Question(models.Model): 8 | question_text = models.CharField(max_length=200) 9 | pub_date = models.DateTimeField('date published') 10 | 11 | def __str__(self): 12 | return self.question_text 13 | 14 | def was_puplished_recently(self): 15 | now = timezone.now() 16 | return now - datetime.timedelta(days=1) <= self.pub_date <= now 17 | 18 | was_puplished_recently.admin_order_field = 'pub_date' 19 | was_puplished_recently.boolean = True 20 | was_puplished_recently.short_description = 'Published recently?' 21 | 22 | 23 | class Choice(models.Model): 24 | question = models.ForeignKey(Question) 25 | choice_test = models.CharField(max_length=200) 26 | votes = models.IntegerField(default=0) 27 | 28 | def __str__(self): 29 | return self.choice_test 30 | -------------------------------------------------------------------------------- /.idea/first_steps.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | -------------------------------------------------------------------------------- /polls/views.py: -------------------------------------------------------------------------------- 1 | from django.core.urlresolvers import reverse 2 | from django.http import HttpResponseRedirect 3 | from django.shortcuts import render, get_object_or_404 4 | from django.utils import timezone 5 | from django.views import generic 6 | 7 | from .models import Choice, Question 8 | 9 | 10 | class IndexView(generic.ListView): 11 | template_name = "polls/index.html" 12 | context_object_name = "latest_question_list" 13 | 14 | def get_queryset(self): 15 | return Question.objects.filter( 16 | pub_date__lte=timezone.now() 17 | ).order_by('-pub_date')[:5] 18 | 19 | 20 | class DetailView(generic.DetailView): 21 | model = Question 22 | template_name = "polls/detail.html" 23 | 24 | def get_queryset(self): 25 | """ 26 | Excludes any questions that aren't published yet. 27 | """ 28 | return Question.objects.filter(pub_date__lte=timezone.now()) 29 | 30 | 31 | class ResultsView(generic.DetailView): 32 | model = Question 33 | template_name = "polls/results.html" 34 | 35 | 36 | def vote(request, question_id): 37 | p = get_object_or_404(Question, pk=question_id) 38 | try: 39 | selected_choice = p.choice_set.get(pk=request.POST['choice']) 40 | except (KeyError, Choice.DoesNotExist): 41 | return render(request, 'polls/detail.html', { 42 | 'question': p, 43 | 'error_message': "You didn't select a choice", 44 | }) 45 | else: 46 | selected_choice.votes += 1 47 | selected_choice.save() 48 | return HttpResponseRedirect(reverse('polls:results', args=(p.id,))) -------------------------------------------------------------------------------- /first_steps/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for first_steps project. 3 | 4 | For more information on this file, see 5 | https://docs.djangoproject.com/en/1.6/topics/settings/ 6 | 7 | For the full list of settings and their values, see 8 | https://docs.djangoproject.com/en/1.6/ref/settings/ 9 | """ 10 | 11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 12 | import os 13 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 14 | 15 | 16 | # Quick-start development settings - unsuitable for production 17 | # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/ 18 | 19 | # SECURITY WARNING: keep the secret key used in production secret! 20 | SECRET_KEY = 'hlq5ru(06+(ea@6kb+tl=7(oj3+f_!rk#$msygh^48a689nbte' 21 | 22 | # SECURITY WARNING: don't run with debug turned on in production! 23 | DEBUG = True 24 | 25 | TEMPLATE_DEBUG = True 26 | 27 | ALLOWED_HOSTS = [] 28 | 29 | 30 | # Application definition 31 | 32 | INSTALLED_APPS = ( 33 | 'django.contrib.admin', 34 | 'django.contrib.auth', 35 | 'django.contrib.contenttypes', 36 | 'django.contrib.sessions', 37 | 'django.contrib.messages', 38 | 'django.contrib.staticfiles', 39 | 'polls', 40 | 41 | ) 42 | 43 | MIDDLEWARE_CLASSES = ( 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | 'django.middleware.csrf.CsrfViewMiddleware', 47 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 48 | 'django.contrib.messages.middleware.MessageMiddleware', 49 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 50 | ) 51 | 52 | ROOT_URLCONF = 'first_steps.urls' 53 | 54 | WSGI_APPLICATION = 'first_steps.wsgi.application' 55 | 56 | 57 | # Database 58 | # https://docs.djangoproject.com/en/1.6/ref/settings/#databases 59 | 60 | DATABASES = { 61 | 'default': { 62 | 'ENGINE': 'django.db.backends.sqlite3', 63 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 64 | } 65 | } 66 | 67 | # Internationalization 68 | # https://docs.djangoproject.com/en/1.6/topics/i18n/ 69 | 70 | LANGUAGE_CODE = 'en-us' 71 | 72 | TIME_ZONE = 'UTC' 73 | 74 | USE_I18N = True 75 | 76 | USE_L10N = True 77 | 78 | USE_TZ = True 79 | 80 | 81 | # Static files (CSS, JavaScript, Images) 82 | # https://docs.djangoproject.com/en/1.6/howto/static-files/ 83 | 84 | STATIC_URL = '/static/' 85 | 86 | TEMPLATE_DIRS = ( 87 | os.path.join(BASE_DIR, 'templates'), 88 | ) 89 | 90 | TEMPLATES = [ 91 | { 92 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 93 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 94 | 'APP_DIRS': True, 95 | 'OPTIONS': { 96 | 'context_processors': [ 97 | 'django.template.context_processors.debug', 98 | 'django.template.context_processors.request', 99 | 'django.contrib.auth.context_processors.auth', 100 | 'django.contrib.messages.context_processors.messages', 101 | ], 102 | }, 103 | }, 104 | ] -------------------------------------------------------------------------------- /polls/tests.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from django.core.urlresolvers import reverse 3 | 4 | from django.test import TestCase 5 | from django.utils import timezone 6 | 7 | from .models import Question 8 | 9 | 10 | def create_question(question_text, days): 11 | time = timezone.now() + datetime.timedelta(days=days) 12 | return Question.objects.create(question_text=question_text, 13 | pub_date=time) 14 | 15 | 16 | class QuestionViewTests(TestCase): 17 | def test_index_view_with_no_questions(self): 18 | """ 19 | If no questions exist, an appropriate message should be displayed. 20 | """ 21 | response = self.client.get(reverse('polls:index')) 22 | self.assertEqual(response.status_code, 200) 23 | self.assertContains(response, "No polls are available.") 24 | self.assertQuerysetEqual(response.context['latest_question_list'], []) 25 | 26 | def test_index_view_with_a_past_question(self): 27 | """ 28 | Questions with a pub_date in the past should be displayed on the 29 | index page. 30 | """ 31 | create_question(question_text="Past question.", days=-30) 32 | response = self.client.get(reverse('polls:index')) 33 | self.assertQuerysetEqual( 34 | response.context['latest_question_list'], 35 | [''] 36 | ) 37 | 38 | def test_index_view_with_a_future_question(self): 39 | """ 40 | Questions with a pub_date in the future should not be displayed on 41 | the index page. 42 | """ 43 | create_question(question_text="Future question.", days=30) 44 | response = self.client.get(reverse('polls:index')) 45 | self.assertContains(response, "No polls are available.", 46 | status_code=200) 47 | self.assertQuerysetEqual(response.context['latest_question_list'], []) 48 | 49 | def test_index_view_with_future_question_and_past_question(self): 50 | """ 51 | Even if both past and future questions exist, only past questions 52 | should be displayed. 53 | """ 54 | create_question(question_text="Past question.", days=-30) 55 | create_question(question_text="Future question.", days=30) 56 | response = self.client.get(reverse('polls:index')) 57 | self.assertQuerysetEqual( 58 | response.context['latest_question_list'], 59 | [''] 60 | ) 61 | 62 | def test_index_view_with_two_past_questions(self): 63 | """ 64 | The questions index page may display multiple questions. 65 | """ 66 | create_question(question_text="Past question 1.", days=-30) 67 | create_question(question_text="Past question 2.", days=-5) 68 | response = self.client.get(reverse('polls:index')) 69 | self.assertQuerysetEqual( 70 | response.context['latest_question_list'], 71 | ['', ''] 72 | ) 73 | 74 | 75 | class QuestionIndexDetailTests(TestCase): 76 | def test_detail_view_with_a_future_question(self): 77 | """ 78 | The detail view of a question with a pub_date in the future should 79 | return a 404 not found. 80 | """ 81 | future_question = create_question(question_text='Future question.', 82 | days=5) 83 | response = self.client.get(reverse('polls:detail', 84 | args=(future_question.id,))) 85 | self.assertEqual(response.status_code, 404) 86 | 87 | def test_detail_view_with_a_past_question(self): 88 | """ 89 | The detail view of a question with a pub_date in the past should 90 | display the question's text. 91 | """ 92 | past_question = create_question(question_text='Past Question.', 93 | days=-5) 94 | response = self.client.get(reverse('polls:detail', 95 | args=(past_question.id,))) 96 | self.assertContains(response, past_question.question_text, 97 | status_code=200) -------------------------------------------------------------------------------- /playing_with_API.py: -------------------------------------------------------------------------------- 1 | from polls.models import Question, Choice # Import the model classes we just wrote. 2 | 3 | 4 | Question.objects.all() 5 | 6 | # Create a new Question. 7 | # Support for time zones is enabled in the default settings file, so 8 | # Django expects a datetime with tzinfo for pub_date. Use timezone.now() 9 | # instead of datetime.datetime.now() and it will do the right thing. 10 | from django.utils import timezone 11 | q = Question(question_text="What's new?", pub_date=timezone.now()) 12 | 13 | # Save the object into the database. You have to call save() explicitly. 14 | q.save() 15 | 16 | # Now it has an ID. Note that this might say "1L" instead of "1", depending 17 | # on which database you're using. That's no biggie; it just means your 18 | # database backend prefers to return integers as Python long integer 19 | # objects. 20 | print(q.id) 21 | 22 | 23 | # Access model field values via Python attributes. 24 | print(q.question_text) 25 | print(q.pub_date) 26 | 27 | # Change values by changing the attributes, then calling save(). 28 | q.question_text = "What's up?" 29 | q.save() 30 | 31 | Question.objects.all() 32 | Question.objects.filter(id=1) 33 | Question.objects.filter(question_text__startswith='What') 34 | 35 | # Get the question that was published this year. 36 | from django.utils import timezone 37 | current_year = timezone.now().year 38 | Question.objects.get(pub_date__year=current_year) 39 | 40 | # Request an ID that doesn't exist, this will raise an exception. 41 | Question.objects.get(id=2) 42 | 43 | # Lookup by a primary key is the most common case, so Django provides a 44 | # shortcut for primary-key exact lookups. 45 | # The following is identical to Question.objects.get(id=1). 46 | Question.objects.get(pk=1) 47 | 48 | # Make sure our custom method worked. 49 | q = Question.objects.get(pk=1) 50 | q.was_published_recently() 51 | 52 | # Give the Question a couple of Choices. The create call constructs a new 53 | # Choice object, does the INSERT statement, adds the choice to the set 54 | # of available choices and returns the new Choice object. Django creates 55 | # a set to hold the "other side" of a ForeignKey relation 56 | # (e.g. a question's choice) which can be accessed via the API. 57 | q = Question.objects.get(pk=1) 58 | 59 | # Display any choices from the related object set -- none so far. 60 | q.choice_set.all() 61 | 62 | # Create three choices. 63 | q.choice_set.create(choice_text='Not much', votes=0) 64 | q.choice_set.create(choice_text='The sky', votes=0) 65 | c = q.choice_set.create(choice_text='Just hacking again', votes=0) 66 | 67 | # Choice objects have API access to their related Question objects. 68 | print(c.question) 69 | 70 | # And vice versa: Question objects get access to Choice objects. 71 | q.choice_set.all() 72 | q.choice_set.count() 73 | 74 | # The API automatically follows relationships as far as you need. 75 | # Use double underscores to separate relationships. 76 | # This works as many levels deep as you want; there's no limit. 77 | # Find all Choices for any question whose pub_date is in this year 78 | # (reusing the 'current_year' variable we created above). 79 | Choice.objects.filter(question__pub_date__year=current_year) 80 | 81 | # Let's delete one of the choices. Use delete() for that. 82 | c = q.choice_set.filter(choice_text__startswith='Just hacking') 83 | c.delete() 84 | 85 | import datetime 86 | from django.utils import timezone 87 | from polls.models import Question 88 | # create a Question instance with pub_date 30 days in the future 89 | future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30)) 90 | # was it published recently? 91 | future_question.was_published_recently() 92 | 93 | 94 | from django.test.utils import setup_test_environment 95 | setup_test_environment() 96 | 97 | from django.test import Client 98 | # create an instance of the client for our use 99 | client = Client() 100 | 101 | # get a response from '/' 102 | response = client.get('/') 103 | # we should expect a 404 from that address 104 | print(response.status_code) 105 | # on the other hand we should expect to find something at '/polls/' 106 | # we'll use 'reverse()' rather than a hardcoded URL 107 | from django.core.urlresolvers import reverse 108 | response = client.get(reverse('polls:index')) 109 | print(response.status_code) 110 | print(response.content) 111 | # note - you might get unexpected results if your ``TIME_ZONE`` 112 | # in ``settings.py`` is not correct. If you need to change it, 113 | # you will also need to restart your shell session 114 | from polls.models import Question 115 | from django.utils import timezone 116 | # create a Question and save it 117 | q = Question(question_text="Who is your favorite Beatle?", pub_date=timezone.now()) 118 | q.save() 119 | # check the response once again 120 | response = client.get('/polls/') 121 | print(response.content) 122 | # If the following doesn't work, you probably omitted the call to 123 | # setup_test_environment() described above 124 | print(response.context['latest_question_list']) 125 | --------------------------------------------------------------------------------