добавил всего по мелочи, отредактировал urls, добавил файлы библиотеки Chart.js
This commit is contained in:
parent
1ac1a8cc14
commit
4c94a7271a
23
index/urls.py
Normal file
23
index/urls.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
"""index URL Configuration
|
||||||
|
|
||||||
|
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||||
|
https://docs.djangoproject.com/en/3.2/topics/http/urls/
|
||||||
|
Examples:
|
||||||
|
Function views
|
||||||
|
1. Add an import: from my_app import views
|
||||||
|
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||||
|
Class-based views
|
||||||
|
1. Add an import: from other_app.views import Home
|
||||||
|
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||||
|
Including another URLconf
|
||||||
|
1. Import the include() function: from django.urls import include, path
|
||||||
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', views.view_index, name='index'),
|
||||||
|
# path('methods/<str:method_name>', views.call_method, name='call_method')
|
||||||
|
]
|
@ -1,3 +1,8 @@
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
from django.db.models import Manager
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
|
|
||||||
|
def view_index(request):
|
||||||
|
return render(request, 'index.html')
|
||||||
|
@ -26,15 +26,19 @@ PROJECT_ROOT = os.path.dirname(__file__)
|
|||||||
# Quick-start development settings - unsuitable for production
|
# Quick-start development settings - unsuitable for production
|
||||||
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
|
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
SECRET_KEY = os.getenv('DJANGO_SECRET')
|
||||||
SECRET_KEY = 'django-insecure-_5hkdwg1(1b)z#^wi#edgm=ustw5-g1qd346@pdm4eexlhxc&4'
|
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
|
||||||
DEBUG = True
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['localhost']
|
ALLOWED_HOSTS = ['localhost']
|
||||||
# CSRF_TRUSTED_ORIGINS = ['https://ospaz.wawaa.ru']
|
# CSRF_TRUSTED_ORIGINS = ['https://ospaz.wawaa.ru']
|
||||||
|
|
||||||
|
# HTTPS settings https://docs.djangoproject.com/en/5.0/topics/security/
|
||||||
|
#CSRF_COOKIE_SECURE = True
|
||||||
|
#SESSION_COOKIE_SECURE = True
|
||||||
|
#DEBUG = False
|
||||||
|
|
||||||
|
# HTTP settings
|
||||||
|
DEBUG = True
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
@ -66,7 +70,7 @@ ROOT_URLCONF = 'ospaz_site.urls'
|
|||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': [],
|
'DIRS': [os.path.join(BASE_DIR, 'templates')],
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'context_processors': [
|
'context_processors': [
|
||||||
@ -124,9 +128,9 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/5.0/topics/i18n/
|
# https://docs.djangoproject.com/en/5.0/topics/i18n/
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
LANGUAGE_CODE = 'ru-RU'
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
TIME_ZONE = 'Europe/Moscow'
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
@ -137,6 +141,9 @@ USE_TZ = True
|
|||||||
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
||||||
|
|
||||||
STATIC_URL = 'static/'
|
STATIC_URL = 'static/'
|
||||||
|
STATICFILES_DIRS = [
|
||||||
|
os.path.join(BASE_DIR, 'static')
|
||||||
|
]
|
||||||
|
|
||||||
# Default primary key field type
|
# Default primary key field type
|
||||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
||||||
|
@ -15,8 +15,14 @@ Including another URLconf
|
|||||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
"""
|
"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path
|
from django.urls import path, include
|
||||||
|
|
||||||
|
from django.contrib.staticfiles.views import serve
|
||||||
|
|
||||||
|
import index.urls
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
path('', include('index.urls')),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
|
path('favicon.ico', lambda req: serve(req, 'favicon.svg'))
|
||||||
]
|
]
|
||||||
|
79
static/css/style.css
Normal file
79
static/css/style.css
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/* TODO исправить стили, тут верхней навигации вообще нет */
|
||||||
|
|
||||||
|
|
||||||
|
/* ========== THEME ========== */
|
||||||
|
body {
|
||||||
|
--text-color: #111;
|
||||||
|
--brand-color: #231765;
|
||||||
|
--bkg-color-blue: #0066e3;
|
||||||
|
|
||||||
|
--bkg-color: #fff;
|
||||||
|
--bkg-color2: #ccc;
|
||||||
|
--bkg-color3: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
/* defaults to dark theme */
|
||||||
|
body {
|
||||||
|
--text-color: #eee;
|
||||||
|
--brand-color: #654dea;
|
||||||
|
--bkg-color-blue: #003aac;
|
||||||
|
|
||||||
|
--bkg-color: #121212;
|
||||||
|
--bkg-color2: #202020;
|
||||||
|
--bkg-color3: #353435;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--bkg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
text-align: center;
|
||||||
|
margin: 1em 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== MAIN STYLES ========== */
|
||||||
|
|
||||||
|
#header-wrapper {
|
||||||
|
display: flex;
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header-wrapper * {
|
||||||
|
color: var(--brand-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > * {
|
||||||
|
margin: auto 0.5em;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > div > * {
|
||||||
|
display: block;
|
||||||
|
margin: 0.1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logo-text {
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: xx-large;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logo-image {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
2
static/favicon.svg
Normal file
2
static/favicon.svg
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:se="http://svg-edit.googlecode.com" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="85" height="80" style=""> <title>favicon</title>
|
||||||
|
<rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill-opacity="0.0" stroke="none" style="" class=""/> <g class="currentLayer" style=""><title>Layer 1</title><rect fill="#4a90d6" stroke="#222222" stroke-width="2" stroke-linejoin="round" stroke-dashoffset="" fill-rule="nonzero" id="svg_38" x="77.39605712890625" y="107.25747680664062" width="7" height="0" style="color: rgb(0, 0, 0);" class=""/><path fill="#4a90d6" stroke="#222222" stroke-width="2" stroke-linejoin="round" stroke-dashoffset="" fill-rule="nonzero" marker-start="" marker-mid="" marker-end="" id="svg_42" d="M4.913043206854184,73.1181132105101 L23.991979764689916,39.72997423429874 L43.07091632252451,73.1181132105101 L4.913043206854184,73.1181132105101 z" style="color: rgb(0, 0, 0);" class=""/><path fill="#4a90d6" stroke="#222222" stroke-width="2" stroke-linejoin="round" stroke-dashoffset="" fill-rule="nonzero" marker-start="" marker-mid="" marker-end="" d="M43.558975285215695,73.08701234133133 L62.63791184305143,39.698873365119965 L81.71684840088602,73.08701234133133 L43.558975285215695,73.08701234133133 z" style="color: rgb(0, 0, 0);" class="" id="svg_47"/><path fill="#4a90d6" stroke="#222222" stroke-width="2" stroke-linejoin="round" stroke-dashoffset="" fill-rule="nonzero" marker-start="" marker-mid="" marker-end="" d="M24.25514847628625,39.17074458868393 L43.33408503412198,5.782605612472565 L62.41302159195658,39.17074458868393 L24.25514847628625,39.17074458868393 z" style="color: rgb(0, 0, 0);" class="" id="svg_46"/><path fill="#4a90d6" stroke="#222222" stroke-width="2" stroke-linejoin="round" stroke-dashoffset="" fill-rule="nonzero" marker-start="" marker-mid="" marker-end="" id="svg_48" d="M33.91763093443063,55.92877816603246 L43.29425964116126,40.29240521591228 L52.6708883478924,55.92877816603246 L33.91763093443063,55.92877816603246 z" style="color: rgb(0, 0, 0);" class=""/></g></svg>
|
After Width: | Height: | Size: 2.3 KiB |
14
static/js/chart-4.4.1.umd.js
Normal file
14
static/js/chart-4.4.1.umd.js
Normal file
File diff suppressed because one or more lines are too long
7
static/js/chartjs-adapter-moment.js
Normal file
7
static/js/chartjs-adapter-moment.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/*!
|
||||||
|
* chartjs-adapter-moment v1.0.1
|
||||||
|
* https://www.chartjs.org
|
||||||
|
* (c) 2022 chartjs-adapter-moment Contributors
|
||||||
|
* Released under the MIT license
|
||||||
|
*/
|
||||||
|
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("moment"),require("chart.js")):"function"==typeof define&&define.amd?define(["moment","chart.js"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).moment,e.Chart)}(this,(function(e,t){"use strict";function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var f=n(e);const a={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};t._adapters._date.override("function"==typeof f.default?{_id:"moment",formats:function(){return a},parse:function(e,t){return"string"==typeof e&&"string"==typeof t?e=f.default(e,t):e instanceof f.default||(e=f.default(e)),e.isValid()?e.valueOf():null},format:function(e,t){return f.default(e).format(t)},add:function(e,t,n){return f.default(e).add(t,n).valueOf()},diff:function(e,t,n){return f.default(e).diff(f.default(t),n)},startOf:function(e,t,n){return e=f.default(e),"isoWeek"===t?(n=Math.trunc(Math.min(Math.max(0,n),6)),e.isoWeekday(n).startOf("day").valueOf()):e.startOf(t).valueOf()},endOf:function(e,t){return f.default(e).endOf(t).valueOf()}}:{})}));
|
15
static/js/moment-2.js
Normal file
15
static/js/moment-2.js
Normal file
File diff suppressed because one or more lines are too long
20
templates/base.html
Normal file
20
templates/base.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta name="description" content="Состояние резервуара, насосной станции и график резервуара">
|
||||||
|
<title> {% block title %} Мониторинг резервуара {% endblock %} </title>
|
||||||
|
{% load static %}
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">
|
||||||
|
{% block styles %} {% endblock %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
{% block header %} тут должен быть хидер {% endblock %}
|
||||||
|
</header>
|
||||||
|
<main id="content">
|
||||||
|
{% block content %} тут должен быть контент {% endblock %}
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
164
templates/index.html
Normal file
164
templates/index.html
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block header %}
|
||||||
|
<h1 class="page-header"> Мониторинг водозаборного узла </h1>
|
||||||
|
{% if user.is_superuser %}
|
||||||
|
<a href="/admin">Админка</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<script>
|
||||||
|
function getAccessToken(new_value) {
|
||||||
|
if (new_value === undefined || new_value === null || new_value === "") {
|
||||||
|
let res = localStorage.getItem("access_token")
|
||||||
|
if (res === null) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
} else {
|
||||||
|
console.log(`Storing ${new_value} as token`)
|
||||||
|
localStorage.setItem("access_token", new_value)
|
||||||
|
document.getElementById('current_access_token').innerText = new_value
|
||||||
|
return new_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function sendRequest(method, params) {
|
||||||
|
let url = `/methods/${method}`
|
||||||
|
if (params !== undefined && params !== null) {
|
||||||
|
url += "?" + new URLSearchParams(params)
|
||||||
|
}
|
||||||
|
return await fetch(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function makeRequest(view, method, inputs) {
|
||||||
|
let params = {}
|
||||||
|
for (let k in inputs) {
|
||||||
|
let element = document.getElementById(inputs[k])
|
||||||
|
const name = element.name
|
||||||
|
let val = element.value
|
||||||
|
if (name === "access_token") {
|
||||||
|
val = getAccessToken(val)
|
||||||
|
}
|
||||||
|
if (val.length > 0)
|
||||||
|
params[name] = val
|
||||||
|
}
|
||||||
|
let res = await sendRequest(method, params)
|
||||||
|
const text = await res.text()
|
||||||
|
document.getElementById(view).innerText = text
|
||||||
|
|
||||||
|
// чтобы запоминался токен
|
||||||
|
try {
|
||||||
|
let j = JSON.parse(text)
|
||||||
|
getAccessToken(j["response"]["access_token"])
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% for method in api_methods %}
|
||||||
|
<div class="method-div">
|
||||||
|
<details>
|
||||||
|
<summary>{{ method.name }}</summary>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>Описание</h3>
|
||||||
|
<p>
|
||||||
|
{{ method.doc | safe }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Параметры</h3>
|
||||||
|
{% if method.params %}
|
||||||
|
<div class="table-wrapper"><table>
|
||||||
|
<tr>
|
||||||
|
<th>Название</th>
|
||||||
|
<th>Тип</th>
|
||||||
|
<th>Описание</th>
|
||||||
|
<th>Обязательный</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% for param in method.params %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ param.name }}</td>
|
||||||
|
<td>{{ param.type }}</td>
|
||||||
|
<td>{{ param.description | safe }}</td>
|
||||||
|
<td>{{ param.required }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table></div>
|
||||||
|
{% else %}
|
||||||
|
<p>
|
||||||
|
Этот метод не принимает параметров.
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<h3>Результат</h3>
|
||||||
|
<p>
|
||||||
|
{{ method.returns | safe }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Ссылка на метод (без параметров): <a href="/methods/{{ method.name }}">{{ method.name }}</a>
|
||||||
|
</p>
|
||||||
|
<details>
|
||||||
|
<summary>Конструктор</summary>
|
||||||
|
<div class="constructor-wrapper" id="view-{{ method.name }}">
|
||||||
|
<div class="constructor-fields">
|
||||||
|
<div style="">
|
||||||
|
<h3>Параметры</h3>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if method.params %}
|
||||||
|
{% for param in method.params %}
|
||||||
|
<div class="constructor-param">
|
||||||
|
<label for="param-{{ method.name }}-{{ param.name }}">{{ param.name }}</label>
|
||||||
|
<input type="text" name="{{ param.name }}" id="param-{{ method.name }}-{{ param.name }}">
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<div class="constructor-param">
|
||||||
|
<p>
|
||||||
|
Этот метод не принимает параметров.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="constructor-param">
|
||||||
|
<button onclick="makeRequest('result-{{ method.name }}', '{{ method.name }}',
|
||||||
|
[{% for param in method.params %}'param-{{ method.name }}-{{ param.name }}', {% endfor %}])">Выполнить</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="constructor-result">
|
||||||
|
<h3>Результат</h3>
|
||||||
|
<hr>
|
||||||
|
<pre id="result-{{ method.name }}"></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<div style="text-align: center; background: var(--bkg-color2); margin: 0; margin-top: 3em; padding: 2em; overflow-wrap: break-word;">
|
||||||
|
Перейти в <a href="/admin">админку</a>.
|
||||||
|
<div>
|
||||||
|
Текущий токен: <i id="current_access_token"></i><br><a onclick="localStorage.clear(); document.getElementById('current_access_token').innerText = ''">Сбросить</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.onload = (event) => {
|
||||||
|
const at = localStorage.getItem("access_token")
|
||||||
|
if (at !== null) {
|
||||||
|
document.getElementById('current_access_token').innerText = at
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
@ -1,8 +1,13 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import User
|
from .models import User
|
||||||
|
from django.contrib.auth.models import Group
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.unregister(Group)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(User)
|
@admin.register(User)
|
||||||
class UserAdmin(admin.ModelAdmin):
|
class UserAdmin(admin.ModelAdmin):
|
||||||
list_display = ['id', 'login', 'is_superuser', 'registered', 'last_password_change']
|
# fields = ['id', 'login', 'is_superuser', 'registered', 'last_password_change', 'last_login']
|
||||||
readonly_fields = ['id', 'registered']
|
list_display = ['id', 'login', 'is_superuser', 'registered', 'last_password_change', 'last_login']
|
||||||
|
readonly_fields = ['id', 'registered', 'last_login', 'last_password_change']
|
||||||
|
@ -6,14 +6,23 @@ from django.core.validators import MinLengthValidator
|
|||||||
from .managers import CustomUserManager
|
from .managers import CustomUserManager
|
||||||
|
|
||||||
|
|
||||||
class User(AbstractBaseUser, PermissionsMixin):
|
class User(AbstractBaseUser):
|
||||||
login = models.CharField(max_length=16, validators=[MinLengthValidator(4)], verbose_name="Логин", unique=True)
|
login = models.CharField(max_length=16, validators=[MinLengthValidator(4)], verbose_name="Логин", unique=True)
|
||||||
is_staff = models.BooleanField(default=False, verbose_name="Разрешение на вход в админку")
|
password = models.CharField(verbose_name="Пароль", max_length=128)
|
||||||
|
last_login = models.DateTimeField(verbose_name="Последний вход", blank=True, null=True)
|
||||||
is_superuser = models.BooleanField(default=False, verbose_name="Администратор")
|
is_superuser = models.BooleanField(default=False, verbose_name="Администратор")
|
||||||
registered = models.DateTimeField(default=timezone.now, editable=False, verbose_name="Время регистрации")
|
registered = models.DateTimeField(default=timezone.now, editable=False, verbose_name="Время регистрации")
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.is_staff = self.is_superuser
|
||||||
|
|
||||||
last_password_change = models.DateTimeField(default=timezone.now, verbose_name="Последняя смена пароля")
|
last_password_change = models.DateTimeField(default=timezone.now, verbose_name="Последняя смена пароля")
|
||||||
|
|
||||||
|
def set_password(self, raw_password):
|
||||||
|
super().set_password(raw_password)
|
||||||
|
self.last_password_change = timezone.now()
|
||||||
|
|
||||||
USERNAME_FIELD = "login"
|
USERNAME_FIELD = "login"
|
||||||
REQUIRED_FIELDS = []
|
REQUIRED_FIELDS = []
|
||||||
|
|
||||||
@ -21,3 +30,12 @@ class User(AbstractBaseUser, PermissionsMixin):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.login
|
return self.login
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
default_permissions = ()
|
||||||
|
|
||||||
|
def has_perm(self, perm, obj=None):
|
||||||
|
return self.is_superuser
|
||||||
|
|
||||||
|
def has_module_perms(self, package_name):
|
||||||
|
return self.is_superuser
|
||||||
|
Loading…
x
Reference in New Issue
Block a user