1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
|
---
title: 'Руководство Django Часть 8: Аутентификация и авторизация пользователя'
slug: Learn/Server-side/Django/Authentication
tags:
- Python
- Аутентификация
- Аутентификация django
- Django
- Начинающий
- Обучение
- Разграничение доступа
- Руководство
- Сервер
- Статья
- Формы
- на стороне сервера
- сессии
translation_of: Learn/Server-side/Django/Authentication
original_slug: Learn/Server-side/Django/Аутентификация
---
<div>{{LearnSidebar}}</div>
<div>{{PreviousMenuNext("Learn/Server-side/Django/Sessions", "Learn/Server-side/Django/Forms", "Learn/Server-side/Django")}}</div>
<p class="summary">В данном руководстве мы продемонстрируем вам систему входа пользователя на ваш сайт используя его собственный аккаунт. Кроме того, мы покажем как реализовать контроль того, что может видеть и делать пользователь, в зависимости от того, залогинен он, или нет, а также имеет ли он соответствующий уровень прав доступа <em>(permissions)</em>. Для того чтобы продемонстрировать все это, мы расширим <a href="https://developer.mozilla.org/ru/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a>, добавив страницы для входа/выхода, а также страницы просмотра/редактирования книг, специфические для пользователя и персонала.</p>
<table class="learn-box standard-table">
<tbody>
<tr>
<th scope="row">Требования:</th>
<td>Завершить изучение предыдущих тем руководства, включая <a href="/ru/docs/Learn/Server-side/Django/Sessions">Руководство Django Часть 7: Работа с сессиями</a>.</td>
</tr>
<tr>
<th scope="row">Цель:</th>
<td>Понимать как настроить и использовать механизм аутентификации пользователя и разграничений прав доступа.</td>
</tr>
</tbody>
</table>
<h2 id="Обзор">Обзор</h2>
<p>Django предоставляет систему аутентификации и авторизации ("permission") пользователя, реализованную на основе фреймворка работы с сессиями, который мы рассматривали в <a href="/ru/docs/Learn/Server-side/Django/Sessions">предыдущей части</a>. Система аутентификации и авторизации позволяет вам проверять учетные данные пользователей и определять какие действия какой пользователь может выполнять. Данный фреймворк включает в себя встроенные модели для <code>Пользователей</code> и <code>Групп</code> (основной способ применения прав доступа для более чем одного пользователя), непосредственно саму систему прав доступа (permissions)/флаги, которые определяют может ли пользователь выполнить задачу, с какой формой и отображением для авторизованных пользователей, а так же получить доступ к контенту с ограниченным доступом.</p>
<div class="note">
<p><strong>Примечание</strong>: В соответствии с идеологией Django система аутентификации является очень общей и, таким образом, не предоставляет некоторые возможности, которые присутствуют в других системах веб-аутентификации. Решениями некоторых общих задач занимаются пакеты сторонних разработчиков, например, защита от подбора пароля (через стороннюю библиотеку OAuth).</p>
</div>
<p>В данном разделе руководства мы покажем вам реализацию аутентификации пользователя на сайте <a href="https://developer.mozilla.org/ru/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a>, создание страниц входа/выхода, добавления разграничения доступа (permissions) к вашим моделям, а также продемонстрируем контроль за доступом к некоторым страницам. Мы будем использовать аутентификацию/авторизацию для показа пользователям и сотрудникам библиотеки, списков книг, которые были взяты на прокат.</p>
<p>Система аутентификации является очень гибкой и позволяет вам формировать свои собственные URL-адреса, формы, отображения, а также шаблоны страниц, если вы пожелаете, с нуля, через простой вызов функций соответствующего API для авторизации пользователя. Тем не менее, в данной статье мы будем использовать "встроенные" в Django методы отображений и форм аутентификации, а также методы построения страниц входа и выхода. Нам все еще необходимо создавать шаблоны страниц, но это будет достаточно несложно.</p>
<p>Мы покажем вам как реализовать разграничение доступа (permissions), а также выполнять соответствующую проверку статусов авторизации и прав доступа, в отображениях, и в шаблонах страниц.</p>
<h2 id="Подключение_аутентификации">Подключение аутентификации</h2>
<p>Аутентификация была подключена автоматически когда мы создали <a href="/ru/docs/Learn/Server-side/Django/skeleton_website">скелет сайта</a> (в части 2), таким образом на данный момент вам ничего не надо делать.</p>
<div class="note">
<p><strong>Примечание</strong>: Необходимые настройки были выполнены для нас, когда мы создали приложение при помощи команды <code>django-admin startproject</code>. Таблицы базы данных для пользователей и модели авторизации были созданы, когда в первый раз выполнили команду <code>python manage.py migrate</code>.</p>
</div>
<p>Соответствующие настройки сделаны в параметрах <code>INSTALLED_APPS</code> и <code>MIDDLEWARE</code> файла проекта (<strong>locallibrary/locallibrary/settings.py</strong>), как показано ниже:</p>
<pre class="brush: python notranslate">INSTALLED_APPS = [
...
<strong> 'django.contrib.auth', </strong># Фреймворк аутентификации и моделей по умолчанию.
<strong> 'django.contrib.contenttypes', # </strong>Django контент-типовая система (даёт разрешения, связанные с моделями).
....
MIDDLEWARE = [
...
<strong> 'django.contrib.sessions.middleware.SessionMiddleware',</strong> # Управление сессиями между запросами
...
<strong> 'django.contrib.auth.middleware.AuthenticationMiddleware',</strong> # Связывает пользователей, использующих сессии, запросами.
....
</pre>
<h2 id="Создание_пользователей_и_групп">Создание пользователей и групп</h2>
<p>Вы уже создали своего первого пользователя когда мы рассматривали <a href="/ru/docs/Learn/Server-side/Django/Admin_site">Административная панель сайта Django</a> в части 4 (это был суперпользователь, созданный при помощи команды<code> python manage.py createsuperuser</code>). Наш суперпользователь уже авторизован и имеет все необходимые уровни доступа к данным и функциям, таким образом нам необходимо создать тестового пользователя для отработки соответствующей работы сайта. В качестве наиболее быстрого способа, мы будем использовать административную панель сайта для создания соответствующих групп и аккаунтов <em>locallibrary</em>.</p>
<div class="note">
<p><strong>Примечание</strong>: Вы можете создавать пользователей программно, как показано ниже. Например, вам мог бы подойти данный способ в том случае, если вы разрабатываете интерфейс, который позволяет пользователям создавать их собственные аккаунты (вы не должны предоставлять доступ пользователям к административной панели вашего сайта).</p>
<pre class="brush: python notranslate">from django.contrib.auth.models import User
# Создайте пользователя и сохраните его в базе данных
user = User.objects.create_user('myusername', 'myemail@crazymail.com', 'mypassword')
# Обновите поля и сохраните их снова
user.first_name = 'John'
user.last_name = 'Citizen'
user.save()
</pre>
</div>
<p>Ниже мы создадим группу, а затем пользователя. Несмотря на то, что у нас пока нет никаких разрешений для добавления к нашей библиотеке каких-либо членов, если мы захотим это сделать в будущем, то будет намного проще добавлять их к уже созданной группе, с заданной аутентификацией.</p>
<p>Запустите сервер разработки и перейдите к административной панели вашего сайта (<a href="http://127.0.0.1:8000/admin/">http://127.0.0.1:8000/admin/</a>). Залогиньтесь на сайте при помощи параметров (имя пользователя и пароля) аккаунта суперпользователя. Самая "верхняя" страница панели Администратора показывает все наши модели. Для того, чтобы увидеть записи в разделе <strong>Authentication and Authorisation</strong> вы можете нажать на ссылку <strong>Users</strong>, или <strong>Groups</strong>.</p>
<p><img alt="Admin site - add groups or users" src="https://mdn.mozillademos.org/files/14091/admin_authentication_add.png" style="border-style: solid; border-width: 1px; display: block; height: 364px; margin: 0px auto; width: 661px;"></p>
<p>В первую очередь, в качестве нового члена нашего сайта, давайте создадим новую группу.</p>
<ol>
<li>Нажмите на кнопку <strong>Add</strong> <strong>(Добавить)</strong> (рядом с Group) и создайте новую <em>группу</em>; для данной группы введите <strong>Name (Имя) </strong>"Library Members".<img alt="Admin site - add group" src="https://mdn.mozillademos.org/files/14093/admin_authentication_add_group.png" style="border-style: solid; border-width: 1px; display: block; height: 561px; margin: 0px auto; width: 800px;"></li>
<li>Для данной группы не нужны какие-либо разрешения, поэтому мы просто нажимаем кнопку <strong>SAVE (Сохранить)</strong> (вы перейдете к списку групп).</li>
</ol>
<p>Теперь давайте создадим пользователя:</p>
<ol>
<li>Перейдите обратно на домашнюю страницу административной панели</li>
<li>Для перехода к диалогу добавления пользователя нажмите на кнопку <strong>Add</strong>, соответствующую строке <em>Users (Пользователи)</em>.<img alt="Admin site - add user pt1" src="https://mdn.mozillademos.org/files/14095/admin_authentication_add_user_prt1.png" style="border-style: solid; border-width: 1px; display: block; height: 409px; margin: 0px auto; width: 800px;"></li>
<li>Введите соответствующие <strong>Username</strong> (имя пользователя) и <strong>Password</strong>/<strong>Password confirmation (пароль/подтверждение пароля)</strong> для вашего тестового пользователя</li>
<li>Нажмите <strong>SAVE</strong> для завершения процесса создания пользователя.<br>
<br>
Административная часть сайта создаст нового пользователя и немедленно перенаправит вас на страницу <em>Change user (Изменение параметров пользователя)</em> где вы можете, соответственно, изменить ваш <strong>username</strong>, а кроме того добавить информацию для дополнительных полей модели User. Эти поля включают в себя имя пользователя, фамилию, адрес электронной почты, статус пользователя, а также соответствующие параметры доступа (может быть установлен только флаг <strong>Active</strong>). Ниже вы можете определить группу для пользователя и необходимые параметры доступа, а кроме того, вы можете увидеть важные даты, относящиеся к пользователю (дату подключения к сайту и дату последнего входа).<img alt="Admin site - add user pt2" src="https://mdn.mozillademos.org/files/14097/admin_authentication_add_user_prt2.png" style="border-style: solid; border-width: 1px; display: block; height: 635px; margin: 0px auto; width: 800px;"></li>
<li>В разделе <em>Groups</em>, из списка <em>Доступные группы</em> выберите группу <strong>Library Member</strong>, а затем переместите ее в блок "Выбранные группы" (нажмите <strong>стрелку-"направо"</strong>, находящуюся между блоками).<img alt="Admin site - add user to group" src="https://mdn.mozillademos.org/files/14099/admin_authentication_user_add_group.png" style="border-style: solid; border-width: 1px; display: block; height: 414px; margin: 0px auto; width: 933px;"></li>
<li>Больше нам не нужно здесь нечего делать, просто нажмите "Save"(Сохранить), и вы вернетесь к списку созданных пользователей.</li>
</ol>
<p>Вот и все! Теперь у вас есть учетная запись «обычного члена библиотеки», которую вы сможете использовать для тестирования (как только добавим страницы, чтобы пользователи могли войти в систему).</p>
<div class="note">
<p><strong>Note</strong>: Попробуйте создать другого пользователя, например "Библиотекаря". Так же создайте группу "Библиотекарей" и добавьте туда своего только что созданного библиотекаря</p>
</div>
<h2 id="Настройка_представлений_проверки">Настройка представлений проверки</h2>
<p>Django предоставляет почти все, что нужно для создания страниц аутентификации входа, выхода из системы и управления паролями из коробки. Это включает в себя url-адреса, представления (views) и формы,но не включает шаблоны — мы должны создать свой собственный шаблон!</p>
<p>В этом разделе мы покажем, как интегрировать систему по умолчанию в Сайт LocalLibrary и создать шаблоны. Мы поместим их в основные URL проекта.</p>
<div class="note">
<p><strong>Заметка</strong>: Вы не должны использовать этот код, но вполне вероятно, что вы хотите, потому что это делает вещи намного проще. Вам почти наверняка потребуется изменить код обработки формы, если вы измените свою модель пользователя (сложная тема!) но даже в этом случае вы все равно сможете использовать функции просмотра запасов.</p>
</div>
<div class="note">
<p><strong>Заметка: </strong>В этом случае мы могли бы разумно поместить страницы аутентификации, включая URL-адреса и шаблоны, в наше приложение каталога. Однако, если бы у нас было несколько приложений, было бы лучше отделить это общее поведение входа в систему и иметь его доступным на всем сайте, так что это то, что мы показали здесь!</p>
</div>
<h3 id="Проектирование_URLs">Проектирование URLs</h3>
<p>Добавьте следующее в нижней части проекта urls.py файл (<strong>locallibrary/locallibrary/urls.py</strong>) файл:</p>
<pre class="brush: python notranslate">#Add Django site authentication urls (for login, logout, password management)
urlpatterns += [
path('accounts/', include('django.contrib.auth.urls')),
]
</pre>
<p>Перейдите по <a href="http://127.0.0.1:8000/accounts/">http://127.0.0.1:8000/accounts/</a> URL (обратите внимание на косую черту!), Django покажет ошибку, что он не смог найти этот URL, и перечислить все URL, которые он пытался открыть. Из этого Вы можете увидеть URL-адреса, которые будут работать, например:</p>
<div class="note">
<p><span id="result_box" lang="ru"><span>Примечание. Использование вышеуказанного метода добавляет следующие URL-адреса с именами в квадратных скобках, которые могут использоваться для изменения сопоставлений URL-адресов.</span> <span>Вам не нужно реализовывать что-либо еще - приведенное выше сопоставление URL-адресов автоматически отображает указанные ниже URL-адреса.</span></span></p>
</div>
<div class="note">
<pre class="brush: python notranslate">accounts/ login/ [name='login']
accounts/ logout/ [name='logout']
accounts/ password_change/ [name='password_change']
accounts/ password_change/done/ [name='password_change_done']
accounts/ password_reset/ [name='password_reset']
accounts/ password_reset/done/ [name='password_reset_done']
accounts/ reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/ reset/done/ [name='password_reset_complete']</pre>
</div>
<p><span id="result_box" lang="ru"><span>Теперь попробуйте перейти к URL-адресу входа (<a href="http://127.0.0.1:8000/accounts/login/">http://127.0.0.1:8000/accounts/login/</a>).</span> <span>Это приведет к сбою снова, но с ошибкой, сообщающей вам, что нам не хватает требуемого шаблона (registration / login.html) в пути поиска шаблона.</span> <span>Вы увидите следующие строки, перечисленные в желтом разделе вверху:</span></span></p>
<pre class="brush: python notranslate">Exception Type: TemplateDoesNotExist
Exception Value: <strong>registration/login.html</strong></pre>
<p><span id="result_box" lang="ru"><span>Следующий шаг - создать каталог регистрации в пути поиска, а затем добавить файл login.html.</span></span></p>
<h3 id="Каталог_шаблонов">Каталог шаблонов</h3>
<p><span id="result_box" lang="ru"><span>URL-адреса (и неявные представления), которые мы только что добавили, ожидают найти связанные с ними шаблоны в каталоге / регистрации / где-то в пути поиска шаблонов.</span><br>
<br>
<span>Для этого сайта мы разместим наши HTML-страницы в каталоге <strong>templates / registration /</strong>.</span> <span>Этот каталог должен находиться в корневом каталоге проекта, то есть в том же каталоге, что и в каталоге и папках <strong>locallibrary</strong>).</span> <span>Создайте эти папки сейчас.</span></span></p>
<div class="note">
<p><strong>Примечание:</strong> Ваша структура папок теперь должна выглядеть как показано внизу:<br>
locallibrary (django project folder)<br>
|_catalog<br>
|_locallibrary<br>
|_templates <strong>(new)</strong><br>
|_registration</p>
</div>
<p>Чтобы сделать эти директории видимыми для загрузчика шаблонов (<span id="result_box" lang="ru"><span>т. е. помещать этот каталог в путь поиска шаблона</span></span>) откройте настройки проекта (<strong>/locallibrary/locallibrary/settings.py</strong>), и обновите в секции <code>TEMPLATES</code> строку <code>'DIRS'</code> как показано.</p>
<pre class="brush: python notranslate">TEMPLATES = [
{
...
<strong> </strong><strong>'DIRS': [os.path.join(BASE_DIR, 'templates')],</strong>
'APP_DIRS': True,
...
</pre>
<h3 id="Шаблон_аутентификации">Шаблон <span id="result_box" lang="ru"><span>аутентификации</span></span></h3>
<div class="warning">
<p><strong>Важно</strong>: <span id="result_box" lang="ru"><span>Шаблоны аутентификации, представленные в этой статье, являются очень простой / слегка измененной версией шаблонов логина демонстрации Django.</span> <span>Возможно, вам придется настроить их для собственного использования!</span></span></p>
</div>
<p>Создайте новый HTML файл, названный /<strong>locallibrary/templates/registration/login.html</strong>. <span class="short_text" id="result_box" lang="ru"><span>дайте ему следующее содержание</span></span>:</p>
<pre class="brush: html line-numbers language-html notranslate">{% extends "base_generic.html" %}
{% block content %}
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
<code class="language-html">
{% if next %}
{% if user.is_authenticated %}
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>p</span><span class="punctuation token">></span></span>Your account doesn't have access to this page. To proceed,
please login with an account that has access.<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>p</span><span class="punctuation token">></span></span>
{% else %}
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>p</span><span class="punctuation token">></span></span>Please login to see this page.<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>p</span><span class="punctuation token">></span></span>
{% endif %}
{% endif %}
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>form</span> <span class="attr-name token">method</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>post<span class="punctuation token">"</span></span> <span class="attr-name token">action</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>{% url 'login' %}<span class="punctuation token">"</span></span><span class="punctuation token">></span></span>
{% csrf_token %}
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>table</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>tr</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>td</span><span class="punctuation token">></span></span>\{{ form.username.label_tag }}<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>td</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>td</span><span class="punctuation token">></span></span>\{{ form.username }}<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>td</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>tr</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>tr</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>td</span><span class="punctuation token">></span></span>\{{ form.password.label_tag }}<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>td</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>td</span><span class="punctuation token">></span></span>\{{ form.password }}<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>td</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>tr</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>table</span><span class="punctuation token">></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>input</span> <span class="attr-name token">type</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>submit<span class="punctuation token">"</span></span> <span class="attr-name token">value</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>login<span class="punctuation token">"</span></span> <span class="punctuation token">/></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>input</span> <span class="attr-name token">type</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>hidden<span class="punctuation token">"</span></span> <span class="attr-name token">name</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>next<span class="punctuation token">"</span></span> <span class="attr-name token">value</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>\{{ next }}<span class="punctuation token">"</span></span> <span class="punctuation token">/></span></span>
<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>form</span><span class="punctuation token">></span></span>
{# Assumes you setup the password_reset view in your URLconf #}
<span class="tag token"><span class="tag token"><span class="punctuation token"><</span>p</span><span class="punctuation token">></span></span><span class="tag token"><span class="tag token"><span class="punctuation token"><</span>a</span> <span class="attr-name token">href</span><span class="attr-value token"><span class="punctuation token">=</span><span class="punctuation token">"</span>{% url 'password_reset' %}<span class="punctuation token">"</span></span><span class="punctuation token">></span></span>Lost password?<span class="tag token"><span class="tag token"><span class="punctuation token"></</span>a</span><span class="punctuation token">></span></span><span class="tag token"><span class="tag token"><span class="punctuation token"></</span>p</span><span class="punctuation token">></span></span>
{% endblock %}</code></pre>
<p id="sect1"><span id="result_box" lang="ru"><span>Этот шаблон имеет сходство с тем, что мы видели раньше - он расширяет наш базовый шаблон и переопределяет блок контента.</span> <span>Остальная часть кода - это довольно стандартный код обработки формы, о котором мы поговорим в следующем учебном пособии.</span> <span>Все, что вам нужно знать, это показ формы, в которой вы можете ввести свое имя пользователя и пароль, а если вы введете недопустимые значения, вам будет предложено ввести правильные значения, когда страница обновится.</span></span></p>
<p>Перейдите на страницу входа (<a href="http://127.0.0.1:8000/accounts/login/">http://127.0.0.1:8000/accounts/login/</a>) когда вы сохраните свой шаблон, и вы должны увидеть что-то наподобие этого:</p>
<p><img alt="Library login page v1" src="https://mdn.mozillademos.org/files/14101/library_login.png" style="border-style: solid; border-width: 1px; display: block; height: 173px; margin: 0px auto; width: 441px;"></p>
<p><span id="result_box" lang="ru"><span>Если ваша попытка войти в систему будет успешной, вы будете перенаправлены на другую страницу (по умолчанию это будет <a href="http://127.0.0.1:8000/accounts/profile/">http://127.0.0.1:8000/accounts/profile/</a>).</span> <span>Проблема здесь в том, что по умолчанию Django ожидает, что после входа в систему вы захотите перейти на страницу профиля, что может быть или не быть.</span> <span>Поскольку вы еще не определили эту страницу, вы получите еще одну ошибку!</span><br>
<br>
<span>Откройте настройки проекта (<strong>/locallibrary/locallibrary/settings.py</strong>) и добавьте текст ниже.</span> <span>Теперь, когда вы входите в систему, вы по умолчанию должны перенаправляться на домашнюю страницу сайта.</span></span></p>
<pre class="brush: python notranslate"># Redirect to home URL after login (Default redirects to /accounts/profile/)
LOGIN_REDIRECT_URL = '/'
</pre>
<h3 id="Шаблон_выхода">Шаблон выхода</h3>
<p><span id="result_box" lang="ru"><span>Если вы перейдете по URL-адресу выхода (<a href="http://127.0.0.1:8000/accounts/logout/">http://127.0.0.1:8000/accounts/logout/</a>), то увидите странное поведение - </span></span>ваш пользователь наверняка выйдет из системы<span lang="ru"><span>, </span></span>но вы попадете на страницу выхода администратора<span lang="ru"><span>.</span> </span>Это не то, что вам нужно, хотя бы потому, что ссылка для входа на этой странице приведет вас к экрану входа в систему администратора.<span lang="ru"><span> (и это доступно только для пользователей, у которых есть разрешение <code>is_staff</code>).</span><br>
<br>
<span>Создайте и откройте <strong>/locallibrary/templates/registration/logged_out.html</strong>.</span> <span>Скопируйте текст ниже:</span></span></p>
<pre class="brush: html notranslate">{% extends "base_generic.html" %}
{% block content %}
<p>Logged out!</p>
<a href="{% url 'login'%}">Click here to login again.</a>
{% endblock %}</pre>
<p><span id="result_box" lang="ru"><span>Этот шаблон очень прост.</span> <span>Он просто отображает сообщение, информирующее вас о том, что вы вышли из системы, и предоставляет ссылку, которую вы можете нажать, чтобы вернуться на экран входа в систему.</span> <span>Если вы снова перейдете на страницу выхода из системы, вы увидите эту страницу:</span></span></p>
<p><img alt="Library logout page v1" src="https://mdn.mozillademos.org/files/14103/library_logout.png" style="border-style: solid; border-width: 1px; display: block; height: 169px; margin: 0px auto; width: 385px;"></p>
<h3 id="Шаблон_сброса_пароля">Шаблон сброса пароля</h3>
<p><span id="result_box" lang="ru"><span>Система сброса пароля по умолчанию использует электронную почту, чтобы отправить пользователю ссылку на сброс.</span> <span>Вам необходимо создать формы, чтобы получить адрес электронной почты пользователя, отправить электронное письмо, разрешить им вводить новый пароль и отметить, когда весь процесс будет завершен.</span><br>
<br>
<span>В качестве отправной точки можно использовать следующие шаблоны.</span></span></p>
<h4 id="Форма_сброса_пароля">Форма сброса пароля</h4>
<p><span id="result_box" lang="ru"><span>Это форма, используемая для получения адреса электронной почты пользователя (для отправки пароля для сброса пароля).</span> <span>Создайте <strong>/locallibrary/templates/registration/password_reset_form.html</strong> и дайте ему следующее содержание:</span></span></p>
<pre class="brush: html notranslate">{% extends "base_generic.html" %}
{% block content %}
<form action="" method="post">{% csrf_token %}
{% if form.email.errors %} \{{ form.email.errors }} {% endif %}
<p>\{{ form.email }}</p>
<input type="submit" class="btn btn-default btn-lg" value="Reset password" />
</form>
{% endblock %}
</pre>
<h4 id="Сброс_пароля">Сброс пароля</h4>
<p>Эта форма отображается после того, как ваш адрес электронной почты будет собран. Создайте <strong>/locallibrary/templates/registration/password_reset_done.html</strong>, и дайте ему следующее содержание:</p>
<pre class="brush: html notranslate">{% extends "base_generic.html" %}
{% block content %}
<p>We've emailed you instructions for setting your password. If they haven't arrived in a few minutes, check your spam folder.</p>
{% endblock %}
</pre>
<h4 id="Сброс_пароля_по_email">Сброс пароля по email</h4>
<p>Этот шаблон предоставляет текст электронной почты HTML, содержащий ссылку на сброс, которую мы отправим пользователям. Создайте /locallibrary/templates/registration/password_reset_email.html и дайте ему следующее содержание:</p>
<pre class="brush: html notranslate">Someone asked for password reset for email \{{ email }}. Follow the link below:
\{{ protocol}}://\{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
</pre>
<h4 id="Подтверждение_на_сброс_пароля">Подтверждение на сброс пароля</h4>
<p>На этой странице вы вводите новый пароль после нажатия ссылки в электронном письме с возвратом пароля. Создайте /locallibrary/templates/registration/password_reset_confirm.html и дайте ему следующее содержание:</p>
<pre class="brush: html notranslate">{% extends "base_generic.html" %}
{% block content %}
{% if validlink %}
<p>Please enter (and confirm) your new password.</p>
<form action="" method="post">
<code class="language-html">{% csrf_token %}</code>
<table>
<tr>
<td>\{{ form.new_password1.errors }}
<label for="id_new_password1">New password:</label></td>
<td>\{{ form.new_password1 }}</td>
</tr>
<tr>
<td>\{{ form.new_password2.errors }}
<label for="id_new_password2">Confirm password:</label></td>
<td>\{{ form.new_password2 }}</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Change my password" /></td>
</tr>
</table>
</form>
{% else %}
<h1>Password reset failed</h1>
<p>The password reset link was invalid, possibly because it has already been used. Please request a new password reset.</p>
{% endif %}
{% endblock %}
</pre>
<h4 id="Сброс_пароля_завершен">Сброс пароля завершен</h4>
<p>Это последний шаблон сброса пароля, который отображается, чтобы уведомить вас о завершении сброса пароля. Создайте /locallibrary/templates/registration/password_reset_complete.html и дайте ему следующее содержание:</p>
<pre class="brush: html notranslate">{% extends "base_generic.html" %}
{% block content %}
<h1>The password has been changed!</h1>
<p><a href="{% url 'login' %}">log in again?</a></p>
{% endblock %}</pre>
<h3 id="Тестирование_новых_страниц_аутентификации">Тестирование новых страниц аутентификации</h3>
<p>Теперь, когда вы добавили конфигурацию URL и создали все эти шаблоны, теперь страницы аутентификации должны работать! Вы можете протестировать новые страницы аутентификации, попытавшись войти в систему, а затем выйдите из учетной записи суперпользователя, используя эти URL-адреса:</p>
<ul>
<li><a href="http://127.0.0.1:8000/accounts/login/">http://127.0.0.1:8000/accounts/login/</a></li>
<li><a href="http://127.0.0.1:8000/accounts/logout/">http://127.0.0.1:8000/accounts/logout/</a></li>
</ul>
<p>Вы сможете проверить функцию сброса пароля по ссылке на странице входа. <strong>Имейте в виду, что Django отправляет только сбросные электронные письма на адреса (пользователи), которые уже хранятся в его базе данных!</strong></p>
<div class="note">
<p><strong>Заметка</strong>: Система сброса пароля требует, чтобы ваш сайт поддерживал электронную почту, что выходит за рамки этой статьи, поэтому эта часть <strong>еще не будет работать.</strong> Чтобы разрешить тестирование, поместите следующую строку в конец файла settings.py. Это регистрирует любые письма, отправленные на консоль (чтобы вы могли скопировать ссылку на сброс пароля с консоли).</p>
<pre class="brush: python notranslate">EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
</pre>
<p>Для получения дополнительной информации см. <a href="https://docs.djangoproject.com/en/2.0/topics/email/">Отправка email</a> (Django docs).</p>
</div>
<h2 id="Тестирование_проверки_подлинности_пользователей">Тестирование проверки подлинности пользователей</h2>
<p>В этом разделе мы рассмотрим, что мы можем сделать, чтобы выборочно контролировать контент, который видят пользователи, на основе того, вошли ли они в систему или нет.</p>
<h3 id="Тестирование_в_шаблонах">Тестирование в шаблонах</h3>
<p>Вы можете получить информацию о текущем зарегистрированном пользователе в шаблонах с переменной шаблона \{{user}} (это добавляется в контекст шаблона по умолчанию при настройке проекта, как и в нашем скелете).</p>
<p>Обычно вы сначала проверяете переменную шаблона \{{user.is_authenticated}}, чтобы определить, имеет ли пользователь право видеть конкретный контент. Чтобы продемонстрировать это, мы обновим нашу боковую панель, чтобы отобразить ссылку «Вход», если пользователь вышел из системы, и ссылку «Выход», если он вошёл в систему.</p>
<p>Откройте базовый шаблон (/locallibrary/catalog/templates/base_generic.html) и скопируйте следующий текст в sidebar блок непосредственно перед тегом шаблона endblock.</p>
<pre class="brush: python notranslate"> <ul class="sidebar-nav">
...
<strong>{% if user.is_authenticated %}</strong>
<li>User: <strong>\{{ user.get_username }}</strong></li>
<li><a href="{% url 'logout'%}?next=\{{request.path}}">Logout</a></li>
<strong>{% else %}</strong>
<li><a href="{% url 'login'%}?next=\{{request.path}}">Login</a></li>
<strong>{% endif %} </strong>
</ul></pre>
<p>Как вы можете видеть, мы используем теги шаблона if-else-endif для условного отображения текста на основе того, является ли \{{user.is_authenticated}} истинным. Если пользователь аутентифицирован, мы знаем, что у нас есть действительный пользователь, поэтому мы вызываем \{{user.get_username}}, чтобы отобразить их имя.</p>
<p>Мы создаем URL-адрес для входа и выхода из системы, используя тег шаблона URL-адреса и имена соответствующих конфигураций URLs. Также обратите внимание на то, как мы добавили <code>?next=\{{request.path}}</code> в конец URLs. Это означает, что следующий URL-адрес содержит адрес (URL) текущей страницы, в конце связанного URL-адреса. После того, как пользователь успешно выполнил вход в систему, представления будут использовать значение "<code>next</code>" чтобы перенаправить пользователя обратно на страницу, где они сначала нажали ссылку входа / выхода из системы.</p>
<div class="note">
<p><strong>Примечание</strong>: Попробуйте! Если вы находитесь на главной странице и вы нажимаете «Вход / Выход» на боковой панели, то после завершения операции вы должны вернуться на ту же страницу.</p>
</div>
<h3 id="Тестирование_в_представлениях">Тестирование в представлениях</h3>
<p>Если вы используете функциональные представления, самым простым способом ограничить доступ к вашим функциям является применение <code>login_required</code> декоратор к вашей функции просмотра, как показано ниже. Если пользователь вошел в систему, ваш код просмотра будет выполняться как обычно. Если пользователь не вошел в систему, это перенаправит URL-адрес входа, определенный в настройках проекта. (<code>settings.LOGIN_URL</code>), передав текущий абсолютный путь в качестве <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">next</span></font> параметра URL. Если пользователю удастся войти в систему, они будут возвращены на эту страницу, но на этот раз аутентифицированы.</p>
<pre class="brush: python notranslate">from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
...</pre>
<div class="note">
<p><strong>Заметка:</strong> Вы можете сделать то же самое вручную, путём тестирования <code>request.user.is_authenticated</code>, но декоратор намного удобнее!</p>
</div>
<p>Аналогичным образом, самый простой способ ограничить доступ к зарегистрированным пользователям в ваших представлениях на основе классов - это производные от <code>LoginRequiredMixin</code>. Вы должны объявить этот mixin сначала в списке суперкласса, перед классом основного представления.</p>
<pre class="brush: python notranslate">from django.contrib.auth.mixins import LoginRequiredMixin
class MyView(LoginRequiredMixin, View):
...</pre>
<p>Это имеет такое же поведение при переадресации, что и <code>login_required</code> декоратор. Вы также можете указать альтернативное местоположение для перенаправления пользователя, если он не аутентифицирован (<code>login_url</code>), и имя параметра URL вместо "<code>next</code>" , чтобы вставить текущий абсолютный путь (<code>redirect_field_name</code>).</p>
<pre class="brush: python notranslate">class MyView(LoginRequiredMixin, View):
login_url = '/login/'
redirect_field_name = 'redirect_to'
</pre>
<p>Для получения дополнительной информации ознакомьтесь с <a href="https://docs.djangoproject.com/en/1.10/topics/auth/default/#limiting-access-to-logged-in-users">Django docs here</a>.</p>
<h2 id="Пример_-_перечисление_книг_текущего_пользователя">Пример - перечисление книг текущего пользователя</h2>
<p>Теперь, когда мы знаем, как ограничить страницу определенному пользователю, создайте представление о книгах, которые заимствовал текущий пользователь.</p>
<p>К сожалению, у нас пока нет возможности пользователям использовать книги! Поэтому, прежде чем мы сможем создать список книг, мы сначала расширим <code>BookInstance</code> модель для поддержки концепции заимствования и использования приложения Django Admin для заимствования ряда книг нашему тестовому пользователю.</p>
<h3 id="Модели">Модели</h3>
<p>Прежде всего, мы должны предоставить пользователям возможность кредита на <code>BookInstance</code> (у нас уже есть <code>status</code> и <code>due_back</code> дата, но у нас пока нет связи между этой моделью и пользователем. Мы создадим его с помощью поля <code>ForeignKey</code> (один ко многим). Нам также нужен простой механизм для проверки того, просрочена ли заемная книга.</p>
<p>Откройте <strong>catalog/models.py</strong>, и импортируйте модель <code>User</code> из <code>django.contrib.auth.models</code> (добавьте это чуть ниже предыдущей строки импорта в верхней части файла, так <code>User</code> доступен для последующего кода, что позволяет использовать его):</p>
<pre class="brush: python notranslate">from django.contrib.auth.models import User
</pre>
<p>Затем добавьте поле <code>borrower</code> в модель <code>BookInstance</code>:</p>
<pre class="brush: python notranslate">borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
</pre>
<p>Пока мы здесь, давайте добавим свойство, которое мы можем вызвать из наших шаблонов, чтобы указать, просрочен ли конкретный экземпляр книги. Хотя мы могли бы рассчитать это в самом шаблоне, использование свойства, как показано ниже, будет намного более эффективным. Добавьте это где-нибудь в верхней части файла:</p>
<pre class="syntaxbox notranslate">from datetime import date</pre>
<p>Теперь добавьте следующее определение свойства внутри класса BookInstance:</p>
<pre class="brush: python notranslate">@property
def is_overdue(self):
if self.due_back and date.today() > self.due_back:
return True
return False</pre>
<div class="note">
<p><strong>Примечание.</strong> Сначала мы проверим, является ли <code>due_back</code> пустым, прежде чем проводить сравнение. Пустое поле <code>due_back</code> заставило Django выкидывать ошибку, а не показывать страницу: пустые значения не сопоставимы. Это не то, что мы хотели бы, чтобы наши пользователи испытывали!</p>
</div>
<p>Теперь, когда мы обновили наши модели, нам нужно будет внести новые изменения в проект, а затем применить эти миграции:</p>
<pre class="brush: bash notranslate">python3 manage.py makemigrations
python3 manage.py migrate
</pre>
<h3 id="Admin">Admin</h3>
<p>Теперь откройте каталог <strong>catalog/admin.py</strong>, и добавьте поле <code>borrower</code> в класс <code>BookInstanceAdmin</code> , как в <code>list_display</code> , так и в полях <code>fieldsets</code> , как показано ниже. Это сделает поле видимым в разделе Admin, так что мы можем при необходимости назначить <code>User</code> в <code>BookInstance</code>.</p>
<pre class="brush: python notranslate">@admin.register(BookInstance)
class BookInstanceAdmin(admin.ModelAdmin):
list_display = ('book', 'status'<strong>, 'borrower'</strong>, 'due_back', 'id')
list_filter = ('status', 'due_back')
fieldsets = (
(None, {
'fields': ('book','imprint', 'id')
}),
('Availability', {
'fields': ('status', 'due_back'<strong>,'borrower'</strong>)
}),
)</pre>
<h3 id="Займите_несколько_книг">Займите несколько книг</h3>
<p>Теперь, когда возможно кредитовать книги конкретному пользователю, зайдите и заработайте на нескольких записей в <code>BookInstance</code>. Установите <code>borrowed</code> поле вашему тестовому пользователю, сделайте <code>status</code> «В займе» и установите сроки оплаты как в будущем, так и в прошлом.</p>
<div class="note">
<p><strong>Заметка:</strong> Мы не будем описывать процесс, так как вы уже знаете, как использовать Admin сайт!</p>
</div>
<h3 id="Займ_в_представлении">Займ в представлении</h3>
<p>Теперь мы добавим представление для получения списка всех книг, которые были предоставлены текущему пользователю. Мы будем использовать один и тот же общий класс, с которым мы знакомы, но на этот раз мы также будем импортировать и выводить из <code>LoginRequiredMixin</code>, так что только вошедший пользователь сможет вызвать это представление. Мы также решили объявить <code>template_name</code>, вместо того, чтобы использовать значение по умолчанию, потому что у нас может быть несколько разных списков записей BookInstance, с разными представлениями и шаблонами.</p>
<p>Добавьте следующее в catalog/views.py:</p>
<pre class="brush: python notranslate">from django.contrib.auth.mixins import LoginRequiredMixin
class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
"""
Generic class-based view listing books on loan to current user.
"""
model = BookInstance
template_name ='catalog/bookinstance_list_borrowed_user.html'
paginate_by = 10
def get_queryset(self):
return BookInstance.objects.filter(borrower=self.request.user).filter(status__exact='o').order_by('due_back')</pre>
<p>Чтобы ограничить наш запрос только объектами BookInstance для текущего пользователя, мы повторно реализуем <code>get_queryset()</code>, как показано выше. Обратите внимание, что "o" это сохраненный код для "on loan" и мы сортируем по дате <code>due_back</code>, чтобы сначала отображались самые старые элементы.</p>
<h3 id="URL-адрес_для_заёмных_книг">URL-адрес для заёмных книг</h3>
<p>Теперь откройте <strong>/catalog/urls.py</strong> и добавьте <code>url()</code> , указывая на приведённое выше представление (вы можете просто скопировать текст ниже в конец файла).</p>
<pre class="brush: python notranslate">urlpatterns += [
url(r'^mybooks/$', views.LoanedBooksByUserListView.as_view(), name='my-borrowed'),
]</pre>
<h3 id="Шаблон_для_заёмных_книг">Шаблон для заёмных книг</h3>
<p>Теперь все, что нам нужно сделать для этой страницы, - это добавить шаблон. Сначала создайте файл шаблона <strong>/catalog/templates/catalog/bookinstance_list_borrowed_user.html</strong> и дайте ему следующее содержание:</p>
<pre class="brush: python notranslate">{% extends "base_generic.html" %}
{% block content %}
<h1>Borrowed books</h1>
{% if bookinstance_list %}
<ul>
{% for bookinst in bookinstance_list %}
<li class="{% if bookinst.is_overdue %}text-danger{% endif %}">
<a href="{% url 'book-detail' bookinst.book.pk %}">\{{bookinst.book.title}}</a> (\{{ bookinst.due_back }})
</li>
{% endfor %}
</ul>
{% else %}
<p>There are no books borrowed.</p>
{% endif %}
{% endblock %}</pre>
<p>Этот шаблон очень похож на тот, который мы создали ранее для объектов <code>Book</code> и <code>Author</code>. Единственное, что «новое» здесь, это то, что мы проверяем метод, который мы добавили в модель <code>(bookinst.is_overdue</code>) с целью использовать его для изменения цвета просроченных предметов.</p>
<p>Когда сервер разработки запущен, вы должны теперь иметь возможность просматривать список для зарегистрированного пользователя в своем браузере по адресу <a href="http://127.0.0.1:8000/catalog/mybooks/">http://127.0.0.1:8000/catalog/mybooks/</a>. Попробуйте это, когда ваш пользователь войдет в систему и выйдет из системы (во втором случае вы должны быть перенаправлены на страницу входа в систему).</p>
<h3 id="Добавить_список_на_боковую_панель">Добавить список на боковую панель</h3>
<p>Последний шаг - добавить ссылку на эту новую страницу в sidebar. Мы поместим это в тот же раздел, где мы покажем другую информацию для зарегистрированного пользователя.</p>
<p>Откройте базовый шаблон (<strong>/locallibrary/catalog/templates/base_generic.html</strong>) и добавьте выделенную строку из sidebar, как показано на рисунке.</p>
<pre class="brush: python notranslate"> <ul class="sidebar-nav">
{% if user.is_authenticated %}
<li>User: \{{ user.get_username }}</li>
<strong> <li><a href="{% url 'my-borrowed' %}">My Borrowed</a></li></strong>
<li><a href="{% url 'logout'%}?next=\{{request.path}}">Logout</a></li>
{% else %}
<li><a href="{% url 'login'%}?next=\{{request.path}}">Login</a></li>
{% endif %}
</ul>
</pre>
<h3 id="На_что_это_похоже">На что это похоже?</h3>
<p>Когда любой пользователь войдет в систему, он будет видеть ссылку «Мной позаимствовано (<em>My Borrowed)</em>» в боковой колонке, и список книг, показанных ниже (первая книга не имеет установленной даты, что является ошибкой, которую мы надеемся исправить в более позднем уроке!).</p>
<p><img alt="Library - borrowed books by user" src="https://mdn.mozillademos.org/files/14105/library_borrowed_by_user.png" style="border-style: solid; border-width: 1px; display: block; height: 215px; margin: 0px auto; width: 530px;"></p>
<h2 id="Права_доступа">Права доступа</h2>
<p>Права доступа связаны с моделями и определяют операции, которые могут выполняться на экземпляре модели самим пользователем, у которого есть разрешение. По умолчанию Django автоматически дает <em>добавить</em>, <em>изменить</em>, и <em>удалить</em> разрешения у всех моделей, которые позволяют пользователям с правом доступа выполнять связанные действия через администратора сайта. Вы можете определить свои собственные разрешения для моделей и предоставить их конкретным пользователям. Вы также можете изменить разрешения, связанные с разными экземплярами одной и той же модели. Тестирование разрешений в представлениях и шаблонах очень похоже на тестирование по статусу аутентификации (фактически, тестирование прав доступа также проверяет аутентификацию).</p>
<h3 id="Модели_2">Модели</h3>
<p>Определение разрешений выполняется в разделе моделей "<code>class Meta</code>" , используется <code>permissions</code> поле. Вы можете указать столько разрешений, сколько необходимо в кортеже, причем каждое разрешение определяется во вложенном кортеже, содержащем имя разрешения и отображаемое значение разрешения. Например, мы можем определить разрешение, позволяющее пользователю отметить, что книга была возвращена, как показано здесь:</p>
<pre class="brush: python notranslate">class BookInstance(models.Model):
...
class Meta:
...
<strong> permissions = (("can_mark_returned", "Set book as returned"),) </strong> </pre>
<p>Затем мы могли бы назначить разрешение группе «Библиотекарь» (Librarian) на сайте администратора.</p>
<p>Откройте <strong>catalog/models.py</strong>, и добавьте разрешение, как показано выше. Вам нужно будет повторно выполнить миграцию (вызвав <code>python3 manage.py makemigrations</code> и <code>python3 manage.py migrate</code>) для надлежащего обновления базы данных.</p>
<h3 id="Шаблоны">Шаблоны</h3>
<p>Разрешения текущего пользователя хранятся в переменной шаблона, называемой <code>\{{ perms }}</code>. Вы можете проверить, имеет ли текущий пользователь определенное разрешение, используя конкретное имя переменной в соответствующем приложении «Django» - например, <code>\{{ perms.catalog.can_mark_returned }}</code> будет <code>True</code> если у пользователя есть это разрешение, а <code>False</code> - в противном случае. Обычно мы проверяем разрешение с использованием шаблона <code>{% if %}</code>, как показано в:</p>
<pre class="brush: python notranslate">{% if perms.catalog.<code>can_mark_returned</code> %}
<!-- We can mark a BookInstance as returned. -->
<!-- Perhaps add code to link to a "book return" view here. -->
{% endif %}
</pre>
<h3 id="Представления">Представления</h3>
<p>Разрешения можно проверить в представлении функции, используя <code>permission_required</code> декоратор или в представлении на основе классов, используя <code>PermissionRequiredMixin</code>. шаблон и поведение такие же, как для аутентификации входа в систему, хотя, конечно, вы можете разумно добавить несколько разрешений.</p>
<p>Функция в представлении с декоратором:</p>
<pre class="brush: python notranslate">from django.contrib.auth.decorators import permission_required
@permission_required('catalog.<code>can_mark_returned</code>')
@permission_required('catalog.<code>can_edit</code>')
def my_view(request):
...</pre>
<p>Требуется разрешение mixin для представлений на основе классов.</p>
<pre class="brush: python notranslate">from django.contrib.auth.mixins import PermissionRequiredMixin
class MyView(PermissionRequiredMixin, View):
permission_required = 'catalog.<code>can_mark_returned</code>'
# Or multiple permissions
permission_required = ('catalog.<code>can_mark_returned</code>', 'catalog.can_edit')
# Note that 'catalog.can_edit' is just an example
# the catalog application doesn't have such permission!</pre>
<h3 id="Пример">Пример</h3>
<p>Мы не будем обновлять LocalLibrary здесь; возможно, в следующем уроке!</p>
<h2 id="Испытайте_себя">Испытайте себя</h2>
<p> Ранее в этой статье мы показали вам, как создать страницу для текущего пользователя, в которой перечислены книги, которые они заимствовали. Теперь задача состоит в том, чтобы создать аналогичную страницу, которая видна только для библиотекарей, которая отображает <em>все</em> книги, которые были заимствованы, и которая показывает имя каждого заемщика.</p>
<p> Вы должны следовать той же схеме, что и для другого представления. Главное отличие состоит в том, что вам нужно ограничить представление только библиотекарями. Вы можете сделать это на основе того, является ли пользователь сотрудником (декоратор функции: <code>staff_member_required</code>, переменная шаблона: <code>user.is_staff</code>) но мы рекомендуем вам вместо этого использовать <code>can_mark_returned</code> разрешения и <code>PermissionRequiredMixin</code>, как описано в предыдущем разделе.</p>
<div class="warning">
<p><strong>Важно</strong>: Не забудьте использовать вашего суперпользователя для тестирования на основе разрешений (проверки разрешений всегда возвращают true для суперпользователей, даже если разрешение еще не определено!). Вместо этого создайте пользователя-библиотекаря и добавьте необходимые возможности.</p>
</div>
<p> Когда вы закончите, ваша страница должна выглядеть примерно, как на скриншоте ниже.</p>
<p><img alt="All borrowed books, restricted to librarian" src="https://mdn.mozillademos.org/files/14115/library_borrowed_all.png" style="border-style: solid; border-width: 1px; display: block; height: 283px; margin: 0px auto; width: 500px;"></p>
<ul>
</ul>
<h2 id="Подводим_итоги">Подводим итоги</h2>
<p> Отличная работа - теперь вы создали веб-сайт, на котором участники библиотеки могут входить в систему и просматривать собственный контент, и библиотекари (с правом доступа) могут просматривать все заемные книги с их читателями. На данный момент мы все еще просто просматриваем контент, но те же принципы и методы используются, когда вы хотите начать изменять и добавлять данные.</p>
<p> В следующей статье мы рассмотрим, как вы можете использовать формы Django для сбора пользовательского ввода, а затем начнём изменять некоторые из наших сохраненных данных.</p>
<h2 id="Смотрите_также">Смотрите также</h2>
<ul>
<li><a href="https://docs.djangoproject.com/en/1.10/topics/auth/">User authentication in Django</a> (Django docs)</li>
<li><a href="https://docs.djangoproject.com/en/1.10/topics/auth/default//">Using the (default) Django authentication system</a> (Django docs)</li>
<li><a href="https://docs.djangoproject.com/en/1.10/topics/class-based-views/intro/#decorating-class-based-views">Introduction to class-based views > Decorating class-based views</a> (Django docs)</li>
</ul>
<p>{{PreviousMenuNext("Learn/Server-side/Django/Sessions", "Learn/Server-side/Django/Forms", "Learn/Server-side/Django")}}</p>
|