module | version |
---|---|
Python | 3.7.7 |
django-environ | 0.4.5 |
Django | 2.2.11 |
djangorestframework | 3.11.0 |
djangorestframework-jwt | 1.11.0 |
django-rest-swagger | 2.2.0 |
django-filter | 2.2.0 |
mysqlclient | 1.4.6 |
pip3 install django-rest-swagger==2.2.0
backend/src/config/settings/settings.py
.
..
...
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
)
}
backend/src/config/settings/developement.py
.
..
...
INSTALLED_APPS += [
'rest_framework_swagger',
]
REST_FRAMEWORK['DEFAULT_SCHEMA_CLASS'] = 'rest_framework.schemas.coreapi.AutoSchema'
backend/src/config/urls.py
from django.contrib import admin
from django.urls import path
from django.conf import settings #Nachtrag
from rest_framework_jwt.views import obtain_jwt_token #Nachtrag
urlpatterns = [
path('admin/', admin.site.urls),
url('api/v1/login/', obtain_jwt_token), #Swagger ohne eine oder mehrere APIs-Es scheint, dass die Benutzeroberfläche nicht geöffnet werden kann. Registrieren Sie daher vorerst die Anmelde-API
]
#Fügen Sie alle unten hinzu
if settings.DEBUG: # DEBUG=Wird nur verwendet, wenn True (während der Entwicklung)
from rest_framework.schemas import get_schema_view
from rest_framework_swagger import renderers
schema_view = get_schema_view(
title='API-Liste',
public=True,
renderer_classes=[renderers.OpenAPIRenderer, renderers.SwaggerUIRenderer])
urlpatterns += [
url(API_ROOT + 'api-auth/', include('rest_framework.urls')),
url('swagger-ui/', schema_view),
]
Greifen Sie auf http://0.0.0.0:8000/swagger-ui/ zu. Wenn der folgende Bildschirm angezeigt wird, klicken Sie oben rechts auf "Anmelden" Drücken Sie, um sich anzumelden
Sie können die "ModelViewSets" verwenden, die jeder liebt.
backend/src/api/users/views.py
from api.users.serializers import UserSerializer
from common.models import User
from rest_framework.viewsets import ModelViewSet #Dies
class UserViewSets(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
Fertig (mit swagger-ui)
Wenn Sie die Anzahl der Anforderungsmethoden später erhöhen möchten, fügen Sie die entsprechenden Mixins hinzu.
backend/src/api/users/views.py
from api.users.serializers import UserSerializer
from common.models import User
from rest_framework.viewsets import GenericViewSet #Dies
from rest_framework.mixins import CreateModelMixin #Dies
class UserViewSets(GenericViewSet, CreateModelMixin):
queryset = User.objects.all()
serializer_class = UserSerializer
Wenn Sie die Anzahl der Anforderungsmethoden später erhöhen möchten, können Sie die entsprechende APIView hinzufügen.
backend/src/api/users/views.py
from api.users.serializers import UserSerializer
from common.models import User
from rest_framework.generics import CreateAPIView #Dies
class UserViewSets(CreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
Bei Generika unterscheidet sich die Methode zur Angabe von urls.py
backend/src/config/urls.py
# =========== viewsets ===========
router = DefaultRouter()
router.register('user', user_views.UserViewSets) #Bei Viewsets kann der Router verwendet werden
urlpatterns = [
path('admin/', admin.site.urls),
url('api/v1/login/', obtain_jwt_token),
url('', include(router.urls)),
]
backend/src/config/urls.py
# =========== generics ===========
urlpatterns = [
path('admin/', admin.site.urls),
url('api/v1/login/', obtain_jwt_token),
url('user/', user_views.UserViewSets.as_view()), #als URL-Muster_views()Fügen Sie mit hinzu
]
Das Ergebnis ist das gleiche wie bei Methode 1
Für solch eine egoistische Person ist es "http_method_names". Ich frage mich, ob es in Situationen verwendet wird, in denen ich es implementiert habe, es aber noch nicht veröffentlichen möchte.
backend/src/api/users/views.py
from api.users.serializers import UserSerializer
from common.models import User
from rest_framework.viewsets import ModelViewSet
class UserViewSets(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
http_method_names = ['get', 'post'] #Die HTTP-Anforderungsmethode ist klein (wichtig) und nicht groß geschrieben
Geben Sie in der Berechtigungsklasse "IsAuthenticated" an.
Ändern Sie zunächst settings.py.
backend/src/config/settings/settings.py
.
..
...
REST_FRAMEWORK = {
#hinzufügen
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
# 'common.permissions.IsSuperuser', #Wenn Sie Ihre eigene Berechtigung erstellt haben, fügen Sie sie hier hinzu.
),
...
..
.
}
Wenn Sie die API im nicht authentifizierten Zustand anfordern, wird "403" zurückgegeben.
backend/src/api/users/views.py
from api.users.serializers import UserSerializer
from common.models import User
from rest_framework.permissions import IsAuthenticated #hinzufügen
from rest_framework.viewsets import ModelViewSet
class UserViewSets(ModelViewSet):
permission_classes = [IsAuthenticated,] #hinzufügen
queryset = User.objects.all()
serializer_class = UserSerializer
In einem solchen Fall ist es ein "@ action" -Dekorator. (Wenn die DRF-Version alt ist, ist dies der Dekorator "@ list_route" oder "@ detail_route". Es scheint, dass er in den Dekorator "@ action" integriert wurde.)
backend/src/api/users/views.py
from api.users.serializers import UserSerializer
from common.models import User
from rest_framework.decorators import action #hinzufügen
from rest_framework.viewsets import ModelViewSet
class UserViewSets(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
@action(detail=False, methods=['get'], url_path='get_user', url_name='get_user')
def get_user(self, request, *kwargs):
"""Gibt Anmeldebenutzerinformationen zurück
"""
return UserSerializer(self.request.user).data
backend/src/api/users/views.py
from api.users.serializers import UserSerializer
from common.models import User
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
class UserViewSets(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
@action(detail=True, methods=['get'], url_path='get_user', url_name='get_user')
def get_user(self, request, pk=None, **kwargs):
"""Gibt Anmeldebenutzerinformationen zurück
"""
#Der Prozess ist gescheitert=Gleich wie falsch. Es tut mir Leid(. _ .)
return Response(
status=status.HTTP_200_OK,
data=UserSerializer(self.request.user).data)
Es wird eine API wie api_root / {pk} / url_name /
sein.
Solche Selbstsucht ist "@ action" Dekorateur "url_path".
backend/src/api/users/views.py
from api.users.serializers import UserSerializer
from common.models import User
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
class UserViewSets(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
@action(detail=False, methods=['get'], url_path='get_user/(?P<user_id>[0-9]+)', url_name='get_user')
def get_user(self, request, user_id=None):
"""Gibt Anmeldebenutzerinformationen zurück
"""
return Response(
status=status.HTTP_200_OK,
data=UserSerializer(self.request.user).data)
Es kann für diejenigen empfohlen werden, die die von detail = True
lol generierte URL nicht mögen
Installieren Sie den Django-Filter mit Pip
pip3 install django-filter==2.2.0
Erstellen Sie filter.py (Dateiname ist beliebig) und definieren Sie Filter
backend/src/api/users/filters.py
from common.models import User
from django_filters import FilterSet
class UserSearchFilter(FilterSet):
class Meta:
model = User
fields = '__all__'
#Felder, die nicht im Abfrageparameter angegeben werden sollen, sollten zum Ausschließen hinzugefügt werden.
# exclude = [
# 'password',
# 'date_created',
# 'date_updated',
# ]
Verwenden Sie den definierten Filter in Ansichten
backend/src/api/users/views.py
from api.users.serializers import UserSerializer
from common.models import User
from django_filters.rest_framework import DjangoFilterBackend #hinzufügen
from api.users.filters import UserSearchFilter #hinzufügen
from rest_framework.viewsets import ModelViewSet
class UserViewSets(ModelViewSet):
filter_backends = [DjangoFilterBackend,] #hinzufügen
filter_class = UserSearchFilter #hinzufügen
queryset = User.objects.all()
serializer_class = UserSerializer
Sie können die Abfrageparameter angeben und abrufen.
backend/src/api/users/serializers.py
from rest_framework.serializers import ModelSerializer
from common.models import User
class UserSerializer(ModelSerializer):
"""Benutzer-Serializer
"""
class Meta:
model = User
fields = '__all__'
Ich möchte keine Gruppen oder Benutzerberechtigungen als Parameter werfen
In diesem Fall geben Sie es in "extra_kwargs" an.
backend/src/api/users/serializers.py
from rest_framework.serializers import ModelSerializer
from common.models import User
class UserSerializer(ModelSerializer):
"""Benutzer-Serializer
"""
class Meta:
model = User
fields = '__all__'
extra_kwargs = {
'is_superuser': {'read_only': True},
'date_created': {'read_only': True},
'date_deleted': {'read_only': True},
'is_staff': {'read_only': True},
'is_active': {'read_only': True},
'groups': {'read_only': True},
'user_permissions': {'read_only': True},
}
Ich drückte es sicher ↓
Da es nur schreibgeschützt ist (read_only), enthält es das zum Zeitpunkt der Erfassung (GET) in "extra_kwargs" angegebene.
Beispielsweise enthält der Wert des Beziehungsziels nur den Primärschlüssel.
Schule
In einem solchen Fall können Sie "SerializerMethodField" verwenden.
Nach dem Definieren der Felder bereiten wir eine Funktion für get_field name
vor.
backend/src/api/users/serializers.py
import json #hinzufügen
from django.core.serializers import serialize #hinzufügen
from rest_framework.serializers import ModelSerializer, SerializerMethodField #hinzufügen
from common.models import User
class UserSerializer(ModelSerializer):
"""Benutzer-Serializer
"""
school = SerializerMethodField() #Feld hinzufügen
# get_Funktion für Feldname hinzugefügt
def get_school(self, obj):
"""Gibt ein JSON-serialisiertes Objekt des Schulobjekts zurück
Args:
obj (User):Benutzerobjekt
Returns:
dict:Ein Objekt, das das Benutzerobjekt im JSON-Format serialisiert
"""
if obj.school is not None:
data = serialize('json', [obj.school,])
objs = json.loads(data)
return json.dumps(objs[0]['fields'])
return None
class Meta:
...
Das Schulobjekt wurde zu "Schule" erweitert, was früher der Hauptschlüssel war (. _.) SerializerMethodField ist praktisch, da es jeden Wert zusammen zurückgeben kann.
Eine solche Selbstsucht ist eine "Tiefen" -Option.
backend/src/api/users/serializers.py
from common.models import User
class UserSerializer(ModelSerializer):
"""Benutzer-Serializer
"""
class Meta:
model = User
fields = '__all__'
extra_kwargs = {
'is_superuser': {'read_only': True},
'date_created': {'read_only': True},
'date_deleted': {'read_only': True},
'is_staff': {'read_only': True},
'is_active': {'read_only': True},
'groups': {'read_only': True},
'user_permissions': {'read_only': True},
}
depth = 1 #Der Standardwert ist 0, daher ist es nicht sinnvoll, 0 anzugeben
Wenn Sie "Tiefe = 2" tun, erhalten Sie noch weitere Beziehungen. (Wenn in diesem Beispiel "Schule" mehr Beziehungen vor sich hat, wird auch dieses Objekt zurückgegeben.)
Standardmäßig wird nur "Token" zurückgegeben.
Wenn Sie hier zusätzlich zum Token Benutzerinformationen einfügen möchten, erstellen Sie Ihren eigenen "jwt_response_payload_handler". Erstellen Sie jwt_utils.py (Dateiname ist beliebig)
backend/src/common/jwt_utils.py
def jwt_response_payload_handler(token, user=None, request=None):
"""Benutzerdefinierte Antwort für die JWT-Authentifizierung
"""
return {
'token': token,
'first_login': user.first_login,
'id': user.id,
}
Ändern Sie settings.py.
backend/src/config/settings/settings.py
.
..
...
# JWT_AUTH hinzufügen
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER': 'common.jwt_utils.jwt_response_payload_handler', # JWT_RESPONSE_PAYLOAD_Selbstgemachtes JWT für HANDLER_response_payload_Geben Sie den Handler an
}
Ich konnte die Antwort erhöhen (. _.)
pip3 install django-environ==0.4.5 Django==2.2.11 djangorestframework==3.11.0 mysqlclient==1.4.6
mkdir -p project_root/backend/src
Die Verzeichnisstruktur ist wie folgt
project_root #Projektstamm
└── backend
└── src
cd project_root/backend/src
django-admin startproject confing .
Die Verzeichnisstruktur ist wie folgt (Konfigurationsdateien können in config gesammelt werden)
project_root #Projektstamm
└── backend
└── src
├── config
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
│
└── manage.py
project_root #Projektstamm
└── backend
└── src
├── config
│ ├── __init__.py
│ ├── settings
│ │ ├── settings.py
│ │ ├── development.py #Zur Entwicklung
│ │ └── production.py #Für die Produktion
│ │
│ ├── urls.py
│ └── wsgi.py
└── manage.py
Ändern Sie settings.py (BASE_DIR wurde als "src" -Verzeichnis korrigiert.)
backend/src/config/settings/settings.py
.
..
...
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # >> /src/confing/settings
│
↓
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # >> /src
...
..
.
Fix verwalten.py
backend/src/manage.py
.
..
...
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'confing.settings')
│
↓
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'confing.settings.development') #Lesen Sie die Entwicklung
...
..
.
Ändern Sie wsgi.py für die Bereitstellung
backend/src/config/wsgi.py
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'confing.settings')
│
↓
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'confing.settings.production')
Behoben: development.py
backend/src/config/settings/development.py
from confing.settings.settings import *
ALLOWED_HOSTS = ['*']
Ändern Sie product.py
backend/src/config/settings/development.py
from confing.settings.settings import *
Ändern Sie settings.py
backend/src/config/settings.settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', #Nachtrag
]
Bereiten Sie docker-compose.yml und Dockerfile vor
project_root #Projektstamm
├── backend
│ ├── src
│ │ ├── config
│ │ │ ├── __init__.py
│ │ │ ├── settings
│ │ │ │ ├── settings.py
│ │ │ │ ├── development.py
│ │ │ │ └── production.py
│ │ │ │
│ │ │ ├── urls.py
│ │ │ └── wsgi.py
│ │ |
│ │ └── manage.py
│ │
│ └── Dockerfile #hinzufügen
│
└── docker-compose.yml #hinzufügen
docker-compose.yml
version: '3'
services:
web:
build:
context: ./
dockerfile: ./backend/Dockerfile
container_name: drf_web
volumes:
- './backend/src:/src'
environment:
- LC_ALL=ja_JP.UTF-8
ports:
- '8000:8000'
depends_on:
- db
command: python3 manage.py runserver 0.0.0.0:8000
restart: always
tty: true
db:
image: mariadb:latest
container_name: drf_db
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
environment:
- MYSQL_ROOT_USER=root
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=db_drf
- MYSQL_USER=user
- MYSQL_PASSWORD=user
volumes:
- db_data:/var/lib/mysql
ports:
- '3306:3306'
volumes:
db_data:
driver: local
FROM ubuntu:18.04
RUN apt update && apt install -y locales python3-pip python3.7 python3-dev libssl-dev libffi-dev libgeos-dev libmysqlclient-dev
RUN apt-get update && apt-get install -y mysql-client python3-gdal
RUN mkdir /src \
&& rm -rf /var/lib/apt/lists/* \
&& echo "ja_JP UTF-8" > /etc/locale.gen \
&& locale-gen
WORKDIR /src
ADD ./backend/src /src/
RUN LC_ALL=ja_JP.UTF-8 pip3 install -r requirements.txt
Starten Sie den Docker-Container
docker-compose up -d
Erfolg, wenn dieser Bildschirm angezeigt wird
cd backend/src/
touch .env.development
backend/src/.env.development
DEBUG=True
DATABASE_URL=mysql://user:user@db:3306/db_drf
backend/src/config/settings/development.py
import environ
ENV_FILE = os.path.join(BASE_DIR, '.env.development')
ENV = environ.Env()
ENV.read_env(ENV_FILE)
DEBUG = ENV.get_value('DEBUG', cast=bool)
DATABASES['default'] = ENV.db() #DB-Verbindungsinformationen lesen (.env.Entwicklung DATENBANK_Liest die URL)
...
..
.
django-admin startapp base
INSTALLED_APPS = [
.
..
...
'base',
]
backend/src/base/models.py
from django.db import models
from django.utils import timezone
# Create your models here.
from django.db import models
from django.utils import timezone
# Create your models here.
class BaseModel(models.Model):
"""Basismodell
"""
date_created = models.DateTimeField('Erstellungsdatum und -zeit', default=timezone.now)
date_updated = models.DateTimeField('Zuletzt geändert', auto_now_add=True)
date_deleted = models.DateTimeField('Datum und Uhrzeit löschen', null=True)
class Meta:
abstract = True #← Erforderlich
django-admin startapp common
INSTALLED_APPS = [
.
..
...
'base',
'common',
]
Erstellen Sie manager.py
cd backend/src/base
touch manager.py
backend/src/base/manager.py
from django.contrib.auth.models import UserManager
class UserManager(UserManager):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def _create_user(self, username, email, password, **extra_fields):
"""
Create and save a user with the given username, email, and password.
"""
if not username:
raise ValueError('The given username must be set')
email = self.normalize_email(email)
username = self.model.normalize_username(username)
user = self.model(username=username, email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, username, email=None, password=None, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', False)
return self._create_user(username, email, password, **extra_fields)
def create_superuser(self, username, email, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(username, email, password, **extra_fields)
Der Beamte empfiehlt die Implementierung eines benutzerdefinierten Benutzermodells. Befolgen Sie diese (. _.)
backend/src/common/models.py
from django.db import models
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin
from base.manager import UserManager
from base.models import BaseModel
class User(AbstractBaseUser, PermissionsMixin, BaseModel):
email = models.EmailField('Mail Adresse', blank=True, null=True)
username = models.CharField('Nutzername', max_length=150, unique=True)
display_name = models.CharField('Anzeigename des Bildschirms', max_length=30, blank=True, null=True)
last_name = models.CharField('Nachname', max_length=150, blank=True, null=True)
first_name = models.CharField('Name', max_length=30, blank=True, null=True)
is_staff = models.BooleanField('Mitarbeiterflagge', default=False)
is_active = models.BooleanField('Gültiges Flag', default=True)
first_login = models.BooleanField('Erster Login', default=True)
objects = UserManager()
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
class Meta:
verbose_name = verbose_name_plural = 'users'
db_table = 'user'
backend/src/config/settings/settings.py
.
..
...
AUTH_USER_MODEL = 'common.User' #Geben Sie ein benutzerdefiniertes Benutzermodell für das zur Authentifizierung verwendete Benutzermodell an.
Wenn Sie vergessen, "AUTH_USER_MODEL" anzugeben, tritt der folgende Fehler auf.
python3 manage.py makemigrations
SystemCheckError: System check identified some issues:
ERRORS:
auth.User.groups: (fields.E304) Reverse accessor for 'User.groups' clashes with reverse accessor for 'User.groups'.
HINT: Add or change a related_name argument to the definition for 'User.groups' or 'User.groups'.
auth.User.user_permissions: (fields.E304) Reverse accessor for 'User.user_permissions' clashes with reverse accessor for 'User.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'User.user_permissions' or 'User.user_permissions'.
common.User.groups: (fields.E304) Reverse accessor for 'User.groups' clashes with reverse accessor for 'User.groups'.
HINT: Add or change a related_name argument to the definition for 'User.groups' or 'User.groups'.
common.User.user_permissions: (fields.E304) Reverse accessor for 'User.user_permissions' clashes with reverse accessor for 'User.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'User.user_permissions' or 'User.user_permissions'.
python3 manage.py makemigrations
python3 manage.py migrate
Recommended Posts