Writing your first Django app, part 1¶
Let’s learn by example.
Throughout this tutorial, we’ll walk you through the creation of a basic
poll application.
It’ll consist of two parts:
- A public site that lets people view polls and vote in them.
- An admin site that lets you add, change, and delete polls.
We’ll assume you have Django installed already. You can
tell Django is installed and which version by running the following command
in a shell prompt (indicated by the $ prefix):
/
$ python -m django --version
...> py -m django --version
If Django is installed, you should see the version of your installation. If it
isn’t, you’ll get an error telling “No module named django”.
This tutorial is written for Django 4.2, which supports Python 3.8 and
later. If the Django version doesn’t match, you can refer to the tutorial for
your version of Django by using the version switcher at the bottom right corner
of this page, or update Django to the newest version. If you’re using an older
version of Python, check What Python version can I use with Django? to find a compatible
version of Django.
See How to install Django for advice on how to remove
older versions of Django and install a newer one.
Where to get help:
If you’re having trouble going through this tutorial, please head over to
the Getting Help section of the FAQ.
Creating a project¶
If this is your first time using Django, you’ll have to take care of some
initial setup. Namely, you’ll need to auto-generate some code that establishes a
Django project – a collection of settings for an instance of Django,
including database configuration, Django-specific options and
application-specific settings.
From the command line, cd
into a directory where you’d like to store your
code, then run the following command:
/
$ django-admin startproject mysite
...> django-admin startproject mysite
This will create a mysite
directory in your current directory. If it didn’t
work, see Problems running django-admin.
Note
You’ll need to avoid naming projects after built-in Python or Django
components. In particular, this means you should avoid using names like
django
(which will conflict with Django itself) or test
(which
conflicts with a built-in Python package).
Where should this code live?
If your background is in plain old PHP (with no use of modern frameworks),
you’re probably used to putting code under the web server’s document root
(in a place such as /var/www
). With Django, you don’t do that. It’s
not a good idea to put any of this Python code within your web server’s
document root, because it risks the possibility that people may be able
to view your code over the web. That’s not good for security.
Put your code in some directory outside of the document root, such as
/home/mycode
.
Let’s look at what startproject
created:
mysite/ manage.py mysite/ __init__.py settings.py urls.py asgi.py wsgi.py
These files are:
- The outer
mysite/
root directory is a container for your project. Its
name doesn’t matter to Django; you can rename it to anything you like. manage.py
: A command-line utility that lets you interact with this
Django project in various ways. You can read all the details about
manage.py
in django-admin and manage.py.- The inner
mysite/
directory is the actual Python package for your
project. Its name is the Python package name you’ll need to use to import
anything inside it (e.g.mysite.urls
). mysite/__init__.py
: An empty file that tells Python that this
directory should be considered a Python package. If you’re a Python beginner,
read more about packages in the official Python docs.mysite/settings.py
: Settings/configuration for this Django
project. Django settings will tell you all about how settings
work.mysite/urls.py
: The URL declarations for this Django project; a
“table of contents” of your Django-powered site. You can read more about
URLs in URL dispatcher.mysite/asgi.py
: An entry-point for ASGI-compatible web servers to
serve your project. See How to deploy with ASGI for more details.mysite/wsgi.py
: An entry-point for WSGI-compatible web servers to
serve your project. See How to deploy with WSGI for more details.
The development server¶
Let’s verify your Django project works. Change into the outer mysite
directory, if
you haven’t already, and run the following commands:
/
$ python manage.py runserver
...> py manage.py runserver
You’ll see the following output on the command line:
Performing system checks... System check identified no issues (0 silenced). You have unapplied migrations; your app may not work properly until they are applied. Run 'python manage.py migrate' to apply them. July 11, 2023 - 15:50:53 Django version 4.2, using settings 'mysite.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
Note
Ignore the warning about unapplied database migrations for now; we’ll deal
with the database shortly.
You’ve started the Django development server, a lightweight web server written
purely in Python. We’ve included this with Django so you can develop things
rapidly, without having to deal with configuring a production server – such as
Apache – until you’re ready for production.
Now’s a good time to note: don’t use this server in anything resembling a
production environment. It’s intended only for use while developing. (We’re in
the business of making web frameworks, not web servers.)
Now that the server’s running, visit http://127.0.0.1:8000/ with your web
browser. You’ll see a “Congratulations!” page, with a rocket taking off.
It worked!
Changing the port
By default, the runserver
command starts the development server
on the internal IP at port 8000.
If you want to change the server’s port, pass
it as a command-line argument. For instance, this command starts the server
on port 8080:
/
$ python manage.py runserver 8080
...> py manage.py runserver 8080
If you want to change the server’s IP, pass it along with the port. For
example, to listen on all available public IPs (which is useful if you are
running Vagrant or want to show off your work on other computers on the
network), use:
/
$ python manage.py runserver 0.0.0.0:8000
...> py manage.py runserver 0.0.0.0:8000
Full docs for the development server can be found in the
runserver
reference.
Automatic reloading of runserver
The development server automatically reloads Python code for each request
as needed. You don’t need to restart the server for code changes to take
effect. However, some actions like adding files don’t trigger a restart,
so you’ll have to restart the server in these cases.
Creating the Polls app¶
Now that your environment – a “project” – is set up, you’re set to start
doing work.
Each application you write in Django consists of a Python package that follows
a certain convention. Django comes with a utility that automatically generates
the basic directory structure of an app, so you can focus on writing code
rather than creating directories.
Projects vs. apps
What’s the difference between a project and an app? An app is a web
application that does something – e.g., a blog system, a database of
public records or a small poll app. 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.
Your apps can live anywhere on your Python path. In
this tutorial, we’ll create our poll app in the same directory as your
manage.py
file so that it can be imported as its own top-level module,
rather than a submodule of mysite
.
To create your app, make sure you’re in the same directory as manage.py
and type this command:
/
$ python manage.py startapp polls
...> py manage.py startapp polls
That’ll create a directory polls
, which is laid out like this:
polls/ __init__.py admin.py apps.py migrations/ __init__.py models.py tests.py views.py
This directory structure will house the poll application.
Write your first view¶
Let’s write the first view. Open the file polls/views.py
and put the following Python code in it:
polls/views.py
¶
from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the polls index.")
This is the simplest view possible in Django. To call the view, we need to map
it to a URL — and for this we need a URLconf.
To create a URLconf in the polls directory, create a file called urls.py
.
Your app directory should now look like:
polls/ __init__.py admin.py apps.py migrations/ __init__.py models.py tests.py urls.py views.py
In the polls/urls.py
file include the following code:
polls/urls.py
¶
from django.urls import path from . import views urlpatterns = [ path("", views.index, name="index"), ]
The next step is to point the root URLconf at the polls.urls
module. In
mysite/urls.py
, add an import for django.urls.include
and insert an
include()
in the urlpatterns
list, so you have:
mysite/urls.py
¶
from django.contrib import admin from django.urls import include, path urlpatterns = [ path("polls/", include("polls.urls")), path("admin/", admin.site.urls), ]
The include()
function allows referencing other URLconfs.
Whenever Django encounters include()
, it chops off whatever
part of the URL matched up to that point and sends the remaining string to the
included URLconf for further processing.
The idea behind include()
is to make it easy to
plug-and-play URLs. Since polls are in their own URLconf
(polls/urls.py
), they can be placed under “/polls/”, or under
“/fun_polls/”, or under “/content/polls/”, or any other path root, and the
app will still work.
When to use include()
You should always use include()
when you include other URL patterns.
admin.site.urls
is the only exception to this.
You have now wired an index
view into the URLconf. Verify it’s working with
the following command:
/
$ python manage.py runserver
...> py manage.py runserver
Go to http://localhost:8000/polls/ in your browser, and you should see the
text “Hello, world. You’re at the polls index.”, which you defined in the
index
view.
Page not found?
If you get an error page here, check that you’re going to
http://localhost:8000/polls/ and not http://localhost:8000/.
The path()
function is passed four arguments, two required:
route
and view
, and two optional: kwargs
, and name
.
At this point, it’s worth reviewing what these arguments are for.
path()
argument: route
¶
route
is a string that contains a URL pattern. When processing a request,
Django starts at the first pattern in urlpatterns
and makes its way down
the list, comparing the requested URL against each pattern until it finds one
that matches.
Patterns don’t search GET and POST parameters, or the domain name. For example,
in a request to https://www.example.com/myapp/
, the URLconf will look for
myapp/
. In a request to https://www.example.com/myapp/?page=3
, the
URLconf will also look for myapp/
.
path()
argument: view
¶
When Django finds a matching pattern, it calls the specified view function with
an HttpRequest
object as the first argument and any
“captured” values from the route as keyword arguments. We’ll give an example
of this in a bit.
path()
argument: kwargs
¶
Arbitrary keyword arguments can be passed in a dictionary to the target view. We
aren’t going to use this feature of Django in the tutorial.
path()
argument: name
¶
Naming your URL lets you refer to it unambiguously from elsewhere in Django,
especially from within templates. This powerful feature allows you to make
global changes to the URL patterns of your project while only touching a single
file.
When you’re comfortable with the basic request and response flow, read
part 2 of this tutorial to start working with the
database.
Напишем простое веб-приложение на Django.
Джанго — это Open Source фреймворк для создания веб-приложений различной сложности на Python. Одним из его основных преимуществ является то, что вам нужно позаботиться только о логике будущего веб-приложения, остальное сделает Django.
Мы создадим приложение, у которого будет панель администратора и возможность загружать загадки, а у пользователей, соответственно, возможность отвечать на них. Во время разработки будут использоваться Python 3.4.3 и Django 1.9.1.
- Устанавливаем Django
- Создаём проект
Устанавливаем Django
Делается это очень просто, в командной строке нужно написать: pip install Django==1.9.1
.
Создаём проект
Если вы правильно установили Django, то после запуска django-admin --version
вы увидите текущую версию фреймворка. Теперь создадим проект. Это можно сделать следующим образом: django-admin startproject django_example
.
Как только создание проекта будет завершено, взглянем на директорию нашего проекта:
- django_example/__init__.py — пустой файл, который говорит Python, что данная директория должна восприниматься в качестве пакета.
- django_example/settings.py содержит конфигурацию нашего проекта.
- django_example/urls.py — здесь объявляются URL.
- django_example/wsgi.py — с помощью него приложение может работать с веб-сервером по протоколу WSGI.
- manage.py позволяет взаимодействовать с проектом.
Теперь пришло время запустить наше приложение. Для этого в командной строке нужно написать python manage.py runserver
. После этого в адресной строке браузера нужно написать: http://127.0.0.1:8000/
. Если вы увидели «You have unapplied migrations; your app may not work properly until they are applied.», то не волнуйтесь, мы вернемся к этому чуть позже.
Пишем веб-приложение на Django
Определим различие между проектом и приложением. Приложение — это программа, которая что-то делает, а проект — это группа приложений.
Итак, приступим к созданию приложения. Это делается следующим образом: python manage.py startapp riddles
.
Как только создано веб-приложение, напишем вид, по правилам Джанго все виды должны храниться в файле views.py
.
riddles/views.py
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, World!")
Теперь, чтобы привязать наш вид к URL, создадим файл urls.py
.
riddles/urls.py
from django.conf.urls import url
from . import views
app_name = 'riddles'
urlpatterns = [
url(r'^$', views.index, name='index'),
]
В urls.py
мы должны написать следующее:
django_example/urls.py
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^riddles/', include('riddles.urls')),
url(r'^admin/', admin.site.urls),
]
Теперь, если мы запустим наше приложение http://127.0.0.1:8000/riddles/
, мы увидим «Hello, World!».
Установка базы данных
По умолчанию в Django используется SQLite, если она вас не устраивает, то вы можете ознакомиться с нашей статьей, в которой мы рассказываем, как безболезненно перейти с SQLite на MySQL.
Теперь откроем django_example/settings.py
и взглянем на переменную INSTALLED_APPS
, она хранит все приложения, которые активны в текущем проекте. По умолчанию она содержит:
- django.contrib.admin — админка, скоро мы ей воспользуемся.
- django.contrib.auth — система аутентификации.
- django.contrib.contenttypes — фреймворк для content types.
- django.contrib.sessions — сессионный фреймворк.
- django.contrib.messages — фреймворк для отправки сообщений.
- django.contrib.staticfiles — фреймворк для работы со статичными файлами.
Некоторые из этих приложений используют базы данных, но они еще не установлены, поэтому мы и видели «You have unapplied migrations; your app may not work properly until they are applied.». Поправить это можно следующим образом: python manage.py migrate
. Вы должны увидеть следующее:
Operations to perform:
Apply all migrations: admin, sessions, auth, contenttypes
Running migrations:
Rendering model states... DONE
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying sessions.0001_initial... OK
Теперь создадим нашу модель. Для начала создадим Riddle
и Option
. В Riddle
будет содержаться загадка, в Option
— один из возможных ответов на нее.
riddles/models.py
from django.db import models
class Riddle(models.Model):
riddle_text = models.CharField(max_length=255)
pub_date = models.DateTimeField('date published')
class Option(models.Model):
riddle = models.ForeignKey(Riddle, on_delete=models.CASCADE)
text = models.CharField(max_length=255)
correct = models.BooleanField(default=False)
Данная модель обеспечивает Django информацией, необходимой для создания схемы базы данных и database-access API для доступа к объектам. Теперь нам нужно привязать наше приложение к нашему проекту, делается это следующим образом:
django_example/settings.py
INSTALLED_APPS = [
'riddles.apps.RiddlesConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
После этого нужно сделать миграцию: python manage.py makemigrations riddles
. Вы должны увидеть следующее:
Migrations for 'riddles':
0001_initial.py:
- Create model Option
- Create model Riddle
- Add field riddle to option
Так мы говорим Django, что в моделях были сделаны некоторые изменения, и их нужно сохранить в качестве миграции.
Проверить, что сделает миграция, можно так: python manage.py sqlmigrate riddles 0001
(0001 — версия миграции, которую мы хотим проверить). На выходе мы получим:
BEGIN;
--
-- Create model Option
--
CREATE TABLE "riddles_option" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "text" varchar(255) NOT NULL, "correct" bool NOT NULL);
--
-- Create model Riddle
--
CREATE TABLE "riddles_riddle" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "riddle_text" varchar(255) NOT NULL, "pub_date" datetime NOT NULL);
--
-- Add field riddle to option
--
ALTER TABLE "riddles_option" RENAME TO "riddles_option__old";
CREATE TABLE "riddles_option" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "text" varchar(255) NOT NULL, "correct" bool NOT NULL, "riddle_id" integer NOT NULL REFERENCES "riddles_riddle" ("id"));
INSERT INTO "riddles_option" ("riddle_id", "id", "text", "correct") SELECT NULL, "id", "text", "correct" FROM "riddles_option__old";
DROP TABLE "riddles_option__old";
CREATE INDEX "riddles_option_a7c97949" ON "riddles_option" ("riddle_id");
COMMIT;
Заметьте, что команда sqlmigrate
нужна только для проверки, каждый раз ее запускать необязательно.
Теперь мы можем начать пользоваться панелью администратора. Но для этого нам нужен пользователь. Создать его можно следующим образом: python manage.py createsuperuser
. После этого запускаем сервер, если он не запущен, и переходим на http://127.0.0.1:8000/admin/
. Вы увидите следующее:
Теперь дадим админу возможность изменять наши модели. Делается это так:
riddles/admin.py
from django.contrib import admin
from .models import Option, Riddle
admin.site.register(Riddle)
admin.site.register(Option)
Вот что получится в итоге:
Возьмите небольшую паузу и поиграйтесь с панелью администратора. Вы будете приятно удивлены тем, что умеет Джанго.
Главная страница
Что нам нужно для создания главной страницы?
- Templates: скелет нашей страницы.
- Views: функция на Python для отображения контента.
Начнем с шаблонов. Создадим папку templates
внутри папки riddle
, а в ней создадим index.html
.
riddles/templates/index.html
<h1>Available Riddles</h1>
{% if message %}
<p><strong>{{ message }}</strong></p>
{% endif %}
{% if latest_riddles %}
<ul>
{% for riddle in latest_riddles %}
<li>
<a href="/riddles/{{ riddle.id }}/">
{{ riddle.riddle_text }}
</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>No riddles are available right now.</p>
{% endif %}
Теперь создадим макет для ответов:
riddles/templates/answer.html
<h1>{{ riddle.riddle_text }}</h1>
{% if error_message %}
<p>
<strong>{{ error_message }}</strong>
</p>
{% endif %}
<form action="answer' riddle.id %}" method="post">
{% csrf_token %}
{% for option in riddle.option_set.all %}
<input type="radio" name="option" id="option{{ forloop.counter }}" value="{{ option.id }}" />
<label for="option{{ forloop.counter }}">{{ option.text }}</label><br>
{% endfor %}
<input type="submit" value="Answer" />
</form>
Здесь мы используем csrf_token
, он нужен для защиты от межсайтовой подделки запроса, каждая внутренняя форма должна его использовать. Теперь напишем виды для рендеринга наших шаблонов:
riddles/views.py
from django.http.response import HttpResponse
from django.shortcuts import get_object_or_404, render
from .models import Riddle, Option
def index(request):
return render(request, "index.html", {"latest_riddles": Riddle.objects.order_by('-pub_date')[:5]})
def detail(request, riddle_id):
return render(request, "answer.html", {"riddle": get_object_or_404(Riddle, pk=riddle_id)})
def answer(request, riddle_id):
riddle = get_object_or_404(Riddle, pk=riddle_id)
try:
option = riddle.option_set.get(pk=request.POST['option'])
except (KeyError, Option.DoesNotExist):
return render(request, 'answer.html', {'riddle': riddle, 'error_message': 'Option does not exist'})
else:
if option.correct:
return render(request, "index.html", {"latest_riddles": Riddle.objects.order_by('-pub_date')[:5], "message": "Nice! Choose another one!"})
else:
return render(request, 'answer.html', {'riddle': riddle, 'error_message': 'Wrong Answer!'})
Давайте пройдемся по каждой функции веб-приложения на Django отдельно:
- index: Index использует функцию
render
. На вход она получает HttpRequest, местонахождение шаблона и его содержимое, а возвращает HttpResponse с окончательным html. - detail: Detail делает практически то же самое, но только функция
get_object_or_404
возвращает HttpResponse404, если нужный объект не был найден. - answer: Answer ищет предоставленную загадку (и возвращает 404, если она не найдена) и проверяет правильность ответа.
Теперь добавим наши функции в urls.py
:
riddles/urls.py
from django.conf.urls import url
from . import views
app_name = 'riddles'
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^(?P[0-9]+)/$', views.detail, name='detail'),
url(r'^(?P[0-9]+)/answer/$', views.answer, name='answer')
]
Стили
Для начала создадим директорию static
, а в ней создадим файл main.css
.
riddles/static/main.css
body{
margin:40px auto;
max-width:650px;
line-height:1.6;
font-size:18px;
color:#444;
padding:0 10px;
}
h1,h2,h3{
line-height:1.2;
text-align: center;
}
a {
color: blue;
}
form {
margin: 0 auto;
padding: 1em;
border: 1px solid #CCC;
border-radius: 1em;
}
form div + div {
margin-top: 1em;
}
label {
display: inline-block;
text-align: center;
width: 40%;
}
input {
font: 1em sans-serif;
-moz-box-sizing: border-box;
box-sizing: border-box;
border: 1px solid #999;
width: 50%;
}
input:focus {
border-color: #000;
}
p, div.button {
text-align: center;
}
p.error-message {
color: lightcoral;
}
Немного изменим наши шаблоны:
riddles/templates/index.html
{% load staticfiles %}
<link rel="stylesheet" type="text/css" href="{% static 'main.css' %}" />
<h1>Available Riddles</h1>
{% if message %}
<p><strong>{{ message }}</strong></p>
{% endif %}
{% if latest_riddles %}
<ul>
{% for riddle in latest_riddles %}
<li>
<a href="/riddles/{{ riddle.id }}/">
{{ riddle.riddle_text }}
</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>No riddles are available right now.</p>
{% endif %}
riddles/templates/answer.html
{% load staticfiles %}
<link rel="stylesheet" type="text/css" href="{% static 'main.css' %}" />
<h1>{{ riddle.riddle_text }}</h1>
{% if error_message %}
<p>
<strong>{{ error_message }}</strong>
</p>
{% endif %}
<form action="answer' riddle.id %}" method="post">
{% csrf_token %}
{% for option in riddle.option_set.all %}
<input type="radio" name="option" id="option{{ forloop.counter }}" value="{{ option.id }}" />
<label for="option{{ forloop.counter }}">{{ option.text }}</label><br>
{% endfor %}
<input type="submit" value="Answer" />
</form>
Первая строка загружает статические файлы, потом мы используем {% static '#' %}
, где # — путь к вашему файлу. Аналогичная процедура проводится и для JavaScript.
Теперь вы можете создавать свои веб-приложения на Django. В качестве подсказки на старте работы с фреймворком воспользуйтесь одной из наших шпаргалок по Python.
Исходный код нашего приложения можно скачать по этой ссылке.
Если этот веб-проект на Django показался сложным, попробуйте пройти двухчасовой видеокурс. На нём вы пошагово создадите 3 веб-приложения: сокращатель ссылок, ToDo List и словарь английских слов.
Перевод статьи «Python Django Tutorial»
Публикация представляет собой незначительно сокращенное пособие Дэйна Хилларда Build a Blog Using Django, Vue, and GraphQL.
***
Это руководство проведет вас через процесс создания серверной части на Django и клиентской части на Vue со связкой между ними в виде GraphQL. Это большой пошаговый проект, делайте перерывы по мере необходимости.
Из руководства вы узнаете:
- Как транслировать модели Django в GraphQL API.
- Как одновременно запустить сервер Django и приложение на Vue.
- Как администрировать Django-проект.
- Как использовать GraphQL API для отображения данных в браузере с помощью Vue.
Традиционно стартовым проектом для веба явлется блог — такие проекты включают все стандартные CRUD-операции: создание, чтение, обновление и удаление.
Мы разработаем небольшое приложение для ведения блога с некоторыми элементарными функциями. Авторы могут писать множество сообщений, сообщения могут иметь множество тегов, могут публиковаться или сохраняться в черновиках.
Бэкенд блога мы сделаем на Django, затем реализуем передачу контента GraphQL API и, наконец, воспользуемся Vue для отображения данных в браузере. Вот наши шаги:
- Настроить блог Django.
- Создать администратора блога Django.
- Настроить Graphene-Django.
- Настроить django-cors-headers.
- Настроить Vue.js.
- Настроить Vue Router.
- Создать компоненты Vue.
- Получить и отобразить даные.
Предварительные знания
Руководство будет легче воспринять, если вы уже знакомы с концепцией веб-приложений: как работают HTTP-запросы и API. В этом плане будет полезно прочитать нашу публикацию про Python и API.
Поскольку мы будем использовать для создания серверной части блога Django, хорошо бы ознакомиться с процедурой запуска проекта на Django. Возможно, стоит для начала попробовать создать проект на чистом Django.
Поскольку мы будем использовать для интерфейса пользователя Vue, будет полезен опыт работы с реактивным JavaScript. Если в прошлом вы манипулировали DOM-элементами только с помощью jQuery, знакомство с Vue станет хорошим продолжением.
Запросы GraphQL похожи на JSON-объекты и возвращают данные в формате JSON. Поэтому полезно разобраться, что это такое. Позже в этом руководстве вам потребуется установить Node.js, прочитайте наше руководство для новичков.
Шаг 1. Настраиваем Django
Создадим каталог, в котором будем хранить код проекта. Назовем его dvg
, сокращенно от Django-Vue-GraphQL:
mkdir dvg/
cd dvg/
Мы будем разделять фронтенд и бэкенд-код, так что неплохо сразу создать подкаталоги для бэкенда:
mkdir backend/
cd backend/
Весь код Django мы поместим в каталог backend
, полностью изолировав его от кода Vue.
Устанавливаем Django
Чтобы отделить зависимости проекта от других ваших проектов, создадим виртуальное окружение. Далее в руководстве предполагается, что вы запускате команды, связанные с Python и Django, в активированном виртуальном окружении. Для установки зависимостей создадим в директории backend
файл requirements.txt
:
Django==3.1.7
Устанавливаем Django в виртуальном окружении:
(venv) $ python -m pip install -r requirements.txt
Django установлен, инициализируем проект:
(venv) $ django-admin startproject backend .
Команда создаст в каталоге backend
модуль manage.py
и внутренний пакет backend
. Структура каталогов теперь выглядит так:
dvg
└── backend
├── manage.py
├── requirements.txt
└── backend
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
Мы создали новый проект, а значит, нужно создать базу данных и осуществить миграцию:
(venv) $ python manage.py migrate
Вы увидите список миграций:
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying sessions.0001_initial... OK
Это создаст файл базы данных SQLite с именем db.sqlite3
, в котором будут храниться данные нашего проекта.
Есть база данных — можем создать суперпользователя:
(venv) $ python manage.py createsuperuser
Итак, мы установили Django, создали проект, провели миграции и добавили суперпользователя. В итоге у нас есть полностью функционирующее (пусть пока и пустое) приложение. Запускается оно так:
(venv) $ python manage.py runserver
Результат можно посмотреть по адресу http://localhost:8000/
. Вы увидите стартовую страницу пустого Django-приложения. Кроме того, станет доступна страница http://localhost:8000/admin
, с помощью которой можно администрировать проект. Чтобы попасть внутрь, используйте логин и пароль суперпользователя.
Шаг 2. Создаем приложение
Проект на Django может содержать множество различных приложений. Обычно одно приложение соответствует одному смысловому блоку сайта, например, ленте новостей, магазину товаров или корзине. Создадим приложение блога:
(venv) $ python manage.py startapp blog
Будет создана директория blog
с несколькими шаблонными файлами:
blog
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
Позже в руководстве мы их дополним.
Вновь созданное приложение не добавляется по умолчанию в проект. Чтобы фреймворк знал, что приложение является частью проекта, дополняем список приложений в файле настроек проекта backend/settings.py
:
INSTALLED_APPS = [
...
"blog",
]
Это поможет Django найти информацию о приложении: модели данных и шаблоны URL-адресов.
Создаем модели данных для блога
Создадим три следующие модели данных:
Profile
хранит информацию о пользователях блога.Tag
содержит данные о категориях, по которым группируются записи блога.Post
используется для хранения контента и метаданных о каждом посте блога.
Все модели добавляются в соответствующий файл приложения blog/models.py
. Каждая модель наследуется от стандартных моделей Django:
from django.db import models
Модель Profile
Модель профиля содержит несколько полей:
user
— связь с пользователем Django (связь один-к-одному).website
— опциональный URL, по которому можно узнать больше о пользователе.bio
— опциональное небольшое био («о себе»).
Импортируем из Django модуль настроек settings
и опишем класс для нашей новой модели:
from django.conf import settings
class Profile(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT,
)
website = models.URLField(blank=True)
bio = models.CharField(max_length=240, blank=True)
def __str__(self):
return self.user.get_username()
Метод __str__
сделает удобнее отображение профилей в панели администратора .
Модель Tag
В модели Tag
будет единственное поле, короткое имя тега:
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.name
Модель Post
Модель Post
— самая сложная, содержит множество полей: заголовок (title
), подзаголовок (subtitle
), слаг (slug
, уникальная часть URL для нашего поста), контент поста (body
) и т.д.
class Post(models.Model):
class Meta:
ordering = ["-publish_date"]
title = models.CharField(max_length=255, unique=True)
subtitle = models.CharField(max_length=255, blank=True)
slug = models.SlugField(max_length=255, unique=True)
body = models.TextField()
meta_description = models.CharField(max_length=150, blank=True)
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
publish_date = models.DateTimeField(blank=True, null=True)
published = models.BooleanField(default=False)
author = models.ForeignKey(Profile, on_delete=models.PROTECT)
tags = models.ManyToManyField(Tag, blank=True)
Некоторые пояснения:
- В подклассе
Meta
мы указываем порядок сортировки постов (ordering
) по дате публикации. - Аргумент
on_delete = models.PROTECT
для поляauthor
гарантирует, что при удалении постов мы случайно не удалим автора. - Каждый тег может быть связан со многими сообщениями, поэтому для поля
tags
используется отношениеManyToManyField
.
Конфигурируем панель администратора
Для того, чтобы определить, как будут отображаться записи в панели администрирования блога, переходим в blog/admin.py
и импортируем созданные модели:
from django.contrib import admin
from blog.models import Profile, Post, Tag
Создаем и регистрируем классы моделей:
@admin.register(Profile)
class ProfileAdmin(admin.ModelAdmin):
model = Profile
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
model = Tag
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
model = Post
list_display = (
"id",
"title",
"subtitle",
"slug",
"publish_date",
"published",
)
list_filter = (
"published",
"publish_date",
)
list_editable = (
"title",
"subtitle",
"slug",
"publish_date",
"published",
)
search_fields = (
"title",
"subtitle",
"slug",
"body",
)
prepopulated_fields = {
"slug": (
"title",
"subtitle",
)
}
date_hierarchy = "publish_date"
save_on_top = True
У постов мы не показываем все поля подряд, а только необходимые для администрирования. К ним мы добавили возможности фильтрации, редактирования и поиска. Подробно эти настройки рассмотрены в статье Customize the Django Admin With Python.
Создаем миграции модели
В Django есть вся информация, необходимая для управления содержимым сайта, но сначала нужно обновить базу данных. Ранее в этом руководстве мы запускали миграции Django для встроенных моделей. Теперь создадим и запустим миграции уже для наших моделей:
(venv) $ python manage.py makemigrations
Migrations for 'blog':
blog/migrations/0001_initial.py
- Create model Tag
- Create model Profile
- Create model Post
Это создаст миграцию с именем по умолчанию 0001_initial.py
. Запустим миграцию с помощью команды управления migrate
:
(venv) $ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
Applying blog.0001_initial... OK
Теперь у нас есть модели данных и мы настроили админпанель Django, чтобы добавлять и редактировать эти модели.
Запустите или перезапустите сервер разработки Django и зайдите в панель по адресу http://localhost:8000/admin
посмотреть, что изменилось. Вы увидите ссылки на списки тегов, профилей и сообщений, а также ссылки для добавления и редактирования каждого из них. Добавьте и отредактируйте несколько из них, чтобы увидеть, как отреагирует интерфейс администратора.
Шаг 3. Настройка Graphene-Django
В результате предыдущего этапа мы завершили основную работу над бэкендом. Далее можно было бы использовать механизмы маршрутизации URL и шаблонов Django для создания страниц, которые будут показывать читателям контент. Но вместо этого мы обернем созданную нами серверную часть в GraphQL API. За счет этого мы обеспечим более удобную работу на стороне клиента.
GraphQL позволяет получать только те данные, которые нам действительно нужны, что выгодно отличает эту технологию от RESTful API. GraphQL обеспечивает гибкость при проектировании данных, за счет чего мы можем получать новые структуры данных, не изменяя логику службы, предоставляющей GraphQL API.
Устанавливаем Graphene-Django
Для интеграции Django и GraphQL мы используем библиотеку Graphene-Django. Для установки библиотеки дополняем файл requirements.txt
:
graphene-django==2.14.0
Запускаем установку через менеджер пакетов pip:
(venv) $ python -m pip install -r requirements.txt
Теперь нужно добавить приложение "graphene_django"
в список INSTALLED_APPS
в модуле settings.py
:
INSTALLED_APPS = [
...
"blog",
"graphene_django",
]
Настраиваем Graphene-Django
Параметр GRAPHENE
в файле settings.py
указывает Graphene-Django расположение схемы GraphQL. Для нашего примера этот путь соответствует blog.schema.schema
(саму схему мы вскоре создадим):
GRAPHENE = {
"SCHEMA": "blog.schema.schema",
}
Добавляем шаблон URL для GraphQL и GraphiQL. Чтобы позволить Django обслуживать конечную точку GraphQL и интерфейс GraphiQL, добавим новый шаблон URL в backend/urls.py
. Поскольку мы не используем функции защиты от подделки межсайтовых запросов (CSRF) шаблонизатора Django, нам необходимо импортировать декоратор Django csrf_exempt
, чтобы пометить представление, как свободное от CSRF-защиты:
from django.views.decorators.csrf import csrf_exempt
from graphene_django.views import GraphQLView
Добавляем паттерн в список переменной urlpatterns
:
urlpatterns = [
...
path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True))),
]
Аргумент graphiql = True
указывает Graphene-Django сделать доступным GraphiQL-интерфейс .
Создаем GraphQL-схему. Теперь мы создадим схему GraphQL, похожую по своей логике на конфигурацию панели администратора. Схема состоит из нескольких классов, каждый из которых связан с определенной моделью Django, а также ещё одного класса, который показывает, как разрешать несколько важных типов запросов, которые понадобятся нам во внешнем интерфейсе.
В каталоге blog/
создадим новый модуль schema.py
. Импортируем из Graphene-Django DjangoObjectType
, модели блога и модель пользователя Django:
from django.conf import settings
from graphene_django import DjangoObjectType
from blog import models
Создадим класс для каждой из наших моделей и модели User
. Имя каждого класса должно заканчиваться на Type
, потому что каждое из них соответствует типу GraphQL. Классы должны выглядеть следующим образом:
class UserType(DjangoObjectType):
class Meta:
model = settings.AUTH_USER_MODEL
class AuthorType(DjangoObjectType):
class Meta:
model = models.Profile
class PostType(DjangoObjectType):
class Meta:
model = models.Post
class TagType(DjangoObjectType):
class Meta:
model = models.Tag
Ещё нам нужно создать класс Query
, наследуемый от graphene.ObjectType
. Этот класс объединит все созданные нами классы типов, и мы добавим к нему методы, указывающие способы запроса моделей. Сначала импортируем модуль graphene
:
import graphene
Класс Query
требует ряда атрибутов, которые являются либо graphene.List
, (если запрос возращает несколько элементов), либо graphene.Field
(если запрос возвращает один элемент).
Для каждого из атрибутов мы создадим метод решения запроса. Мы разрешаем запрос, беря информацию, предоставленную в запросе, и возвращая в ответ соответствующий запрос Django. Метод каждого преобразователя должен начинаться с resolve_
, а остальная часть имени должна соответствовать атрибуту. Например, метод разрешения запросов для атрибута all_posts
должен называться resolve_all_posts
.
В итоге получается следующий сниппет:
class Query(graphene.ObjectType):
all_posts = graphene.List(PostType)
author_by_username = graphene.Field(AuthorType, username=graphene.String())
post_by_slug = graphene.Field(PostType, slug=graphene.String())
posts_by_author = graphene.List(PostType, username=graphene.String())
posts_by_tag = graphene.List(PostType, tag=graphene.String())
def resolve_all_posts(root, info):
return (
models.Post.objects.prefetch_related("tags")
.select_related("author")
.all()
)
def resolve_author_by_username(root, info, username):
return models.Profile.objects.select_related("user").get(
user__username=username
)
def resolve_post_by_slug(root, info, slug):
return (
models.Post.objects.prefetch_related("tags")
.select_related("author")
.get(slug=slug)
)
def resolve_posts_by_author(root, info, username):
return (
models.Post.objects.prefetch_related("tags")
.select_related("author")
.filter(author__user__username=username)
)
def resolve_posts_by_tag(root, info, tag):
return (
models.Post.objects.prefetch_related("tags")
.select_related("author")
.filter(tags__name__iexact=tag)
)
Теперь у нас есть все типы и преобразователи для нашей схемы. Но помним, что переменная GRAPHENE
указывает на blog.schema.schema
. Создаем переменную схемы, которая обертывает класс Query
в graphene.Schema
, чтобы связать все это вместе:
schema = graphene.Schema(query=Query)
В результатае переменная соответствует значению blog.schema.schema
, которое мы настроили для Graphene-Django ранее в этом руководстве.
Итак, мы обернули модель данных с помощью Graphene-Django, чтобы использовать эти данные в GraphQL API. Запустите сервер разработки Django и посетите страницу http://localhost:8000/graphql
. Вы должны увидеть интерфейс GraphiQL с некоторыми комментариями, объясняющими, как использовать инструмент.
Разверните раздел Docs
в правом верхнем углу экрана и щелкните по query:Query
. Вы должны увидеть каждый из запросов и типов, которые мы настроили в схеме.
Если вы еще не наполняли блог тестовыми данными, сделайте это сейчас. Попробуйте выполнить следующий запрос. Он должен вернуть список всех созданных сообщений:
{
allPosts {
title
subtitle
author {
user {
username
}
}
tags {
name
}
}
}
Ответ должен вернуть список постов. Структура каждого поста должна соответствовать форме запроса, как в следующем примере:
{
"data": {
"allPosts": [
{
"title": "The Great Coney Island Debate",
"subtitle": "American or Lafayette?",
"author": {
"user": {
"username": "coney15land"
}
},
"tags": [
{
"name": "food"
},
{
"name": "coney island"
}
]
}
]
}
}
Если вы сохранили несколько постов и видите их в ответе, значит, можно продолжать.
Шаг 4. Настраиваем django-cors-headers
Чтобы считать работу над бэкендом завершенной, сделаем еще один шаг. Серверная часть и интерфейс будут запускаются на разных портах, а на практике так и вообще могут запускаться на разных доменах. Поэтому важное значение принимает вопрос совместного использования ресурсов (CORS). Без поддержки CORS запросы от фронтенда к бэкенду обычно блокируются браузером.
Библиотека django-cors-headers
делает работу с CORS довольно безболезненной. Мы будем использовать эту библиотеку, чтобы указать Django отвечать на запросы, даже если они исходят из другого источника. Это позволит фронтенду правильно взаимодействовать с GraphQL API.
Установка. Добавляем название модуля в зависимости (requirements.txt
):
django-cors-headers==3.6.0
Устанавливаем:
(venv) $ python -m pip install -r requirements.txt
Добавляем в список приложений INSTALLED_APPS
в файле settings.py
:
INSTALLED_APPS = [
...
"corsheaders",
]
Теперь нужно подключить библиотеку в качестве промежуточного обработчика в переменной MIDDLEWARE
:
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware",
...
]
Документация django-cors-headers
советует ставить эту строку как можно выше в списке обработчиков.
CORS существует не просто так. Мы не хотим, чтобы наше приложение было доступно для использования из любого места в Интернете. Чтобы этого избежать, мы используем две настройки, чтобы определить, насколько мы хотим открыть GraphQL API:
CORS_ORIGIN_ALLOW_ALL
определяет, должен ли Django быть полностью открыт или полностью закрыт по умолчанию.CORS_ORIGIN_WHITELIST
определяет, для каких доменов приложение Django будет разрешать запросы.
Соответственно добавляем в settings.py
две следующие строки:
CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = ["http://localhost:8080"]
Такие настройки разрешат запросы только от нашего фронтенда, которые в конечном итоге мы будем запускать локально на порту 8080.
Бэкенд готов! У нас есть модель данных, интерфейс администратора, GraphQL API на базе GraphiQL и возможность запрашивать API из внешнего интерфейса, который мы создадим дальше. Отличный момент, чтобы передохнуть, если вы ещё этого не делали ⛱️.
Шаг 5. Настраиваем Vue.js
В качестве фронтенд-фреймворка мы будем использовать Vue. Как и Django, Vue предоставляет интерфейс для создания проекта. Используя этот подход, нам не придется устанавливать вручную множество отдельных зависимостей, необходимых для запуска проекта на Vue. Достаточно использовать npx:
$ cd путь_к_директории_с_проектом
$ npx @vue/cli create frontend --default
...
🎉 Successfully created project frontend.
...
$ cd frontend/
Указанная комана создаст подкаталог frontend/
рядом с уже существующим каталогом backend/
, установит ряд зависимостей JavaScript и создаст некоторые каркасные файлы для нашего будущего фронтенд-приложения.
Установим плагины Vue
Чтобы правильно выполнять маршрутизацию и взаимодействовать с GraphQL API, нам понадобятся плагины Vue Router и Vue Apollo. При появлении запросов выбирайте параметры по умолчанию:
$ npx @vue/cli add router
$ npx @vue/cli add apollo
Этим командам потребуется время для установки зависимостей, они добавят или изменят некоторые файлы в проекте. Теперь мы можем запустить сервер разработки:
$ npm run serve
Итак, у нас есть приложение Django, работающее по адресу http://localhost:8000, и приложение Vue, которое запускается по адресу http://localhost:8080.
Шаг 6. Настраиваем Vue Router
Важной частью клиентских приложений является обработка маршрутизации без необходимости делать новые запросы к серверу. Распространенным решением в Vue является плагин Vue Router, который мы установили ранее.
В каталоге src/
создадим модуль router.js
. Этот файл будет содержать настройки сопоставления URL-адресов и компонентов Vue. Начнем с импорта Vue
и Vue Router
:
import Vue from 'vue'
import VueRouter from 'vue-router'
Каждый из следующих элементов импорта соответствует нашим будущим компонентам:
import Post from '@/components/Post'
import Author from '@/components/Author'
import PostsByTag from '@/components/PostsByTag'
import AllPosts from '@/components/AllPosts'
Регистрируем плагин:
Vue.use(VueRouter)
Теперь создадим список маршрутов. Каждый маршрут имеет два параметра:
path
— URL-шаблон, похожий по смыслу на URL-шаблоны Django.component
— компонент Vue, соответствующий пути.
Создадим константу routes
:
const routes = [
{ path: '/author/:username', component: Author },
{ path: '/post/:slug', component: Post },
{ path: '/tag/:tag', component: PostsByTag },
{ path: '/', component: AllPosts },
]
Создадим новый экземпляр VueRouter
и экспортируем его из модуля router.js
, чтобы другие модули могли его использовать:
const router = new VueRouter({
routes: routes,
mode: 'history',
})
export default router
Далее в начале файла src/main.js
импортируем router
:
import router from '@/router'
Передаем маршрут в экземпляр Vue:
new Vue({
router,
...
})
На этом настройка Vue Router завершена. Мы создали маршруты для внешнего интерфейса, которые сопоставляют шаблон URL-адреса с отображаемым компонентом. Сами маршруты пока не работают, потому как указывают на компоненты, которые еще не созданы.
Шаг 7. Создаем компоненты Vue
Теперь Vue умеет работать с маршрутами, пора создать компоненты, которые будут отображать данные из конечной точки GraphQL:
AuthorLink
– ссылка на страницу автора (используется вPost
иPostList
).PostList
– список постов в блоге (используется вAllPosts
,Author
иPostsByTag
).AllPosts
– список постов, начиная с самых последних.PostsByTag
– список постов, связанных с заданным тегом, начиная с самых недавних.Post
– метаданные и контент публикации.Author
– информация об авторе и список написанных им постов.
Компонент AuthorLink
Первый компонент, который мы создадим, отображает ссылку на автора. В каталоге src/components/
создадим файл AuthorLink.vue
. Этот файл представляет собой однофайловый компонент (single file component, SFC) Vue. SFC в одном файле хранит HTML, JavaScript и CSS, необходимые для визуализации компонента.
AuthorLink
принимает prop-объект author
, структура которого соответствует данным об авторах в GraphQL API. Компонент должен отображать имя и фамилию пользователя, если они указаны, в противном случае — имя пользователя.
Файл AuthorLink.vue
должен выглядеть следующим образом:
<template>
<router-link
:to="`/author/${author.user.username}`"
>{{ displayName }}</router-link>
</template>
<script>
export default {
name: 'AuthorLink',
props: {
author: {
type: Object,
required: true,
},
},
computed: {
displayName () {
return (
this.author.user.firstName &&
this.author.user.lastName &&
`${this.author.user.firstName} ${this.author.user.lastName}`
) || `${this.author.user.username}`
},
},
}
</script>
Этот компонент не будет использовать GraphQL напрямую. Вместо этого другие компоненты передают информацию об авторе, используя свойство author
.
Компонент PostList
Компонент PostList
принимает prop-объект posts
, структура которого соответствует данным о сообщениях в нашем GraphQL API. Компонент отображает следующие вещи:
- Заголовок и подзаголовок поста, слинкованный с самой страницей поста.
- Ссылка на автора поста через
AuthorLink
(если логическая переменнаяshowAuthor
равнаtrue
). - Дата публикации поста.
- Мета-описание поста.
- Список тегов.
Создайте PostList.vue
в каталоге src/components/
. Шаблон компонента должен выглядеть следующим образом:
<template>
<div>
<ol class="post-list">
<li class="post" v-for="post in publishedPosts" :key="post.title">
<span class="post__title">
<router-link
:to="`/post/${post.slug}`"
>{{ post.title }}: {{ post.subtitle }}</router-link>
</span>
<span v-if="showAuthor">
by <AuthorLink :author="post.author" />
</span>
<div class="post__date">{{ displayableDate(post.publishDate) }}</div>
<p class="post__description">{{ post.metaDescription }}</p>
<ul>
<li class="post__tags" v-for="tag in post.tags" :key="tag.name">
<router-link :to="`/tag/${tag.name}`">#{{ tag.name }}</router-link>
</li>
</ul>
</li>
</ol>
</div>
</template>
Код JavaScript должен выглядеть так:
<script>
import AuthorLink from '@/components/AuthorLink'
export default {
name: 'PostList',
components: {
AuthorLink,
},
props: {
posts: {
type: Array,
required: true,
},
showAuthor: {
type: Boolean,
required: false,
default: true,
},
},
computed: {
publishedPosts () {
return this.posts.filter(post => post.published)
}
},
methods: {
displayableDate (date) {
return new Intl.DateTimeFormat(
'en-US',
{ dateStyle: 'full' },
).format(new Date(date))
}
},
}
</script>
Компонент PostList
получает данные через prop вместо прямого использования GraphQL.
В том же файле можно добавить несколько дополнительных стилей CSS, чтобы сделать список постов удобнее для чтения:
<style>
.post-list {
list-style: none;
}
.post {
border-bottom: 1px solid #ccc;
padding-bottom: 1rem;
}
.post__title {
font-size: 1.25rem;
}
.post__description {
color: #777;
font-style: italic;
}
.post__tags {
list-style: none;
font-weight: bold;
font-size: 0.8125rem;
}
</style>
Компонент AllPosts
Следующий компонент, который мы создадим, — список постов в блоге. Он должен отображать две вещи:
- Заголовок, например «Недавние сообщения» или «Recent Posts».
- Список постов с помощью
PostList
.
Создаём AllPosts.vue
в каталоге src/components/
. Должно получиться так:
<template>
<div>
<h2>Recent posts</h2>
<PostList v-if="allPosts" :posts="allPosts" />
</div>
</template>
<script>
import PostList from '@/components/PostList'
export default {
name: 'AllPosts',
components: {
PostList,
},
data () {
return {
allPosts: null,
}
},
}
</script>
Позже мы заполним переменную allPosts
динамически с помощью GraphQL-запроса.
Компонент PostsByTag
Компонент PostsByTag
очень похож на компонент AllPosts
:
<template>
<div>
<h2>Posts in #{{ $route.params.tag }}</h2>
<PostList :posts="posts" v-if="posts" />
</div>
</template>
<script>
import PostList from '@/components/PostList'
export default {
name: 'PostsByTag',
components: {
PostList,
},
data () {
return {
posts: null,
}
},
}
</script>
Компонент Author
Компонент Author
действует, как страница профиля автора. То есть компонент должен отображать следующее:
- Заголовок с именем автора.
- Ссылка на сайт автора (если указана).
- «О себе» автора (если предоставлено).
- Список постов автора.
<template>
<div v-if="author">
<h2>{{ displayName }}</h2>
<a
:href="author.website"
target="_blank"
rel="noopener noreferrer"
>Website</a>
<p>{{ author.bio }}</p>
<h3>Posts by {{ displayName }}</h3>
<PostList :posts="author.postSet" :showAuthor="false" />
</div>
</template>
<script>
import PostList from '@/components/PostList'
export default {
name: 'Author',
components: {
PostList,
},
data () {
return {
author: null,
}
},
computed: {
displayName () {
return (
this.author.user.firstName &&
this.author.user.lastName &&
`${this.author.user.firstName} ${this.author.user.lastName}`
) || `${this.author.user.username}`
},
},
}
</script>
Компонент Post
Компонент Post
является наиболее интересным, поскольку отвечает за отображение всей информации о публикации:
- Заголовок и подзаголовок.
- Автор (с помощью ссылки
AuthorLink
). - Дата публикации.
- Мета-описание.
- Содержание («тело» поста).
- Список связанных тегов в виде ссылок.
За счет используемой модели данных и компонентной архитектуры нам потребуется совсем немного кода:
<template>
<div class="post" v-if="post">
<h2>{{ post.title }}: {{ post.subtitle }}</h2>
By <AuthorLink :author="post.author" />
<div>{{ displayableDate(post.publishDate) }}</div>
<p class="post__description">{{ post.metaDescription }}</p>
<article>
{{ post.body }}
</article>
<ul>
<li class="post__tags" v-for="tag in post.tags" :key="tag.name">
<router-link :to="`/tag/${tag.name}`">#{{ tag.name }}</router-link>
</li>
</ul>
</div>
</template>
<script>
import AuthorLink from '@/components/AuthorLink'
export default {
name: 'Post',
components: {
AuthorLink,
},
data () {
return {
post: null,
}
},
methods: {
displayableDate (date) {
return new Intl.DateTimeFormat(
'en-US',
{ dateStyle: 'full' },
).format(new Date(date))
}
},
}
</script>
Компонент App
Прежде чем увидеть результаты работы, необходимо обновить компонент App
, созданный при первоначальной настройке Vue. Вместо отображения страницы-заставки Vue должен отображаться компонент AllPosts
.
В каталоге src/
откроем App.vue
и заменим содержимое следующим кодом:
<template>
<div id="app">
<header>
<router-link to="/">
<h1>Awesome Blog</h1>
</router-link>
</header>
<router-view />
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
<style>
* {
margin: 0;
padding: 0;
}
body {
margin: 0;
padding: 1.5rem;
}
* + * {
margin-top: 1.5rem;
}
#app {
margin: 0;
padding: 0;
}
</style>
Здесь описан заголовок с названием блога, который ведет на главную страницу, а также компонент Vue Router
, который отображает компонент для текущего маршрута.
Итак, мы подошли к концу седьмого (предпоследнего) шага. Если вы раньше не использовали Vue, этот шаг, возможно, был трудоемким. Однако мы достигли важной вехи — работающего приложения Vue с маршрутами и представлениями, готовыми для отображения данных.
Запустите сервер разработки Vue и перейдите по адресу http://localhost:8080. Вы должны увидеть заголовок блога и заголовки недавних публикаций. На последнем шаге мы воспользуемся Apollo для обращений к GraphQL API, чтобы соединить между собой интерфейс и серверную часть.
Шаг 8. Собираем данные
Пора получить данные из GraphQL API. Плагин Vue Apollo, который мы установили ранее, интегрирует Apollo во Vue и делает удобнее процедуру выполнения запросов к GraphQL API.
Vue Apollo уже настроен «из коробки», но нужно указать правильную конечную точку запроса. Мы также можем отключить WebSocket-соединение, которое плагин пытается использовать по умолчанию, поскольку это создает лишний шум на вкладках «Сеть» и «Консоль» в средствах разработки в браузере. Отредактируем определение apolloProvider
в модуле src/main.js
, указав свойства httpEndpoint
и wsEndpoint
:
new Vue({
...
apolloProvider: createProvider({
httpEndpoint: 'http://localhost:8000/graphql',
wsEndpoint: null,
}),
...
})
Теперь мы можем добавить запросы для заполнения страниц. Мы сделаем это, добавив функцию created()
в несколько наших SFC. Эта функция — специальный хук жизненного цикла Vue, выполняемый в момент, когда компонент готовится к рендерингу на странице. Функцию можно использовать для запроса данных, которые мы хотим визуализировать. Мы создадим запросы для следующих компонентов:
Post
Author
PostByTag
AllPosts
Запрос для получения информации о посте (Post)
Запрос для отдельного поста принимает slug нужного сообщения и возвращает необходимую информацию для отображения публикации. Для создания запроса в функции created()
мы используем функции-помощники $apollo.query
и gql
. Функция created()
будет выглядеть следующим образом:
<script>
...
async created () {
const post = await this.$apollo.query({
query: gql`query ($slug: String!) {
postBySlug(slug: $slug) {
title
subtitle
publishDate
metaDescription
slug
body
author {
user {
username
firstName
lastName
}
}
tags {
name
}
}
}`,
variables: {
slug: this.$route.params.slug,
},
})
this.post = post.data.postBySlug
},
...
</script>
Запрос извлекает большую часть данных о публикации, авторе и тегах. Обратите внимание, что в запросе используется заполнитель $slug
, для заполнения которого применяется свойство variables
, передаваемое в $apollo.query
. Свойство slug
соответствует имени заполнителя $slug
. Мы встретим такой же шаблон в следующих запросах.
Запрос для Author
Запрос Author
принимает username
автора и возвращает информацию, необходимую для отображения автора и его списка постов. Должно получиться так:
<script>
...
async created () {
const user = await this.$apollo.query({
query: gql`query ($username: String!) {
authorByUsername(username: $username) {
website
bio
user {
firstName
lastName
username
}
postSet {
title
subtitle
publishDate
published
metaDescription
slug
tags {
name
}
}
}
}`,
variables: {
username: this.$route.params.username,
},
})
this.author = user.data.authorByUsername
},
...
</script>
В этом запросе используется postSet
, который может показаться знакомым по нашей модели данных Django. Название «post set» происходит от связи, которую Django создает для поля внешнего ключа. Graphene-Django автоматически предоставляет postSet
в GraphQL API.
Запрос для PostByTag
Запрос для PostsByTag
очень похож на предыдущие. Он принимает желаемый тег и возвращает список подходящих постов:
<script>
...
async created () {
const posts = await this.$apollo.query({
query: gql`query ($tag: String!) {
postsByTag(tag: $tag) {
title
subtitle
publishDate
published
metaDescription
slug
author {
user {
username
firstName
lastName
}
}
tags {
name
}
}
}`,
variables: {
tag: this.$route.params.tag,
},
})
this.posts = posts.data.postsByTag
},
...
</script>
Вы уже могли заметить, что некоторые части запросов очень похожи друг на друга. Чтобы уменьшить дублирование кода, обратите внимание на концепцию GraphQL fragments.
Запрос для AllPosts
Запрос AllPosts
не требует ввода информации и возвращает тот же набор данных, что и запрос PostsByTag
. Должно получиться так:
<script>
...
async created () {
const posts = await this.$apollo.query({
query: gql`query {
allPosts {
title
subtitle
publishDate
published
metaDescription
slug
author {
user {
username
firstName
lastName
}
}
tags {
name
}
}
}`,
})
this.allPosts = posts.data.allPosts
},
...
</script>
Ура! Это последний запрос. Теперь каждый компонент получает данные, необходимые для отображения, а мы получили работающий блог. Запустите сервер разработки Django и сервер разработки Vue. Откройте http://localhost:8080 и посмотрите на результат.
Возможные следующие шаги
Мы начали с создания серверной части блога Django для администрирования, сохранения и обслуживания данных блога. Затем создали интерфейс Vue для использования и отображения этих данных. Наконец, научили их общаться с GraphQL, используя Graphene и Apollo.
Чтобы еще раз убедиться, что блог работает должным образом, можно попробовать следующее:
- Добавить пользователей и посты, чтобы увидеть разделение по авторам.
- Создать несколько сообщений без публикации, чтобы убедиться, что они не отображаются в блоге, а сохраняются в форме черновика.
Заключение
Итак, мы узнали, как использовать GraphQL для создания гибких типизированных представлений данных. Вы можете применять эти методы как в уже созданных приложениях Django, так и в тех, что вы только планируете создать. Как и другие API, этот подход применим для большинства современных фронтенд-фреймворков. Надеемся, что эта концепция пригодится вам ещё не раз.
Вот ещё несколько материалов о GraphQL:
- Сравним GraphQL и библиотеку Graphene в Python
- Хватит использовать REST для API!
- Создаем мощный API на Node.js, GraphQL, MongoDB, Hapi и Swagger
#статьи
- 3 авг 2022
-
0
Разберёмся в том, как устроен фреймворк Django, как он работает, и напишем своё первое приложение.
Иллюстрация: rawpixel.com / Freepik / mulyadi / Sebastian Dumitru / Unsplash / Дима Руденок для Skillbox Media
Изучает Python, его библиотеки и занимается анализом данных. Любит путешествовать в горах.
Django — это фреймворк для веб-разработки, написанный на Python в 2005 году. Он позволяет создавать сайты и веб-приложения разной сложности — от личного блога до аудиоплатформы (на нём собран Spotify). От других фреймворков Django отличается модульностью и простотой освоения.
В этой статье мы увидим, как создавать веб-приложения на Django: рассмотрим его структуру и напишем первое веб-приложение — движок для блога, который умеет публиковать и редактировать записи. Это первая часть урока по Django — во второй мы будем настраивать админку и подтюним внешний вид наших постов.
Согласно исследованию Stack Overflow за 2022 год, Django занимает девятое место среди всех веб-фреймворков, обгоняя, например, популярные Spring и Ruby on Rails.
Скриншот: Stack Overflow
На Django работают многие известные сервисы — например, «Инстаграм»*, Pinterest, Spotify, Last.fm и сайт Mozilla. Однако его используют не только в энтерпрайзе: нередко на нём собирают личные сайты: блоги, фотогалереи, портфолио и так далее.
Одно из главных преимуществ Django — принцип DRY (don’t repeat yourself): единожды написанный код можно повторно использовать в различных проектах. Поэтому Django часто сравнивают с конструктором Lego.
Кроме принципа DRY Django имеет и другие преимущества:
- понятый и простой синтаксис (он написан на Python);
- большое количество готовых шаблонов и модулей, которые облегчают разработку и позволяют не использовать внешние библиотеки или расширения. Это помогает избегать конфликтов между ними после обновлений;
- подробная документация и большое комьюнити. Если у вас появился какой-то вопрос о коде, то вы легко найдёте на него ответ на Stack Overflow или других сайтах.
Фреймворк основывается на четырёх главных компонентах:
- Модели (Models) — взаимодействуют с базой данных и достают из неё ту информацию, которую необходимо отобразить в браузере.
- Представления (Views) — обрабатывают запрос и обращаются к модели, сообщая ей, какую информацию необходимо достать из базы данных.
- Шаблоны (Templates) — показывают, каким именно образом необходимо показать информацию, полученную из базы данных.
- URL-маршруты (URL dispatcher) — перенаправляют HTTP-запрос от браузера в представления.
Сейчас структура может казаться запутанной, но на самом деле она простая — мы разберёмся с ней на практике, и вы быстро всё поймёте.
Прежде чем перейти к установке Django и virtualenv, необходимо убедиться в том, что у вас уже установлен Python. Проверить это и узнать версию Python можно с помощью терминала. Откройте Terminal в своей IDE или операционной системе и введите команду:
% python --version
Если Python установлен, то терминал покажет его версию:
Если же Python не установлен, то можно воспользоваться другим нашим руководством. После этого переходим к настройке виртуального окружения.
Виртуальное окружение, создаваемое с помощью virtualenv, — это специальный инструмент, который помогает управлять зависимостями и изолировать проекты друг от друга: например, устанавливать дополнительные библиотеки и пакеты локально для каждого проекта, а не глобально для всего компьютера. Установить virtualenv тоже можно через терминал:
% sudo pip3 install virtualenv
После этого необходимо создать директорию для проекта, внутри которой мы развернём виртуальное окружение:
% mkdir blog % cd blog
С помощью команды mkdir мы создаём папку blog, а благодаря команде cd в терминале переходим в неё. Теперь папка blog — это каталог, где мы организуем виртуальное окружение и будем хранить все наши файлы, связанные с нашим проектом Django.
Развернём виртуальное окружение внутри папки development:
% virtualenv venv -p python3
Теперь его необходимо активировать, иначе оно не будет работать:
% source venv/bin/activate
После активации виртуального окружения мы увидим результат в самом терминале. Обратите внимание, что во второй строке команда теперь начинается с (venv), а не с (base), как раньше:
Сам фреймворк Django тоже устанавливается с помощью терминала:
% pip install django
Результат установки:
В последней строке видно, что установка прошла успешно. При этом мы установили не только Django последней версии, но и ещё две библиотеки:
- asgiref — интерфейс для взаимодействия с асинхронными веб-сервисами, платформами и приложениями на Python;
- sqlparse — простой парсер для работы с базами данных SQL.
Итак, убедитесь, что вы сделали всё по чек-листу:
- проверили, что у вас есть Python;
- создали директорию и развернули в ней виртуальное окружение с помощью virtualenv;
- установили в ту же папку Django.
Теперь можно переходить к созданию самого проекта.
Разобраться в основных понятиях фреймворка Django лучше всего на практике. Напишем веб-приложение для небольшого блога с текстовыми записями и поговорим про его работу. Весь процесс разобьём на семь простых шагов — да здравствует модульность
Готовим и настраиваем окружение
Проще всего создать базовую структуру проекта с помощью терминала:
% django-admin startproject blog .
Очень важно не забыть точку в конце команды — она указывает на то, что проект создаётся в текущей папке без необходимости создания нового каталога. Если точку не поставить, то ничего не произойдёт и терминал выдаст ошибку.
Если всё прошло хорошо, то Django создаст в нашей директории blog файл manage.py, управляющий выполнением всех задач в терминале, и папку blog с пятью файлами:
Мы не будем подробно обсуждать предназначение каждого из этих файлов — не все из них нам надо будет редактировать. Если вам интересны подробности, просто откройте файлы, в шапке каждого из них будет написано, для чего он нужен.
Теперь нам надо продумать, где мы станем хранить записи блога. Для этого потребуется создать базу данных. Django может работать с различными базами данных, но по умолчанию работает с SQLite. Её нам будет достаточно — у нас всего один пользователь и простая структура записей.
Самое приятное — можно создать базу данных SQLite с помощью одной команды.
% python manage.py migrate
И всё. Если посмотреть в каталог проекта, вы увидите, что там появился файл db.sqlite3. Это и есть наша база данных.
Для работы с нашим блогом необходимо создать суперпользователя, обладающего возможностями администратора. Сделать это можно с помощью простой команды:
% python manage.py createsuperuser
После выполнения команды терминал попросит ввести имя пользователя, email и установить пароль. Запомните их — они нам ещё понадобятся.
Ещё одна команда, которой мы будем часто пользоваться — runserver. Она запускает веб-сервер для разработки:
% python manage.py runserver
По умолчанию сервер запускается на порту 8000 по адресу 127.0.0.1 и доступен только на вашем компьютере. Порт и IP-адрес можно указать самостоятельно, но сейчас это нам не нужно.
Когда сервер запущен, вы можете открыть проект Django в браузере, введя http://127.0.0.1:8000 или http://localhost:8000. Если все предыдущие этапы выполнены правильно, вы увидите приветственное окно Django:
Завершим настройку нашего проекта и перейдём на страницу администрирования http://localhost:8000/admin. В форме введём имя и пароль суперпользователя, которые мы задали в предыдущем пункте. В результате нам откроется панель администратора:
Панель администратора позволяет управлять контентом и пользователями сайта. Она понадобится нам на следующих этапах создания блога.
Проект Django содержит одно или несколько приложений. Вначале можно запутаться между понятиями «проект» и «приложение», но разница между ними простая:
- Приложение — это модуль нашего проекта. Оно может быть связано с конкретными функциями или группой пользователей. Например, если бы мы создавали социальную сеть, то сделали бы отдельные приложения для обычных пользователей, владельцев групп и модераторов контента.
- Проект — это вся наша программа в целом. Она может состоять из одного приложения (именно так будет у нас) или из нескольких.
Разделение функций проекта по разным приложениям позволяет легко использовать единожды написанный код в разных проектах, быстро добавляя нужную функциональность.
В нашем случае нам достаточно хранить и отображать текстовые заметки, поэтому мы обойдёмся одним приложением. Назовём его entries. Прежде чем создать его, необходимо остановить работу сервера с помощью команды в терминале. Для этого нажмём комбинацию клавиш Ctrl + C в Windows или Control + C в macOS.
Теперь создадим само приложение:
% python manage.py startapp entries
Эта команда создаст папку entries в проекте blog с набором предопределённых файлов (помните, что все эти команды терминала необходимо вводить в папке проекта). С некоторыми из них мы поработаем позже.
Пока что Django не видит созданное приложение entries. Чтобы его подключить, необходимо добавить название приложения в конец списка INSTALLED_APPS в файл blog/settings.py:
# Открываем файл blog/settings.py и добавляем в конец списка запись. INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'entries.apps.EntriesConfig', ]
Теперь приложение entries подключено к нашему проекту и Django видит его конфигурации. Одна из этих конфигураций — та самая модель, которая описывает, как записи блога должны выглядеть в базе данных.
После создания базы данных необходимо создать для неё таблицу, в которой будут храниться записи блога. В Django это делается с помощью класса Models. Как и обычные классы в Python, имена моделей принято задавать в единственном числе, и начинаться они должны с заглавной буквы. Так как наше приложение называется entries, модель будет называться Entry.
Поля Entry — это элементы, которыми будет обладать любая запись в блоге. На самом сайте они будут показаны в виде полей формы, а в базе данных — столбцами. У записи в блоге будет три элемента:
- title — заголовок;
- content — основной текст;
- date_created — дата и время создания.
В файле entries/models.py сначала импортируем модуль timezone из django.utils, а затем создадим класс Entry (в комментариях поясняется, за что отвечает тот или иной кусочек кода):
from django.db import models from django.utils import timezone class Entry(models.Model): # Создаём новый класс, который будет служить для блога моделью, указывая все необходимые элементы. title = models.CharField(max_length=200) content = models.TextField() date_created = models.DateTimeField(default=timezone.now) def __str__(self): # С помощью функции меняем то, как будет представлена запись в модели. return self.title # Указываем, что она будет идентифицироваться с помощью своего заголовка. class Meta: verbose_name_plural = "Entries" # Указываем правильное написание для множественного числа слова Entry.
После импорта модуля timezone мы можем использовать параметр timezone.now в качестве аргумента по умолчанию для date_created. Теперь при создании новой записи не придётся вручную указывать время и дату. Это пригодится нам позже, когда мы будем создавать форму для создания постов.
В дополнение к title, content и date_created Django автоматически добавит id как уникальный первичный ключ для всех записей. Строковое представление записи с первичным ключом 1 по умолчанию будет Entry object (1). Добавив функцию __str__(), мы настраиваем то, что будет отображаться вместо этой нумерации. Так как мы создаём блог, то лучше идентифицировать запись с помощью заголовка.
Ещё одна переменная, которую необходимо настроить вручную, — это verbose_name_plural. Если этого не сделать, то Django будет неправильно указывать множественное число Entry как Entrys (нам надо Entries).
Чтобы наша модель Entry отображалась в панели администрирования Django, регистрируем её в файле entries/admin.py:
from django.contrib import admin from .models import Entry admin.site.register(Entry) # Регистрируем модель.
Если этого не сделать, то Django не выдаст ошибку, однако при работе с блогом возможности управлять моделью через панель администратора уже не будет, а это неудобно.
После добавления нового класса и его регистрации в панели администратора необходимо создать файлы миграции для Django и запустить их. Создаются они с помощью команды makemigrations, а реализуются через команду migrate. Обе команды пишем в терминале:
python manage.py makemigrations python manage.py migrate
После завершения миграции переходим в панель администратора http://localhost:8000/admin. В ней появился раздел Entries с нашим приложением:
Сейчас в этом разделе нет никаких записей. Можно добавить их вручную, нажав Add. Однако удобнее реализовать добавление новых записей в блог через пользовательский интерфейс, а не панель администратора. Но это мы сделаем в следующей части статьи — следите за анонсами в Telegram-канале «Люди и код» и подписывайтесь на нашу рассылку, чтобы не пропустить.
* Решением суда запрещена «деятельность компании Meta Platforms Inc. по реализации продуктов — социальных сетей Facebook и Instagram на территории Российской Федерации по основаниям осуществления экстремистской деятельности».
Нейросети вам помогут.
Большой вебинар по нейросетям. 15 экспертов, 7 топ-нейросетей. Научитесь использовать ИИ в своей работе и повысьте эффективность.
Узнать больше
Django — фрэймворк для создания сложных и комплексных сайтов на языке Python.
В этой статье на примере создания сайта, показывающего погоду в разных городах мира, мы продемонстрируем работу с API, HTML-шаблонами и базами данных в Django.
Запускать проект будем на Джино.Хостинге.
# Подготовка к работе
Создадим для нашего сайта новый хостинг-контейнер. Для этого в разделе АккаунтХостинг выберем «Создать новый хостинг». После создания перейдём в раздел «Услуги» и подключим «Поддержку скриптовых языков» и «Поддержку SSH».
После подключения выбранных услуг подключимся к хостингу по SSH. Данные для SSH-подключения указаны в разделе УправлениеНастройки SSH.
Перед первым подключением к хостинг-контейнеру в разделе УправлениеНастройки SSH нужно задать пароль пользователя. Порт для подключения используется стандартный — 22.
Также в этом разделе можно настроить ограничение доступа к хостингу только с указанных IP-адресов.
После подключения к хостингу по SSH создадим виртуальное окружение для Python:
Эта команда создаёт виртуальное окружение weather_venv
для Python 3.10. Список всех доступных версий Python и пути к их интерпретаторам указаны в разделе УправлениеТехническая информация.
После создания виртуального окружения активируем его
и установим Django с помощью стандартного пакетного менеджера
Теперь наше виртуальное окружение готово к работе. Перейдём к созданию проекта.
# Создание и запуск проекта
Создавать новый проект будем на локальной машине с последующим деплоем проекта на Хостинг. О том, как подготовить Хостинг к развёртыванию нового проекта и о способах загрузки проекта на сервер можно прочитать в нашей инструкции по развёртыванию проекта на Джино.Хостинге.
Создадим новый проект Django:
Эта команда создаст папку weather_project со вложенной одноимённой папкой weather_project и файлом manage.py, отвечающим за управление проектом.
Уже на данном этапе можно настроить загрузку проекта на сервер и проверить работу пустого Django-проекта. Поскольку наше приложение будет объёмным и в течение работы мы будем несоклько раз обновлять его и изменять, рекомендуем само приложение создавать в IDE VSCode или PyCharm, из которых удобно настроить выгрузку приложения непосредственно на сервер.
После загрузки проекта на хостинг в папку с доменным именем, на котором он будет работать, проверим его работу.
Для запуска Django-проекта на Хостинге нужно создать файл passenger_wsgi.py, который будет отвечать за связь проекта с веб-сервером. Содержимое файла должно быть следующим:
Здесь ~/weather_venv/bin/python3
— путь к виртуальному окружению на хостинге; weather_project.wsgi
— указание на основной файл, который будет запускать приложение.
Теперь нужно изменить настройки самого нашего проекта, чтобы он мог запускаться на выбранном домене. Для этого в папке weather_project откроем файл settings.py и в строке ALLOWED_HOSTS
укажем выбранное доменное имя, например ALLOWED_HOSTS = [
example.com]
.
После этого в разделе УправлениеНастройки веб-сервера для выбранного домена укажем версию Python, которая будет использоваться для работы сайта и нажмём Перезагрузить, чтобы перезагрузить веб-сервер и проверить работу нашего проекта.
В адресной строке браузера укажем наше доменное имя. Если всё работает правильно, мы увидим стандартную стартовую страницу Django
# Создание приложения
Вернёмся к нашей IDE и создадим приложение в нашем проекте:
Эта команда создаст папку weather_app, в которой уже находятся шаблоны для создания основных файлов нашего приложения.
Теперь перейдём в папку weather_app и создадим здесь файл urls.py:
Этот файл дублирует файл urls.py всего проекта. Основное отличие его в том, что в этом файле будут только те URL, которые относятся к нашему приложению, а не ко всему проекту в целом.
Чтобы добавить наше приложение к проекту, добавим его название в список уже установленных приложений проекта. Для этого в папке weather_project откроем файл settings.py и в блок INSTALLED_APPS
добавим название нашего приложения:
Теперь изменим основной файл urls.py, находящийся в папке weather_project. Мы добавим к нему строку, указывающую на использование нашего нового файла urls.py:
# Создание HTML-шаблона
Следующим шагом нам нужно создать HTML-шаблон, в который будут экспортироваться полученные по API данные и отображаться на главной странице нашего сайта.
Создадим в папке нашего приложения weather_app папку для шаблонов templates. В папке templates создадим папку для шаблонов приложения weather и уже в этой папке создадим файл index.html.
Содержимое индексного файла (~/domains/example.com/weather_app/templates/weather/index.html
) будет следующим:
index.html
В Django-проекте основное взаимодействие происходит между файлами views.py приложения и urls.py проекта. Теперь, когда шаблон готов, изменим эти файлы для запуска приложения.
Откроем файл views.py и приведём его к следующему виду:
Здесь мы назвали наш основной запрос index и использовали функцию render для обращения к файлу шаблона. Теперь добавим url, который будет отправлять запрос к этому шаблону. Откроем файл urls.py, который мы создали в папке с нашим приложением и приведём его к следующему виду:
Здесь описано перенаправление запроса к файлу views.py, который мы только что изменили.
Проверим работу нашего приложения! Загрузим его на сервер и перезагрузим веб-сервер (УправлениеНастройки веб-сервера). Откроем браузер и проверим страницу нашего сайта:
# API
Для работы нашего приложения, нам будут нужны актуальные данные о погоде в разных городах мира. Получать их мы будем от одного из свободных погодных сервисов — Open Weather Map (opens new window).
Для получения данных по API нам нужно зарегистрироваться на сайте и в личном кабинете получить уникальный ключ, которым будем подписывать запрос
Чтобы проверить работу полученного ключа, можно через бразуер отправить запрос к приложению в следующем формате:
В ответ в браузере должна появиться информация в JSON-формате:
Из этого объёма нам будут нужны только основные сведения. Для этого наше приложение будет конвертировать JSON-формат в формат данных, воспринимаемых Python.
Настроим работу запросов и конвертирование полученные данных из JSON-формата в формат данных Python. Для этого добавим в файл views.py строки import requests
, url
, city
, city_weather
:
Мы добавили URL, к которому будем отправлять запрос и строку, отвечающую за конвертирование данных из JSON в Python-формат.
В нашем случае в URL не указан город, город указан отдельно в строке city
и сейчас это Penza. Позже мы исправим это и будем указывать город в базе данных и через специальную форму ввода в самом приложении.
Сейчас, когда наше приложение отправляет запрос и получает ответ, можно перейти к отображению полученных данных на нашем сайте.
# Отправка данных в шаблон
Чтобы отобразить полученные данные на странице сайта, нужно передать их в index.html. Для этого создадим библиотеку значений, данные из которой и будем передавать в шаблон.
В файле views.py добавим следующие строки:
Здесь мы добавили блок weather={}
, в котором определили переменные city
, temperarute
, description
и icon
. Этим переменным мы присвоили определённые значения из получаемого по API массива данных.
Теперь, чтобы передать эти данные в шаблон создадим дополнительную переменную context
, которая и будет передавать все данные в шаблон. Здесь же, в файле views.py добавим строки:
Переменная context
— это словарь, в котором содержатся все данные блока weather
.
Теперь нужно настроить отображение получаемых данных в самом HTML-шаблоне. Откроем файл index.html и вместо значений, которые на предыдущем шаге задавали вручную вставим значения наших новых переменных, используя
В данном случае важно помнить, что при вызове переменной из словаря нужно использовать .
, а не стандартный синтаксис Python (например, вызов переменной city будет выглядеть как weather.city
вместо привычного weather[‘city’]
.
После внесения необходимых изменений снова обновим наше приложение на сервере и на главной странице нашего сайта увидим, что информация о погоде в указанном городе получена.
Следующая «проблема» нашего приложения — указание города. В данном случае он указан напрямую в коде. Это неправильно — сделаем так, чтобы список городов, для которых нужно отображать информацию, хранился в базе данных.
Воспользуемся для хранения списка городов SQLite, идущей в составе Django. Чтобы добавить города в базу, создадим дополнительную форму в окне администратора приложения.
# Администрирование приложения
Создадим администратора нашего приложения командой:
Эта команда запустит процесс создания администратора нашего сайта, в ходе которого мы укажем его основные данные: пароль, адрес электронной почты и т.д.
После создания администратора, вход в панель администрирования осуществляется через браузер по адресу http://your_domain/admin
.
Теперь добавим в список доступных администратору операций возможность создавать список городов. Для этого воспользуемся файлом models.py, который находится в папке нашего приложения. Приведём этот файл к следующему виду:
Эта директива создаст в базе данных приложения таблицу из одной колонки, которая будет называться «name» и в которой будут храниться названия городов.
Чтобы создать эту таблицу в базе, нужно создать связи командой makemigrations
и затем на основании этих связей обновить содержимое приложения:
Таблица в базе создана и готова к заполнению. Чтобы заполнить её, добавим форму ввода города в панель администрирования. Изменим для этого файл admin.py нашего приложения. Привдём его к следующем виду:
Теперь в панели администрирования появилась новая форма для ввода городов. Добавим несколько.
# Добавление городов на главную страницу сайта
После того, как мы добавили в базу список городов, нам нужно изменить файлы views.py и index.html, чтобы на главной странице нашего сайта отображались все города.
Начнём с изменения файла views.py. Импортируем столбец City и сделаем отдельный запрос по API для каждого из городов:
Теперь нам нужно сохранить данные для каждого города из списка и передать их в HTML-шаблон. Для этого создадим отдельный список weather_data
, в котором будут храниться данные для каждого города.
Затем заменим переменную City
на cities
.
После — добавим словарь weather
для каждого города в общий список weather_data
и в конце немного изменим context
, чтобы он передавал весь список в HTML-шаблон, а не только один блок данных.
В итоге файл views.py должен прийти к такому виду:
Далее, изменим index.html — добавим цикл в отображение данных в шаблоне. Для этого добавим строки {% for weather in weather_data %}
и {% endfor %}
в начало и конец отображения данных о городах. В итоге, блок отображения данных должен стать таким:
Теперь можно проверить, что у нас получилось. В адресной строке браузера адрес нашего сайта. В зависимости от добавленных в список городов, главная страница сайта должна выглядеть примерно так:
# Создание формы ввода
Последним, что осталось сделать для нашего сайта — создать форму ввода, чтобы пользователи могли добавлять города прямо с главной страницы сайта.
Для этого создадим новый файл forms.py в папке нашего приложения. Содержимое файла будет следующим:
Это форма ввода нового имени города. Чтобы она заработала, нужно добавить её описание во views.py и передать данные из неё в шаблон.
Начнём с изменений во views.py
Мы добавили описание передачи данных из формы и описали саму форму ввода данных.
Теперь изменим index.html
В индексный файл мы добавили блок описания формы и CSRF-токен, необходимый для отправки запросов из Django.
После описания формы ввода в шаблоне, нам нужно проверить добавить во views.py блок, который будет отвечать за обработку запросов, отправленных в форме:
Здесь мы добавили if
для проверки наличия запроса, отправленного из HTML-шаблона. Далее полученные данные сохраняются и передаются в обработку.
Сохраните все сделанные изменения и перезагрузите веб-сервер. Теперь можно проверять работу формы для добавления городов. Добавим, например, «Sydney»:
# Заключение
Мы описали создание сайта на Django с использованием HTML-шаблонов, запросов по API, баз данных и формой для добавления информации в базу данных.