Skip to content

Instantly share code, notes, and snippets.

@mikpskov
Last active November 9, 2023 10:49
Show Gist options
  • Save mikpskov/7269d856c182bca75c62 to your computer and use it in GitHub Desktop.
Save mikpskov/7269d856c182bca75c62 to your computer and use it in GitHub Desktop.
Конспект учебника по Django

Источник

Быстрое руководство по установке

Последнюю версию Python можно найти на http://www.python.org/download

Установка официальной версии с помощью pip

pip install Django

Создаём своё первое приложение с Django, часть 1

Проверка версии Django

$ python -c "import django; print(django.get_version())"

Создание проекта

Создание нового проекта

$ django-admin.py startproject mysite

Настройка проекта mysite/settings.py

LANGUAGE_CODE = 'ru'
TIME_ZONE = 'Europe/Moscow'

Запуск основных миграций

$ python manage.py migrate

Запуск сервера для разработки

$ python manage.py runserver
$ python manage.py runserver 8080
$ python manage.py runserver 0.0.0.0:8000

По умолчанию сервер доступен по адресу localhost:8000

При добавление новых файлов необходимо перегрузить сервер.

Cоздание нового приложения в проекте

$ python manage.py startapp polls

Подключение приложения к проекту mysite/settings.py

INSTALLED_APPS = (
    # ...
    'polls',
)

Создание моделей

Модели создаются в polls/models.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)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

Создание миграции на основе моделей

$ python manage.py makemigrations polls

Просмотр того, что будет добавлено в базу из миграций

$ python manage.py sqlmigrate polls 0001

Поиск проблем в проекте не применяя миграции и не изменяя базу данных

$ python manage.py check

Применение миграции

$ python manage.py migrate

Тестирование API

$ python manage.py shell
>>> from polls.models import Question, Choice

# создать новый Question
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())

# сохранить объект в базе данных
>>> q.save()

# доступ к полям
>>> q.id
>>> q.question_text
>>> q.pub_date

# меняем значение поля
>>> q.question_text = "What's up?"
>>> q.save()

# показать все вопросы в базе
>> Question.objects.all()

Изменение формата вывода в моделях 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

Добавление своих методов 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)

Тестирование API

>>> from polls.models import Question, Choice
>>> Question.objects.all()

# найти записи по определенному ключу
>>> Question.objects.filter(id=1)
>>> Question.objects.filter(question_text__startswith='What')

>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)

# поиск по первичному ключу
>>> Question.objects.get(pk=1)

# проверяем метод
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()

# поиск по связанной таблице
>>> q = Question.objects.get(pk=1)
>>> q.choice_set.all()

# создание
>>> q.choice_set.create(choice_text='Not much', votes=0)
>>> q.choice_set.create(choice_text='The sky', votes=0)
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)

# проверяем родителя
>>> c.question

# и наоборот
>>> q.choice_set.all()
>>> q.choice_set.count()

# поиск по связанной таблице
>>> Choice.objects.filter(question__pub_date__year=current_year)

# удаление
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()

Создаём своё первое приложение с Django, часть 2

Создание суперпользователя

Создание суперпользователя

$ python manage.py createsuperuser

Теперь можно зайти в админку по адресу localhost:8000/admin и авторизироваться

Настройка формы

Регистрируем модели для админки polls/admin.py

from django.contrib import admin
from polls.models import Question

admin.site.register(Question)

Можно поменять порядок полей в админке

class QuestionAdmin(admin.ModelAdmin):
    fields = ['pub_date', 'question_text']

admin.site.register(Question, QuestionAdmin)

Можно разбить форму на группу полей

class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None,               {'fields': ['question_text']}),
        ('Date information', {'fields': ['pub_date']}),
    ]

Можно добавить HTML классы для каждой группы полей

class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None,               {'fields': ['question_text']}),
        ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
    ]

Добавление связанных объектов

from django.contrib import admin
from polls.models import Choice, Question
# ...
admin.site.register(Choice)

Теперь можно создавать choices и привязывать к ним questions

Но, удобнее будет добавлять варианты ответов пачками при создании Question:

from django.contrib import admin
from polls.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)

Теперь варианты ответов редактируются на странице вопроса. По умолчанию, будет отображваться 3 формы для добавления вариантов ответа.

Можно отображать их табами

class ChoiceInline(admin.TabularInline):

Настройка страницы списка объектов

По умолчанию Django отображает результат выполнения str() для каждого объекта. Но чаще всего хочется показывать список полей.

class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ('question_text', 'pub_date')

Можно нажать на заголовок колонки чтобы отсортировать записи по полю – но не для was_published_recently. Добавляем в polls/models.py:

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

Добавляем на страницу списка вопросов фильтры polls/admin.py(QuestionAdmin):

list_filter = ['pub_date']

Теперь добавим поиск:

search_fields = ['question_text']

Страница списка объектов также содержит постраничное отображение. По умолчанию отображается 100 объектов на страницу. Можно:

Настройка внешнего вид

Создаем папку, как указано в mysite/settings.py

TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')]

# TODO: почему то заработало только так:
TEMPLATE_DIRS = [
    os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),
]

Узнать, где лежат исходники

python -c "import sys; sys.path = sys.path[1:]; import django; print(django.__path__)"

Копируем base_site.html из встроенных шаблонов в исходниках Django

Меняем {{ site_header }}

При разработке проекта для этого обычно используют атрибут django.contrib.admin.AdminSite.site_header.

Любой шаблон интерфейса администратора можно переопределить.


Создаём своё первое приложение с Django, часть 3

Создание представления

Представления создаются в polls/views.py:

    from django.http import HttpResponse

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

Привязка представления к URL

Создать файл polls/urls.py:

from django.conf.urls import patterns, url
from polls import views

urlpatterns = patterns('',
    # ex: /polls/
    url(r'^$', views.index, name='index'),
)

Подключение модуля polls.urls в главном URLconf

Отредактировать файл mysite/urls.py:

from django.conf.urls import patterns, include, url
from django.contrib import admin

urlpatterns = patterns('',
    url(r'^polls/', include('polls.urls')),
    url(r'^admin/', include(admin.site.urls)),
)

Можно проверять: localhost:8000/polls

Создание еще пары представлений

Отредактировать файл 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)

Отредактировать файл polls/urls.py:

urlpatterns = patterns('',
    #...
    # ex: /polls/5/
    url(r'^(?P<question_id>\d+)/$', views.detail, name='detail'),
    # ex: /polls/5/results/
    url(r'^(?P<question_id>\d+)/results/$', views.results, name='results'),
    # ex: /polls/5/vote/
    url(r'^(?P<question_id>\d+)/vote/$', views.vote, name='vote'),
)

Добавление функционала в представления

Отредактировать polls/views.py

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    output = ', '.join([p.question_text for p in latest_question_list])
    return HttpResponse(output)

Создание файла шаблона

Создать polls/templates/polls/index.html

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

Именить представление index в polls/views.py

from django.http import HttpResponse
from django.template import RequestContext, loader

from polls.models import Question

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

Функция render()

Функция render() предназначна для объединения процесса загрузки шаблона, добавления контекста и возврат объекта HttpResponse.

from django.shortcuts import render

from polls.models import Question

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

Вызов 404 исключения

Теперь создадим страницу опроса, которая отображает вопрос и варианты ответа:

Добавляем в polls/views.py

from django.http import Http404
from django.shortcuts import render

# ...

def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404
    return render(request, 'polls/detail.html', {'question': question})

Функция get_object_or_404() упрощает обыденные операции:

from django.shortcuts import get_object_or_404, render

# ...

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

Добавляем polls/templates/polls/detail.html

<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

Для {{ question.question_text }} Django сначала пытается обратиться к question как к словарю. При неудаче ищется атрибут переменной, в данном случае он и используется. Если атрибут не найден, будет искаться индекс в списке.

Избавляемся от "хардкода" URL-ов в шаблонах

Так как мы указали названия при вызове url() в модуле polls.urls, мы можем ссылаться на шаблоны URL-ов используя шаблонный тег {% url %}:

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

Пространства имен в названиях URL-ов

Добавим пространство имен к polls в mysite/urls.py:

url(r'^polls/', include('polls.urls', namespace="polls")),

Теперь поменяем в шаблоне polls/index.html:

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment