Skip to content

Commit

Permalink
Removes reserved tenant_id dependency (#151)
Browse files Browse the repository at this point in the history
* Removes reserved tenant_id dependency

* Adds documentation compile tests into pipeline

* Fixes documentation issues to make it compliant with latest library version

* Updates documentation to describe the usage of newly introduced TenantMeta option
  • Loading branch information
gurkanindibay authored Mar 2, 2023
1 parent 9ac16f3 commit 12efe25
Show file tree
Hide file tree
Showing 20 changed files with 451 additions and 1,385 deletions.
Binary file removed .coverage
Binary file not shown.
5 changes: 5 additions & 0 deletions .github/workflows/django-multitenant-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ jobs:
- name: Prospector checks
run: |
make lint
- name: Documentation Checks
run: |
cd docs
sphinx-build -W -b html source builds
Expand Down
74 changes: 74 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
### Django-Multitenant v3.1.0(March 1, 2023) ###

* Adds support for Django 4.1

* Adds support for setting tenant automatically for ManyToMany related model

* Fixes invalid error message problem in case of invalid field name

* Adds support for getting models using apps.get_model

* Removes reserved tenant_id limitation by introducing TenantMeta usage

* Introduces ReadTheDocs documentation

### Django-Multitenant v3.0.0(December 8, 2021) ###

* Adds support for Django 4.0

* Drops support for the following EOLed Django and Python versions:
1. Python 2.7
2. Django 1.11
3. Django 3.1

### Django-Multitenant v2.4.0(November 11, 2021) ###

* Backwards migration for `Distribute` migration using `undistribute_table()`

* Adds tests for Django 3.2 and Python 3.9

* Fixes migrations on Django 3.0+

* Fixes aggregations using `annotate`

### Django-Multitenant v2.0.9 (May 18, 2019) ###

* Fixes the process of running old migrations when the model has been deleted from the code.

### Django-Multitenant v2.0.8 (May 18, 2019) ###

* Add tests to confirm the join condition in subqueries includes tenant column.

### Django-Multitenant v2.0.7 (May 18, 2019) ###

* Fixes create with current tenant

### Django-Multitenant v2.0.6 (May 18, 2019) ###

* Fix recursive loop in warning for fields when joining without current_tenant set

### Django-Multitenant v2.0.5 (May 18, 2019) ###

* Adds support for custom query_set in TenantManager

* Cleans the delete code to ensure deleting rows only related to current tenant

### Django-Multitenant v2.0.4 (May 18, 2019) ###

* Adds support for multiple tenant

### Django-Multitenant v1.1.0 (January 26, 2018) ###

* Add TenantForeignKey to emulate composite foreign keys between tenant related models.

* Split apart library into multiple files. Importing the utility function `get_current_tenant` would cause errors due to the import statement triggering evaluation of the TenantModel class. This would cause problems if TenantModel were evaluated before the database backend was initialized.

* Added a simple TenantOneToOneField which does not try to enforce a uniqueness constraint on the ID column, but preserves all the relationship semantics of using a traditional OneToOneField in Django.

* Overrode Django's DatabaseSchemaEditor to produce foreign key constraints on composite foreign keys consisting of both the ID and tenant ID columns for any foreign key between TenantModels

* Monkey-patched Django's DeleteQuery implementation to include tenant_id in its SQL queries.

### Django-Multitenant v1.0.1 (November 7, 2017) ###

* Some bug fixes.
160 changes: 86 additions & 74 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,32 +39,44 @@ In order to use this library you can either use Mixins or have your models inher
from django_multitenant.fields import *
from django_multitenant.models import *
```
1. All models should inherit the TenantModel class.
2. All models should inherit the TenantModel class.
`Ex: class Product(TenantModel):`
1. Define a static variable named tenant_id and specify the tenant column using this variable.
`Ex: tenant_id='store_id'`
1. All foreign keys to TenantModel subclasses should use TenantForeignKey in place of
3. Define a static variable named tenant_id and specify the tenant column using this variable.You can define tenant_id in three ways. Any of them is acceptavle
* Using TenantMeta.tenant_field_name variable
* Using TenantMeta.tenant_id variable
* Using tenant_id field
<br>


> **Warning**
> Using tenant_id field directly in the class is not suggested since it may cause collision if class has a field named with 'tenant'
<br>
4. All foreign keys to TenantModel subclasses should use TenantForeignKey in place of
models.ForeignKey
1. A sample model implementing the above 2 steps:
```python
class Store(TenantModel):
tenant_id = 'id'
name = models.CharField(max_length=50)
address = models.CharField(max_length=255)
email = models.CharField(max_length=50)

class Product(TenantModel):
store = models.ForeignKey(Store)
tenant_id='store_id'
name = models.CharField(max_length=255)
description = models.TextField()
class Meta(object):
unique_together = ["id", "store"]
class Purchase(TenantModel):
store = models.ForeignKey(Store)
tenant_id='store_id'
product_purchased = TenantForeignKey(Product)
```
5. A sample model implementing the above 2 steps:
```python
class Store(TenantModel):
name = models.CharField(max_length=50)
address = models.CharField(max_length=255)
email = models.CharField(max_length=50)
class TenantMeta:
tenant_field_name = "id"

class Product(TenantModel):
store = models.ForeignKey(Store)
name = models.CharField(max_length=255)
description = models.TextField()
class Meta:
unique_together = ["id", "store"]
class TenantMeta:
tenant_field_name = "store_id"
class Purchase(TenantModel):
store = models.ForeignKey(Store)
product_purchased = TenantForeignKey(Product)
class TenantMeta:
tenant_field_name = "store_id"
```


### Changes in Models using mixins:
Expand All @@ -81,36 +93,36 @@ In order to use this library you can either use Mixins or have your models inher
1. Referenced table in TenenatForeignKey should include a unique key including tenant_id and primary key
```
Ex:
class Meta(object):
class Meta:
unique_together = ["id", "store"]
```
1. A sample model implementing the above 3 steps:
```python

class ProductManager(TenantManagerMixin, models.Manager):
pass

class Product(TenantModelMixin, models.Model):
store = models.ForeignKey(Store)
tenant_id='store_id'
name = models.CharField(max_length=255)
description = models.TextField()

objects = ProductManager()

class Meta(object):
unique_together = ["id", "store"]

class PurchaseManager(TenantManagerMixin, models.Manager):
pass

class Purchase(TenantModelMixin, models.Model):
store = models.ForeignKey(Store)
tenant_id='store_id'
product_purchased = TenantForeignKey(Product)
```python

objects = PurchaseManager()
```
class ProductManager(TenantManagerMixin, models.Manager):
pass

class Product(TenantModelMixin, models.Model):
store = models.ForeignKey(Store)
tenant_id='store_id'
name = models.CharField(max_length=255)
description = models.TextField()

objects = ProductManager()

class Meta:
unique_together = ["id", "store"]

class PurchaseManager(TenantManagerMixin, models.Manager):
pass

class Purchase(TenantModelMixin, models.Model):
store = models.ForeignKey(Store)
tenant_id='store_id'
product_purchased = TenantForeignKey(Product)

objects = PurchaseManager()
```



Expand Down Expand Up @@ -167,31 +179,31 @@ In order to use this library you can either use Mixins or have your models inher
## Supported APIs:
1. Most of the APIs under Model.objects.*.
1. Model.save() injects tenant_id for tenant inherited models.
```python
```python
s=Store.objects.all()[0]
set_current_tenant(s)

#All the below API calls would add suitable tenant filters.
#Simple get_queryset()
Product.objects.get_queryset()

#Simple join
Purchase.objects.filter(id=1).filter(store__name='The Awesome Store').filter(product__description='All products are awesome')

#Update
Purchase.objects.filter(id=1).update(id=1)

#Save
p=Product(8,1,'Awesome Shoe','These shoes are awesome')
p.save()

#Simple aggregates
Product.objects.count()
Product.objects.filter(store__name='The Awesome Store').count()

#Subqueries
Product.objects.filter(name='Awesome Shoe');
Purchase.objects.filter(product__in=p);
set_current_tenant(s)
#All the below API calls would add suitable tenant filters.
#Simple get_queryset()
Product.objects.get_queryset()
#Simple join
Purchase.objects.filter(id=1).filter(store__name='The Awesome Store').filter(product__description='All products are awesome')
#Update
Purchase.objects.filter(id=1).update(id=1)
#Save
p=Product(8,1,'Awesome Shoe','These shoes are awesome')
p.save()
#Simple aggregates
Product.objects.count()
Product.objects.filter(store__name='The Awesome Store').count()
#Subqueries
Product.objects.filter(name='Awesome Shoe');
Purchase.objects.filter(product__in=p);

```

Expand Down
Loading

0 comments on commit 12efe25

Please sign in to comment.