Как заставить Django Rest Framework работать с Django Tenants и React?

Вот моя установка:

settings.py

SHARED_APPS = (
    'django_tenants',
    'main',
    other apps...
)

TENANT_APPS = (
    'rest_framework',
    'company',
)

MIDDLEWARE = [
    'django_tenants.middleware.main.TenantMainMiddleware',
    other middleware...
]

DATABASE_ROUTERS = (
    'django_tenants.routers.TenantSyncRouter',
)

urls.py

from django.urls import include, path
from rest_framework import routers

# other imports

from main.api.v1 import projects

router = routers.DefaultRouter()
router.register(r'api/v1/project', projects.ProjectViewSet)

urlpatterns = [
    -- other paths --
    path('', include(router.urls)),
]

api/v1/project.py

# other imports
from company.models import Project

from rest_framework import serializers
from rest_framework import viewsets
from rest_framework import permissions

class ProjectSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Project
        fields = ['url', 'name', 'keycode']

class ProjectViewSet(viewsets.ModelViewSet):
    queryset = Project.objects.all().order_by('id')
    serializer_class = ProjectSerializer
    permission_classes = [permissions.AllowAny]

main.models.py

from django.contrib.auth.models import User as AuthUser
from django_tenants.models import TenantMixin, DomainMixin

# note, definition of custom "User" model which has an AuthUser 1 to 1 relationship

class Company(TenantMixin):
    name = models.CharField(max_length=100)
    subdomain = models.CharField(max_length=32)
    employees = models.ManyToManyField(User, related_name='companies')
    migration_id = models.IntegerField(null=True)

class Domain(DomainMixin):
    pass

company.models.py

from django.db import models

class Project(models.Model):
    name = models.CharField(max_length=100)
    keycode = models.CharField(max_length=8)

И последняя деталь - я использую не Django frontend, а созданный в React. Запрос, который идет к бэкенду, является обычным запросом, однако он идет с поддомена и включает JWT-токен (но я не знаю, имеет ли это значение), вот сокращенная версия заголовков запроса:

Request URL: http://localhost:8000/api/v1/project/
Request Method: GET
Authorization: Bearer <token here>
Origin: http://cbd.localhost:3000

Ошибка, которую я вижу на бэкенде, выглядит следующим образом:

взаимосвязь "компания_проект" не существует

Мое предположение, что это происходит из-за того, что когда этот запрос создается в ProjectViewSet:

queryset = Project.objects.all().order_by('id')

Запрос не выполняется в контексте арендатора. Но мой вопрос в том, как именно это можно сделать. Я вижу в Django Tenants, что есть конструкция, которая выглядит следующим образом:

with tenant_context(tenant):
    # All commands here are ran under the schema from the `tenant` object

Но я не представляю, как мне получить параметр 'tenant' в определении класса, где он, по-видимому, необходим.

Есть идеи?

Что ж, я придумал один способ сделать это, но он уродлив.

в моем файле settings.py я добавил новую константу:

BASE_TENANT = "cbd_co"

Я установил это для моего первого арендатора (в моей системе будет схема арендатора по умолчанию, которая на самом деле не принадлежит клиенту, а является своего рода шаблоном)

Затем, в объявлении набора представлений моей модели я сделал следующее:

class ProjectViewSet(viewsets.ModelViewSet):
    queryset = Project.objects.raw('SELECT * FROM "' + settings.BASE_TENANT + '"."company_project";')
    serializer_class = ProjectSerializer
    permission_classes = [permissions.AllowAny]

    def get_queryset(self):
        schema = self.request.META['HTTP_ORIGIN'].split("//")[1].split('.')[0] + '_co'
        qs = Project.objects.raw('SELECT * FROM "' + schema + '"."company_project" ORDER BY "company_project"."id" ASC;')
        return qs

Я все еще надеюсь, что кто-то предложит лучшее решение...

Примечания к этому решению. В своем нынешнем виде оно совершенно небезопасно. На самом деле мне придется получить заголовок авторизации из запроса, проверить токен JWT, получить пользователя, получить все допустимые компании пользователей (что также определяет допустимые поддомены и, следовательно, допустимые схемы. и только после этого я верну набор запросов.

Вернуться на верх