为OpenWISP提供基本的多租户功能(使用Django Web框架)
项目描述
为OpenWISP实现用户管理和多租户功能(使用python & django构建)。
在生产环境中部署
自动安装程序可在 ansible-openwisp2 找到。
从PyPI安装稳定版本
从 PyPI 安装
pip install openwisp-users
安装开发版本
安装 tar 包
pip install https://github.com/openwisp/openwisp-users/tarball/master
或者您可以通过 pip 使用 git 进行安装
pip install -e git+git://github.com/openwisp/openwisp-users#egg=openwisp_users
设置(集成到现有Django项目中)
INSTALLED_APPS 在 settings.py 中应如下所示
INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'openwisp_utils.admin_theme',
# overrides some templates in django-allauth
'openwisp_users.accounts',
'django_extensions',
'allauth',
'allauth.account',
'allauth.socialaccount',
'rest_framework',
'rest_framework.authtoken',
# must come before the django admin
# to override the admin login page
'openwisp_users',
'django.contrib.admin',
'django.contrib.sites',
'drf_yasg',
]
还要在 settings.py 中添加 AUTH_USER_MODEL、SITE_ID 和 AUTHENTICATION_BACKENDS
AUTH_USER_MODEL = 'openwisp_users.User'
SITE_ID = 1
AUTHENTICATION_BACKENDS = [
'openwisp_users.backends.UsersAuthenticationBackend',
]
urls.py:
from django.conf.urls import include, url
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^accounts/', include('allauth.urls')),
url(r'^api/v1/', include('openwisp_users.api.urls')),
]
urlpatterns += staticfiles_urlpatterns()
在 settings.py 中添加以下内容以配置电子邮件验证成功视图
ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = 'email_confirmation_success'
ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = 'email_confirmation_success'
有关在您的项目中正确配置 allauth 的额外步骤,请参阅他们的文档:allauth 文档安装部分.
开发版安装
安装 sqlite
sudo apt-get install sqlite3 libsqlite3-dev openssl libssl-dev
安装您的分支库
git clone git://github.com/<your_fork>/openwisp-users
cd openwisp-users/
pip install -e .[rest]
安装测试需求
pip install -r requirements-test.txt
启动 Redis
docker-compose up -d
创建数据库
cd tests/
./manage.py migrate
./manage.py createsuperuser
启动开发服务器
./manage.py runserver
您可以在 http://127.0.0.1:8000/admin/ 访问管理员界面。
使用以下命令运行测试
# --parallel and --keepdb are optional but help to speed up the operation
./runtests.py --parallel --keepdb
设置
OPENWISP_ORGANIZATION_USER_ADMIN
type: |
boolean |
default: |
False |
指示管理 OrganizationUser 项目的管理员部分是否启用。
默认情况下是禁用的,因为这些项目可以通过用户管理部分中的内联项目进行管理。
OPENWISP_ORGANIZATION_OWNER_ADMIN
type: |
boolean |
default: |
True |
指示管理 OrganizationOwner 项目的管理员部分是否启用。
有关组织所有者的更多信息,请参阅 组织所有者。
OPENWISP_USERS_AUTH_API
type: |
boolean |
default: |
True |
指示是否启用 REST API。
OPENWISP_USERS_AUTH_THROTTLE_RATE
type: |
str |
default: |
100/day |
指示 获取认证 API 端点的速率限制。
请注意,当前速率限制器非常基础,并将计算用于速率限制的有效请求。有关更多信息,请参阅Django-rest-framework 速率限制指南。
OPENWISP_USERS_AUTH_BACKEND_AUTO_PREFIXES
type: |
元组 |
default: |
元组() |
一个包含国际前缀的元组或列表,当解析电话号码时,openwisp-users的认证后端会自动测试这些前缀。
每个前缀将自动添加到用户名字符串中,并使用phonenumbers库进行解析,以确定结果是否为有效号码。
这允许用户仅使用国内电话号码登录,而无需指定国际前缀。
REST API
要启用API,必须将设置OPENWISP_USERS_AUTH_API设置为True。
实时文档
位于/api/v1/docs/的通用实时API文档(遵循OpenAPI规范)。
可浏览的Web界面
此外,在浏览器中直接打开以下任何端点将显示Django-REST-Framework的可浏览API界面,这使得查找每个端点的详细信息更加容易。
获取认证令牌
/api/v1/users/token/
此端点仅接受POST方法,并用于检索用于对其他端点进行API请求所需的Bearer令牌。
端点示例用法
http POST localhost:8000/api/v1/users/token/ username=openwisp password=1234
HTTP/1.1 200 OK
Allow: POST, OPTIONS
Content-Length: 52
Content-Type: application/json
Date: Wed, 13 May 2020 10:59:34 GMT
Server: WSGIServer/0.2 CPython/3.6.9
Vary: Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"token": "7a2e1d3d008253c123c61d56741003db5a194256"
}
使用用户令牌进行认证
认证类openwisp_users.api.authentication.BearerAuthentication在不同的OpenWISP模块中用于认证。
要使用它,首先获取用户令牌,如上获取认证令牌中所述,然后将令牌发送到Authorization头
# get token
TOKEN=$(http POST :8000/api/v1/users/token/ username=openwisp password=1234 | jq -r .token)
# send bearer token
http GET localhost:8000/api/v1/firmware/build/ "Authorization: Bearer $TOKEN"
端点列表
由于详细说明包含在每个端点的实时文档和可浏览网页中,因此这里我们只提供可用端点的列表;有关更多信息,请打开浏览器中的端点URL。
更改用户密码
PUT /api/v1/users/user/{id}/password/
列出组
GET /api/v1/users/group/
创建新组
POST /api/v1/users/group/
获取组详情
GET /api/v1/users/group/{id}/
更改组详情
PUT /api/v1/users/group/{id}/
修补组详情
PATCH /api/v1/users/group/{id}/
删除组
DELETE /api/v1/users/group/{id}/
列出电子邮件地址
GET /api/v1/users/user/{id}/email/
添加电子邮件地址
POST/api/v1/users/user/{id}/email/
获取电子邮件地址
GET /api/v1/users/user/{id}/email/{id}/
更改电子邮件地址
PUT /api/v1/users/user/{id}/email/{id}/
修补电子邮件地址
PATCH /api/v1/users/user/{id}/email/{id}/
设置/取消设置电子邮件地址为主地址
PATCH /api/v1/users/user/{id}/email/{id}/
标记/取消标记电子邮件地址为已验证
PATCH /api/v1/users/user/{id}/email/{id}/
移除电子邮件地址
DELETE /api/v1/users/user/{id}/email/{id}/
列出组织
GET /api/v1/users/organization/
创建新组织
POST /api/v1/users/organization/
获取组织详情
GET /api/v1/users/organization/{id}/
更改组织详情
PUT /api/v1/users/organization/{id}/
修补组织详情
PATCH /api/v1/users/organization/{id}/
删除组织
DELETE /api/v1/users/organization/{id}/
列出用户
GET /api/v1/users/user/
创建用户
POST /api/v1/users/user/
获取用户详情
GET /api/v1/users/user/{id}/
更改用户详情
PUT /api/v1/users/user/{id}/
修补用户详情
PATCH /api/v1/users/user/{id}/
删除用户
DELETE /api/v1/users/user/{id}/
组织权限
以下是默认权限的摘要
属于管理员组并且是组织管理员(OrganizationUser.is_admin=True)的所有用户都有权编辑他们管理的组织详情。
只有超级用户才有权添加和删除组织。
只有超级用户和组织所有者才有权更改OrganizationOwner内联或删除关系。
组织所有者
组织所有者是特定组织的指定所有者,并且此所有者不能被其他管理员删除或编辑,只有超级用户才有权这样做。
默认情况下,组织的第一个管理员被指定为该组织的所有者。
如果与组织所有者相关的OrganizationUser实例被删除或标记为is_admin=False,则管理界面将返回错误,通知用户此操作不允许,应在尝试该操作之前更改所有者。
组织成员助手
User模型提供了一些方法,可以以高效的方式检查用户是否是组织的成员、管理员或所有者。
这些方法之所以需要,是因为用户可能在一个组织中是管理员,而在另一个组织中是普通终端用户,因此我们需要轻松地区分不同的用例,同时避免生成过多的数据库查询。
import swapper
User = swapper.load_model('openwisp_users', 'User')
Organization = swapper.load_model('openwisp_users', 'Organization')
user = User.objects.first()
org = Organization.objects.first()
user.is_member(org)
user.is_manager(org)
user.is_owner(org)
# also valid (avoids query to retrieve Organization instance)
device = Device.objects.first()
user.is_member(device.organization_id)
user.is_manager(device.organization_id)
user.is_owner(device.organization_id)
is_member(org)
如果用户是传入的 Organization 实例的成员,则返回 True。或者,可以使用 UUID 或 str 代替组织实例,这将被视为组织的键;当构建组织实例需要额外查询时,建议使用第二种选项。
当需要授予访问权给需要消费他们所属组织的服务的最终用户(例如:通过公共Wi-Fi服务的认证)时,应使用此检查。
is_manager(org)
如果用户是 Organization 实例的成员,并且 OrganizationUser.is_admin 字段设置为 True,则返回 True。或者,可以使用 UUID 或 str 代替组织实例,这将被视为组织的键;当构建组织实例需要额外查询时,建议使用第二种选项。
当需要授予访问权给需要执行管理任务的组织的用户(例如:下载他们组织的固件映像)的管理员时,应使用此检查。
is_owner(org)
如果用户是 Organization 实例的成员,并且是该组织的所有者(检查用户是否存在 OrganizationOwner 实例),则返回 True。或者,可以使用 UUID 或 str 代替组织实例,这将被视为组织的键;当构建组织实例需要额外查询时,建议使用第二种选项。
每个组织只能有一个所有者。
此检查用于防止管理员在没有所有者同意的情况下控制组织并排除原始所有者。
organizations_dict
上述方法在底层使用 organizations_dict 属性方法,该方法构建一个字典,其中每个键包含用户所属组织的键,每个键包含另一个字典,可以轻松确定用户是否是管理员(is_admin)和所有者(is_owner)。
此数据结构会自动缓存,多次请求中多次访问它不会生成多个数据库查询。
当添加、更改或删除 OrganizationUser 或 OrganizationOwner 实例时,缓存失效也会自动发生。
使用示例
>>> user.organizations_dict
... {'20135c30-d486-4d68-993f-322b8acb51c4': {'is_admin': True, 'is_owner': False}}
>>> user.organizations_dict.keys()
... dict_keys(['20135c30-d486-4d68-993f-322b8acb51c4'])
organizations_managed
此属性返回一个包含用户可以管理的组织主键的列表。
使用示例
>>> user.organizations_managed
... ['20135c30-d486-4d68-993f-322b8acb51c4']
organizations_owned
此属性返回一个包含用户拥有的组织主键的列表。
使用示例
>>> user.organizations_owned
... ['20135c30-d486-4d68-993f-322b8acb51c4']
认证后端
openwisp_users.backends.UsersAuthenticationBackend 中的认证后端允许用户使用他们的 email 或 phone_number 而不是 username 进行认证。虽然仍然允许使用 username,但 email 优先。
如果传入的 username 字符串解析为有效的电话号码,则 phone_number 优先。
电话号码使用 phonenumbers 库进行解析,这意味着即使用户添加了空格、点或破折号等字符,号码仍然会被识别。
在解析电话号码时,OPENWISP_USERS_AUTH_BACKEND_AUTO_PREFIXES 设置允许指定一个可以自动添加到用户名字符串前的国际前缀列表,以便用户无需输入国际前缀即可登录。
认证后端还可以如下使用:
from openwisp_users.backends import UsersAuthenticationBackend
backend = UsersAuthenticationBackend()
backend.authenticate(request, identifier, password)
Django REST 框架认证类
1. openwisp_users.api.authentication.BearerAuthentication
基于 rest_framework.authentication.TokenAuthentication,BearerAuthentication 是 OpenWISP 所有模块中实现的 REST API 的主要认证类。
请参阅使用用户令牌进行认证部分以获取使用信息。
2. openwisp_users.api.authentication.SesameAuthentication
它使用由 django-sesame 生成的令牌对 REST 视图进行认证。它主要用于提供无密码认证,例如在魔法登录链接中。
此认证类的正常工作需要配置 django-sesame。请参阅 django-sesame 文档 以获取更多信息。
Django REST 框架权限类
API 中可以使用自定义的 Django REST Framework 权限类 IsOrganizationMember、IsOrganizationManager 和 IsOrganizationOwner,以确保请求用户与请求对象属于同一组织,并且分别是组织成员、经理或所有者。使用示例
from openwisp_users.api.permissions import IsOrganizationManager
from rest_framework import generics
class MyApiView(generics.APIView):
permission_classes = (IsOrganizationManager,)
organization_field
type: |
字符串 |
default: |
组织 |
organization_field 可以用来定义查找当前对象组织的地方。在大多数情况下,这不需要更改,但在 organization 仅在父对象上定义时,则需要更改。
例如,在 openwisp-firmware-upgrader 中,organization 定义在 Category 上,而 Build 与 category 有关系,因此 Build 实例的组织是从 Category 的组织推断出来的。
因此,为了正确实现权限类,我们需要这样做
from openwisp_users.api.permissions import IsOrganizationManager
from rest_framework import generics
class MyApiView(generics.APIView):
permission_classes = (IsOrganizationManager,)
organization_field = 'category__organization'
这将转换为访问 obj.category.organization。确保你的视图查询集在这些情况下使用 select_related,以避免生成太多查询。
DjangoModelPermissions
默认的 DjangoModelPermissions 类不会检查任何对象的 GET 请求的 view 权限。扩展的 DjangoModelPermissions 类克服了这个问题。为了允许对任何对象的 GET 请求,它检查是否存在 view 或 change 权限。
使用示例
from openwisp_users.api.permissions import DjangoModelPermissions
from rest_framework.generics import ListCreateAPIView
class TemplateListCreateView(ListCreateAPIView):
serializer_class = TemplateSerializer
permission_classes = (DjangoModelPermissions,)
queryset = Template.objects.all()
注意: DjangoModelPermissions 允许组织经理或所有者以只读模式查看共享对象。
标准用户将无法查看或列出共享对象。
Django REST 框架混入
按组织过滤项目
自定义的 Django REST Framework 混入 FilterByOrganizationMembership、FilterByOrganizationManaged 和 FilterByOrganizationOwned 可用于 API 视图中,以确保当前用户在访问 API 视图时只能看到与他们的组织相关的数据。
它们通过过滤查询集来实现,只显示用户是成员、经理或所有者的组织的相关项目。
这些混入默认将Django REST Framework的IsAuthenticated权限类发送出去,因为组织过滤只对已认证用户有效。在重写视图中的permission_classes时,请始终记住包括此类。
使用示例
from openwisp_users.api.mixins import FilterByOrganizationManaged
from rest_framework import generics
class UsersListView(FilterByOrganizationManaged, generics.ListAPIView):
"""
UsersListView will show only users from organizations managed
by current user in the list.
"""
pass
class ExampleListView(FilterByOrganizationManaged, generics.ListAPIView):
"""
Example showing how to extend ``permission_classes``.
"""
permission_classes = FilterByOrganizationManaged.permission_classes + [
# additional permission classes here
]
检查父对象
有时,API视图需要检查父对象的organization字段的存在。
在这种情况下,可以使用FilterByParentMembership、FilterByParentManaged和FilterByParentOwned。
例如,给定一个假设的URL /api/v1/device/{device_id}/config/,视图必须检查{device_id}是否存在以及用户是否有权访问它,下面是如何做到这一点:
import swapper
from rest_framework import generics
from openwisp_users.api.mixins import FilterByParentManaged
Device = swapper.load_model('config', 'Device')
Config = swapper.load_model('config', 'Config')
# URL is:
# /api/v1/device/{device_id}/config/
class ConfigListView(FilterByParentManaged, generics.DetailAPIView):
model = Config
def get_parent_queryset(self):
qs = Device.objects.filter(pk=self.kwargs['device_id'])
return qs
用于可浏览 Web UI 的多租户序列化器
Django REST Framework提供了一个可浏览的API,可以从浏览器中创建HTTP请求。
该接口中的关系字段显示所有关系,而不按用户可访问的组织进行过滤,这会破坏多租户。
可以使用FilterSerializerByOrgMembership、FilterSerializerByOrgManaged和FilterSerializerByOrgOwned来解决此问题。
这些序列化器不允许非超级用户创建共享对象。
使用示例
from openwisp_users.api.mixins import FilterSerializerByOrgOwned
from rest_framework.serializers import ModelSerializer
from .models import Device
class DeviceSerializer(FilterSerializerByOrgOwned, ModelSerializer):
class Meta:
model = Device
fields = '__all__'
可以使用布尔属性include_shared将共享对象包含在多租户序列化器的接受值中。
共享对象的organization字段设置为None,可由任何组织使用。一个常见的用例是OpenWISP Controller中的共享模板。
使用示例
from openwisp_users.api.mixins import FilterSerializerByOrgOwned
from rest_framework.serializers import ModelSerializer
from .models import Book
class BookSerializer(FilterSerializerByOrgOwned, ModelSerializer):
include_shared = True
class Meta:
model = Book
fields = '__all__'
要基于父对象的organization过滤项目,可以在继承任何混入类的视图函数中定义organization_field属性。
使用示例:organization_field。
管理多租户混入
MultitenantAdminMixin:将此混入添加到ModelAdmin类中,将使其具有多租户功能(用户只能看到他们管理或拥有的组织的项目)。
该类有两个重要的属性可以设置:
multitenant_shared_relations:如果模型具有与其他模型的关系(例如:ForeignKey、OneToOne),并且这些模型也是多租户的(即,它们有organization字段),则希望管理员只显示用户可以管理的关联,为此,请在此处将那些模型属性作为字符串列表列出。有关实际示例,请参阅OpenWISP Controller的使用方法。
multitenant_parent:如果管理员模型没有organization字段,而是依赖于具有该字段的父模型,则可以在此处指定指向父模型的字段。有关实际示例,请参阅OpenWISP Firmware Upgrader的使用方法。
MultitenantOrgFilter:管理员过滤器,仅显示当前用户可以在其可用选项中管理的组织。
MultitenantRelatedOrgFilter:与MultitenantOrgFilter类似,但仅显示与当前用户可以管理的组织之一有关联的对象,应在模型没有自己的organization字段但依赖于具有该字段的父模型时使用。
扩展 openwisp-users
OpenWISP项目的核心价值之一是软件可重用性,因此openwisp-users提供了一组基类,可以导入、扩展和重用以创建衍生应用程序。
如果您想为用户模型创建更多字段,例如在注册时要求用户输入社会保障号码,这将非常有益。
为了实现您自己的 openwisp-users 版本,您需要执行本节中描述的步骤。
如有疑问,测试项目中的代码和 示例应用程序 将作为可信来源:只需复制并修改这些代码,以使 openwisp-users 的基本派生版本正常工作。
前提:如果您计划使用此模块的定制版本,我们建议从开始就使用它,因为将您的数据从默认模块迁移到扩展版本可能需要花费大量时间。
1. 初始化你的自定义模块
您需要做的第一件事是创建一个新的 django 应用程序,该应用程序将包含您自己的 openwisp-users 版本。
一个 django 应用程序不过是一个 python 包(一个 python 脚本目录),在以下示例中我们将此 django 应用程序称为 myusers,但您可以将其命名为您想要的任何名称。
django-admin startapp myusers
请注意,上述命令必须从您的 PYTHON_PATH 中可用的目录中调用,以便您可以将结果导入到项目中。
现在,您需要将 myusers 添加到您的 settings.py 中的 INSTALLED_APPS,并确保已经移除了 openwisp_users。
INSTALLED_APPS = [
# ... other apps ...
# 'openwisp_users' <-- comment out or delete this line
'myusers'
]
有关如何使用 django 项目和 django 应用程序的信息,请参阅 django 文档。
2. 安装 openwisp-users
安装(并将 openwisp-users 添加到您项目的需求中)
pip install openwisp-users
3. 添加 EXTENDED_APPS
将以下内容添加到您的 settings.py
EXTENDED_APPS = ('openwisp_users',)
4. 添加 openwisp_utils.staticfiles.DependencyFinder
将 openwisp_utils.staticfiles.DependencyFinder 添加到您的 settings.py 中的 STATICFILES_FINDERS
STATICFILES_FINDERS = [
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'openwisp_utils.staticfiles.DependencyFinder',
]
5. 添加 openwisp_utils.loaders.DependencyLoader
在您的 settings.py 中将 openwisp_utils.loaders.DependencyLoader 添加到 TEMPLATES 中,在 django.template.loaders.app_directories.Loader 之前
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'OPTIONS': {
'loaders': [
'django.template.loaders.filesystem.Loader',
'openwisp_utils.loaders.DependencyLoader',
'django.template.loaders.app_directories.Loader',
],
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
}
]
6. 继承 AppConfig 类
请参阅测试项目中的示例应用程序中的以下文件
您必须复制并修改这些代码以适应您的项目。
有关 AppConfig 概念的更多信息,请参阅 django 文档中的“Applications”部分。
7. 创建你的自定义模型
为了说明示例,我们在测试项目的示例应用程序的 models 中添加了一个简单的 social_security_number 字段。
您可以用类似的方式在您的 models.py 文件中添加字段。
有关如何使用、扩展或开发模型的问题,请参阅 django 文档中的“Models”部分。
8. 添加交换器配置
创建模型后,将以下内容添加到您的 settings.py
# Setting models for swapper module
AUTH_USER_MODEL = 'myusers.User'
OPENWISP_USERS_GROUP_MODEL = 'myusers.Group'
OPENWISP_USERS_ORGANIZATION_MODEL = 'myusers.Organization'
OPENWISP_USERS_ORGANIZATIONUSER_MODEL = 'myusers.OrganizationUser'
OPENWISP_USERS_ORGANIZATIONOWNER_MODEL = 'myusers.OrganizationOwner'
# The following model is not used in OpenWISP yet
# but users are free to implement it in their projects if needed
# for more information refer to the django-organizations docs:
# https://django-organizations.readthedocs.io/
OPENWISP_USERS_ORGANIZATIONINVITATION_MODEL = 'myusers.OrganizationInvitation'
将 myusers 替换为步骤 1 中选择的名称。
9. 创建数据库迁移
创建数据库迁移
./manage.py makemigrations
现在,手动在由 makemigrations 命令创建的迁移目录中创建一个名为 0004_default_groups.py 的文件,并复制 sample_users/migrations/0004_default_groups.py 中的内容。
然后,运行迁移
./manage.py migrate
注意:0004_default_groups 是必需的,因为其他 OpenWISP 模块依赖于它。如果不按文档中的说明创建它,其他 OpenWISP 模块的迁移将失败。
10. 创建管理员
请参考示例应用的admin.py文件。
要向admin引入更改,您可以通过以下两种主要方式来实现,具体描述如下。
有关Django admin如何工作或如何进行自定义的更多信息,请参阅Django文档中的“Django admin站点”部分。
1. Monkey 修补
如果您需要添加的更改相对较小,您可以求助于猴子补丁。
例如
from openwisp_users.admin import (
UserAdmin,
GroupAdmin,
OrganizationAdmin,
OrganizationOwnerAdmin,
BaseOrganizationUserAdmin,
)
# OrganizationAdmin.field += ['example_field'] <-- Monkey patching changes example
为了方便您在用户表单中添加字段,我们提供了以下函数
usermodel_add_form
当猴子补丁UserAdmin类以在“添加用户”表单中添加字段时,您可以使用此函数。例如,社会保险号被添加到添加表单中
usermodel_change_form
当猴子补丁UserAdmin类以在“更改用户”表单中添加字段以更改/修改用户表单的配置部分时,您可以使用此函数。例如,社会保险号被添加到更改表单中
usermodel_list_and_search
当猴子补丁UserAdmin类时,您可以使用此函数使字段可搜索并将它添加到用户显示列表视图。例如,社会保险号被添加到更改列表视图中
2. 继承 admin 类
如果您需要引入重大更改,并且不想求助于猴子补丁,请按照以下步骤操作
from django.contrib import admin
from openwisp_users.admin import (
UserAdmin as BaseUserAdmin,
GroupAdmin as BaseGroupAdmin,
OrganizationAdmin as BaseOrganizationAdmin,
OrganizationOwnerAdmin as BaseOrganizationOwnerAdmin,
OrganizationUserAdmin as BaseOrganizationUserAdmin,
)
from swapper import load_model
from django.contrib.auth import get_user_model
Group = load_model('openwisp_users', 'Group')
Organization = load_model('openwisp_users', 'Organization')
OrganizationOwner = load_model('openwisp_users', 'OrganizationOwner')
OrganizationUser = load_model('openwisp_users', 'OrganizationUser')
User = get_user_model()
admin.site.unregister(Group)
admin.site.unregister(Organization)
admin.site.unregister(OrganizationOwner)
admin.site.unregister(OrganizationUser)
admin.site.unregister(User)
@admin.register(Group)
class GroupAdmin(BaseGroupAdmin):
pass
@admin.register(Organization)
class OrganizationAdmin(BaseOrganizationAdmin):
pass
@admin.register(OrganizationOwner)
class OrganizationOwnerAdmin(BaseOrganizationOwnerAdmin):
pass
@admin.register(OrganizationUser)
class OrganizationUserAdmin(BaseOrganizationUserAdmin):
pass
@admin.register(User)
class UserAdmin(BaseUserAdmin):
pass
11. 创建根 URL 配置
请参考示例项目中的urls.py文件。
有关Django中URL配置的更多信息,请参阅Django文档中的“URL分发器”部分。
12. 导入自动化测试
在基于此模块开发自定义应用程序时,最好导入并运行基本测试,以确保您引入的更改不会破坏openwisp-users的现有功能。
如果您需要添加破坏性更改,您可以覆盖基类中定义的测试以测试您自己的行为。
请参阅示例应用的测试以了解如何进行此操作。
您可以使用以下命令运行测试
# the --parallel flag is optional ./manage.py test --parallel myusers
将 myusers 替换为步骤 1 中选择的名称。
其他可继承和扩展的基类
以下步骤不是必需的,仅适用于更高级的自定义。
1. 扩展 API 视图
API视图类也可以扩展到其他Django应用程序。请注意,将openwisp-users扩展到您的应用程序不需要扩展,并且仅在您计划更改API视图时需要此更改。
创建一个视图文件,如API视图.py中所做的那样。
请记住在11点中根URL配置中使用这些视图。
有关Django视图的更多信息,请参阅Django文档中的视图部分。
贡献
请参阅OpenWISP贡献指南。
支持
请参阅OpenWISP支持渠道。
变更日志
请参阅更改。
许可证
请参阅许可。
项目详情
下载文件
下载适合您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。