Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added support for primary keys where the field id is missing #707 #708

Merged
merged 2 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Version history

We follow [Semantic Versions](https://semver.org/) starting at the `0.4.0` release.
## 2.0.1 (2024-11-19)

### Features
* Added support for UserModels where the primary key field isnt `id`


## 2.0.0 (2024-09-09) Breaking changes

Expand Down
64 changes: 64 additions & 0 deletions django_test_app/users/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import uuid

from django.db import migrations, models

from tenant_users.permissions.models import PermissionsMixinFacade
Expand Down Expand Up @@ -80,4 +82,66 @@ class Migration(migrations.Migration):
PermissionsMixinFacade,
),
),
migrations.CreateModel(
name="GuidUser",
fields=[
(
"guid",
models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)
),
(
"password",
models.CharField(
max_length=128,
verbose_name="password",
),
),
(
"last_login",
models.DateTimeField(
blank=True,
null=True,
verbose_name="last login",
),
),
(
"email",
models.EmailField(
db_index=True,
max_length=254,
unique=True,
verbose_name="Email Address",
),
),
(
"is_active",
models.BooleanField(
default=True,
verbose_name="active",
),
),
(
"is_verified",
models.BooleanField(default=False, verbose_name="verified"),
),
("name", models.CharField(blank=True, max_length=64)),
(
"tenants",
models.ManyToManyField(
blank=True,
help_text="The tenants this user belongs to.",
related_name="guid_users_set",
to="companies.Company",
verbose_name="tenants",
),
),
],
options={
"abstract": False,
},
bases=(
models.Model,
PermissionsMixinFacade,
),
),
]
16 changes: 14 additions & 2 deletions django_test_app/users/models.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import uuid
from django.db import models

from tenant_users.tenants.models import UserProfile

from django.conf import settings
from django.utils.translation import gettext_lazy as _
_NameFieldLength = 64


class TenantUser(UserProfile):
"""Simple user model definition for testing."""

name = models.CharField(max_length=_NameFieldLength, blank=True)


class GuidUser(UserProfile):
guid = models.UUIDField(default=uuid.uuid4, primary_key=True)
tenants = models.ManyToManyField(
settings.TENANT_MODEL,
verbose_name=_("tenants"),
blank=True,
help_text=_("The tenants this user belongs to."),
related_name="guid_users_set",
)
2 changes: 1 addition & 1 deletion tenant_users/tenants/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ def has_tenant_access(self, request: HttpRequest) -> bool:
if not request.user.is_authenticated:
return True

return request.user.tenants.filter(id=request.tenant.id).exists()
return request.user.tenants.filter(pk=request.tenant.pk).exists()
2 changes: 1 addition & 1 deletion tenant_users/tenants/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def add_user(self, user_obj, *, is_superuser: bool = False, is_staff: bool = Fal
is_staff (bool): If True, assigns staff status to the user. Defaults to False.
"""
# User already is linked here..
if self.user_set.filter(id=user_obj.pk).exists():
if self.user_set.filter(pk=user_obj.pk).exists():
raise ExistsError(
f"User already added to tenant: {user_obj}",
)
Expand Down
7 changes: 7 additions & 0 deletions tests/fixtures/tenant.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import pytest
from django.contrib.auth import get_user_model
from django_tenants.utils import get_tenant_model, schema_context
from django_test_app.users.models import GuidUser

#: Constants
TenantModel = get_tenant_model()
TenantUser = get_user_model()
_USER_PASS = "test1234" # noqa: S105


@pytest.fixture()
def guid_tenant_user(db) -> GuidUser:
with schema_context("public"):
return TenantUser.objects.create_user(email="[email protected]")


@pytest.fixture()
def public_tenant(db) -> TenantModel: # noqa: ARG001
"""Returns Public Tenant instance."""
Expand Down
21 changes: 14 additions & 7 deletions tests/test_tenants/test_tenants_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@
UserModel = get_user_model()


def test_add_guid_based_user_to_tenant(test_tenants, guid_tenant_user) -> None:
"""Test tenant user management with guid based user"""
tenant = test_tenants.first()
# Just the owner
assert tenant.user_set.count() == 1
tenant.add_user(guid_tenant_user)
# Owner + My test user
assert tenant.user_set.count() == 2


def test_provision_with_schema_name(tenant_user) -> None:
"""Test tenant provisioning with a custom schema name.

Expand All @@ -37,7 +47,7 @@ def test_provision_with_schema_name(tenant_user) -> None:
owner = tenant.owner

with pytest.raises(
ExistsError, match="User already added to tenant: [email protected]"
ExistsError, match="User already added to tenant: [email protected]"
):
tenant.add_user(owner)
error_message = f"Cannot remove owner from tenant: {owner}"
Expand Down Expand Up @@ -77,7 +87,6 @@ def test_deleting_a_provision_tenant(tenant_user) -> None:
assert public_tenant.owner == tenant.owner
assert tenant.user_set.count() == 1
with tenant_context(tenant):

# Verify that the public owner retains permission to the provisioned tenant after deletion
public_owner_has_permission = UserTenantPermissions.objects.filter(
profile=tenant.owner
Expand All @@ -91,7 +100,7 @@ def test_deleting_a_provision_tenant(tenant_user) -> None:
profile=another_user
).exists()
assert (
previous_owner_has_permission == False
previous_owner_has_permission == False
), "The previous owner should not retain permission to the provisioned tenant after deletion."


Expand Down Expand Up @@ -134,11 +143,9 @@ def test_delete_provisioned_tenant_with_assigned_user_roles(tenant_user):


def test_transfer_ownership_to_existing_user(test_tenants, public_tenant):

tenant = test_tenants.first()
assert tenant.owner != public_tenant.owner, "Can't transfer ownership to same owner"
with tenant_context(tenant):

tenant.add_user(public_tenant.owner)
# Assign group or role to user through UserTenantPermissions
tenant.transfer_ownership(public_tenant.owner)
Expand All @@ -148,6 +155,6 @@ def test_transfer_ownership_to_existing_user(test_tenants, public_tenant):
profile=public_tenant.owner
).exists()
assert (
UserTenantPermissions.objects.get(profile=public_tenant.owner).is_superuser
== True
UserTenantPermissions.objects.get(profile=public_tenant.owner).is_superuser
== True
)
Loading