mirror of
https://github.com/Findus23/RPGnotes.git
synced 2024-09-19 15:43:45 +02:00
multi-tenant-system
This commit is contained in:
parent
3c884d4e1d
commit
dbf5f721ce
42 changed files with 464 additions and 128 deletions
0
campaigns/__init__.py
Normal file
0
campaigns/__init__.py
Normal file
9
campaigns/admin.py
Normal file
9
campaigns/admin.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
from django_tenants.admin import TenantAdminMixin
|
||||||
|
|
||||||
|
from campaigns.models import Campaign
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Campaign)
|
||||||
|
class ClientAdmin(TenantAdminMixin, admin.ModelAdmin):
|
||||||
|
list_display = ('name',)
|
6
campaigns/apps.py
Normal file
6
campaigns/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class CampaignsConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'campaigns'
|
42
campaigns/migrations/0001_initial.py
Normal file
42
campaigns/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# Generated by Django 3.2.6 on 2021-08-28 21:33
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django_tenants.postgresql_backend.base
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Campaign',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('schema_name', models.CharField(db_index=True, max_length=63, unique=True, validators=[django_tenants.postgresql_backend.base._check_schema_name])),
|
||||||
|
('slug', models.SlugField(blank=True, verbose_name='Tenant URL Name')),
|
||||||
|
('created', models.DateTimeField()),
|
||||||
|
('modified', models.DateTimeField(blank=True)),
|
||||||
|
('name', models.CharField(max_length=1000)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Domain',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('domain', models.CharField(db_index=True, max_length=253, unique=True)),
|
||||||
|
('is_primary', models.BooleanField(db_index=True, default=True)),
|
||||||
|
('tenant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='domains', to='campaigns.campaign')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
23
campaigns/migrations/0002_campaign_owner.py
Normal file
23
campaigns/migrations/0002_campaign_owner.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 3.2.6 on 2021-08-28 21:33
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('campaigns', '0001_initial'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='campaign',
|
||||||
|
name='owner',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
0
campaigns/migrations/__init__.py
Normal file
0
campaigns/migrations/__init__.py
Normal file
24
campaigns/models.py
Normal file
24
campaigns/models.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
from django.db import models
|
||||||
|
# Create your models here.
|
||||||
|
from django_tenants.models import DomainMixin
|
||||||
|
from tenant_users.tenants.models import TenantBase
|
||||||
|
|
||||||
|
from rpg_notes.secrets import DEBUG
|
||||||
|
|
||||||
|
|
||||||
|
class Campaign(TenantBase):
|
||||||
|
name = models.CharField(max_length=1000)
|
||||||
|
|
||||||
|
auto_create_schema = True
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
print(self.get_primary_domain().domain)
|
||||||
|
protocol = "http://" if DEBUG else "https://"
|
||||||
|
return protocol + self.get_primary_domain().domain + (":8000" if DEBUG else "")
|
||||||
|
|
||||||
|
|
||||||
|
class Domain(DomainMixin):
|
||||||
|
pass
|
3
campaigns/tests.py
Normal file
3
campaigns/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
3
campaigns/views.py
Normal file
3
campaigns/views.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
|
@ -2,9 +2,8 @@ from django.contrib import admin
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
from simple_history.admin import SimpleHistoryAdmin
|
from simple_history.admin import SimpleHistoryAdmin
|
||||||
|
|
||||||
from notes.models import Character, Campaign, Faction, Location, Loot, IngameDay, Session
|
from notes.models import Character, Faction, Location, Loot, IngameDay, Session
|
||||||
|
|
||||||
admin.site.register(Campaign, SimpleHistoryAdmin)
|
|
||||||
admin.site.register(Character, SimpleHistoryAdmin)
|
admin.site.register(Character, SimpleHistoryAdmin)
|
||||||
admin.site.register(Faction, SimpleHistoryAdmin)
|
admin.site.register(Faction, SimpleHistoryAdmin)
|
||||||
admin.site.register(Location, SimpleHistoryAdmin)
|
admin.site.register(Location, SimpleHistoryAdmin)
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
from django.forms import ModelForm, ModelMultipleChoiceField, CheckboxSelectMultiple
|
from django.forms import ModelForm, ModelMultipleChoiceField, CheckboxSelectMultiple
|
||||||
|
|
||||||
from notes.models import Campaign, Loot, Character, IngameDay, Session
|
from notes.models import Loot, Character, IngameDay, Session
|
||||||
|
|
||||||
|
|
||||||
class CampaignForm(ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = Campaign
|
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
|
|
||||||
class LootForm(ModelForm):
|
class LootForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
87
notes/migrations/0014_auto_20210828_2311.py
Normal file
87
notes/migrations/0014_auto_20210828_2311.py
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
# Generated by Django 3.2.6 on 2021-08-28 21:11
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('notes', '0013_auto_20210823_1405'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='campaign',
|
||||||
|
name='dm',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='character',
|
||||||
|
name='campaign',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='historicalcharacter',
|
||||||
|
name='campaign',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='historicalfaction',
|
||||||
|
name='campaign',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='historicalingameday',
|
||||||
|
name='campaign',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='historicallocation',
|
||||||
|
name='campaign',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='historicalloot',
|
||||||
|
name='campaign',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='historicalsession',
|
||||||
|
name='campaign',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='loot',
|
||||||
|
name='campaign',
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='faction',
|
||||||
|
unique_together=set(),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='ingameday',
|
||||||
|
unique_together=set(),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='location',
|
||||||
|
unique_together=set(),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='session',
|
||||||
|
unique_together=set(),
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='HistoricalCampaign',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='faction',
|
||||||
|
name='campaign',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='ingameday',
|
||||||
|
name='campaign',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='location',
|
||||||
|
name='campaign',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='session',
|
||||||
|
name='campaign',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Campaign',
|
||||||
|
),
|
||||||
|
]
|
28
notes/migrations/0015_auto_20210828_2323.py
Normal file
28
notes/migrations/0015_auto_20210828_2323.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 3.2.6 on 2021-08-28 21:23
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('notes', '0014_auto_20210828_2311'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='character',
|
||||||
|
name='slug',
|
||||||
|
field=models.SlugField(editable=False, unique=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='faction',
|
||||||
|
name='slug',
|
||||||
|
field=models.SlugField(editable=False, unique=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='location',
|
||||||
|
name='slug',
|
||||||
|
field=models.SlugField(editable=False, unique=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,5 +1,4 @@
|
||||||
from .descriptionmodel import DescriptionModel
|
from .descriptionmodel import DescriptionModel
|
||||||
from .campaign import Campaign
|
|
||||||
from .basemodel import BaseModel
|
from .basemodel import BaseModel
|
||||||
from .faction import Faction
|
from .faction import Faction
|
||||||
from .location import Location
|
from .location import Location
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
|
||||||
from notes.models import Campaign
|
|
||||||
|
|
||||||
|
|
||||||
class BaseModel(models.Model):
|
class BaseModel(models.Model):
|
||||||
campaign = models.ForeignKey(Campaign, on_delete=models.PROTECT)
|
|
||||||
name = models.CharField(max_length=1000)
|
name = models.CharField(max_length=1000)
|
||||||
slug = models.SlugField(editable=False)
|
slug = models.SlugField(editable=False, unique=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
unique_together = ["campaign", "slug"]
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.id:
|
if not self.id:
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
from django.conf import settings
|
|
||||||
from django.db import models
|
|
||||||
from django.urls import reverse
|
|
||||||
from simple_history.models import HistoricalRecords
|
|
||||||
|
|
||||||
from notes.models import DescriptionModel
|
|
||||||
|
|
||||||
|
|
||||||
class Campaign(DescriptionModel):
|
|
||||||
name = models.CharField(max_length=1000)
|
|
||||||
slug = models.SlugField(unique=True, editable=False)
|
|
||||||
dm = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
|
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
|
||||||
last_modified = models.DateTimeField(auto_now=True)
|
|
||||||
history = HistoricalRecords()
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse('campaigndetail', args=[str(self.slug)])
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
|
@ -24,7 +24,7 @@ class Character(BaseModel, DescriptionModel):
|
||||||
ordering = ["name"]
|
ordering = ["name"]
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('characterdetail', args=[self.campaign.slug, self.slug])
|
return reverse('characterdetail', args=[self.slug])
|
||||||
|
|
||||||
def initials(self):
|
def initials(self):
|
||||||
return "".join([word[0] for word in self.name.split()][:2]).upper()
|
return "".join([word[0] for word in self.name.split()][:2]).upper()
|
||||||
|
|
|
@ -3,12 +3,10 @@ from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from simple_history.models import HistoricalRecords
|
from simple_history.models import HistoricalRecords
|
||||||
|
|
||||||
from notes.models import Campaign, Session, DescriptionModel
|
from notes.models import Session, DescriptionModel
|
||||||
|
|
||||||
|
|
||||||
class IngameDay(DescriptionModel):
|
class IngameDay(DescriptionModel):
|
||||||
campaign = models.ForeignKey(Campaign, on_delete=models.PROTECT)
|
|
||||||
|
|
||||||
day = models.PositiveIntegerField()
|
day = models.PositiveIntegerField()
|
||||||
sessions = models.ManyToManyField(Session, related_name="ingame_days")
|
sessions = models.ManyToManyField(Session, related_name="ingame_days")
|
||||||
|
|
||||||
|
@ -18,10 +16,9 @@ class IngameDay(DescriptionModel):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["-day"]
|
ordering = ["-day"]
|
||||||
unique_together = ["campaign", "day"]
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('daydetail', args=[self.campaign.slug, self.day])
|
return reverse('daydetail', args=[self.day])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def prettyname(self):
|
def prettyname(self):
|
||||||
|
|
|
@ -2,12 +2,10 @@ from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from simple_history.models import HistoricalRecords
|
from simple_history.models import HistoricalRecords
|
||||||
|
|
||||||
from notes.models import Campaign, DescriptionModel
|
from notes.models import DescriptionModel
|
||||||
|
|
||||||
|
|
||||||
class Loot(DescriptionModel):
|
class Loot(DescriptionModel):
|
||||||
campaign = models.ForeignKey(Campaign, on_delete=models.PROTECT)
|
|
||||||
|
|
||||||
name = models.CharField(max_length=1000)
|
name = models.CharField(max_length=1000)
|
||||||
quantity = models.PositiveSmallIntegerField()
|
quantity = models.PositiveSmallIntegerField()
|
||||||
value_gold = models.DecimalField("Value (Gold)", max_digits=7, decimal_places=2)
|
value_gold = models.DecimalField("Value (Gold)", max_digits=7, decimal_places=2)
|
||||||
|
|
|
@ -4,12 +4,8 @@ from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from simple_history.models import HistoricalRecords
|
from simple_history.models import HistoricalRecords
|
||||||
|
|
||||||
from notes.models import Campaign
|
|
||||||
|
|
||||||
|
|
||||||
class Session(models.Model):
|
class Session(models.Model):
|
||||||
campaign = models.ForeignKey(Campaign, on_delete=models.PROTECT)
|
|
||||||
|
|
||||||
day = models.DateField(default=date.today)
|
day = models.DateField(default=date.today)
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
last_modified = models.DateTimeField(auto_now=True)
|
last_modified = models.DateTimeField(auto_now=True)
|
||||||
|
@ -17,10 +13,9 @@ class Session(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["-day"]
|
ordering = ["-day"]
|
||||||
unique_together = ["campaign", "day"]
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('sessiondetail', args=[self.campaign.slug, self.id])
|
return reverse('sessiondetail', args=[self.id])
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.day)
|
return str(self.day)
|
||||||
|
|
|
@ -9,17 +9,17 @@
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
|
{# <li class="nav-item">#}
|
||||||
|
{# <a class="nav-link active" href="{% url "campaigndetail" view.kwargs.campslug %}">Home</a>#}
|
||||||
|
{# </li>#}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" href="{% url "campaigndetail" view.kwargs.campslug %}">Home</a>
|
<a class="nav-link" href="{% url "characterlist" %}">Characters</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url "characterlist" view.kwargs.campslug %}">Characters</a>
|
<a class="nav-link" href="{% url "daylist" %}">Timeline</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url "daylist" view.kwargs.campslug %}">Timeline</a>
|
<a class="nav-link" href="{% url "lootlist" %}">Loot</a>
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="{% url "lootlist" view.kwargs.campslug %}">Loot</a>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
{% include "notes/macros/character-pillar.html" with character=c %}
|
{% include "notes/macros/character-pillar.html" with character=c %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
<a class="btn btn-primary" href="{% url "characteradd" character.campaign.slug %}">Add Character</a>
|
<a class="btn btn-primary" href="{% url "characteradd" %}">Add Character</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
<div class="character-heading" style="border-bottom-color: #{{ character.color }}">
|
<div class="character-heading" style="border-bottom-color: #{{ character.color }}">
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
<h1>
|
<h1>
|
||||||
{{ character.name }}
|
{{ character.name }}
|
||||||
<a href="{% url "characteredit" character.campaign.slug character.slug %}">
|
<a href="{% url "characteredit" character.slug %}">
|
||||||
edit
|
edit
|
||||||
</a>
|
</a>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
{% if edit %}
|
{% if edit %}
|
||||||
<h1>Edit "{{ object.name }}"</h1>
|
<h1>Edit "{{ object.name }}"</h1>
|
||||||
{% if request.resolver_match.view_name %}
|
{% if request.resolver_match.view_name %}
|
||||||
<a class="btn btn-danger" href="{% url "lootdelete" object.campaign.slug object.id %}">Delete</a>
|
<a class="btn btn-danger" href="{% url "lootdelete" object.id %}">Delete</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<h1>Add new</h1>
|
<h1>Add new</h1>
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="5" class="collapse-cell">
|
<td colspan="5" class="collapse-cell">
|
||||||
<div class="collapse" id="row-{{ l.id }}">
|
<div class="collapse" id="row-{{ l.id }}">
|
||||||
<h3>{{ l.name }} <a href="{% url "lootedit" l.campaign.slug l.id %}">edit</a></h3>
|
<h3>{{ l.name }} <a href="{% url "lootedit" l.id %}">edit</a></h3>
|
||||||
{{ l.description_html|safe }}
|
{{ l.description_html|safe }}
|
||||||
<p>Value each: {{ l.value_per_unit|format_money_html }}</p>
|
<p>Value each: {{ l.value_per_unit|format_money_html }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
<dd>Total value:</dd>
|
<dd>Total value:</dd>
|
||||||
<dt>{{ total_value|format_money_html }}</dt>
|
<dt>{{ total_value|format_money_html }}</dt>
|
||||||
</dl>
|
</dl>
|
||||||
<a href="{% url "lootadd" loot.0.campaign.slug %}" class="btn btn-primary">Add Loot</a>
|
<a href="{% url "lootadd" %}" class="btn btn-primary">Add Loot</a>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -3,23 +3,23 @@ from django.urls import path
|
||||||
from notes import views
|
from notes import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", views.CampaignListView.as_view(), name="campaignlist"),
|
# path("", views.CampaignListView.as_view(), name="campaignlist"),
|
||||||
path("c/add", views.CampaignCreateView.as_view(), name="campaigncreate"),
|
# path("c/add", views.CampaignCreateView.as_view(), name="campaigncreate"),
|
||||||
path("c/<slug:campslug>", views.CampaignDetailView.as_view(), name="campaigndetail"),
|
# path("c/<slug:campslug>", views.CampaignDetailView.as_view(), name="campaigndetail"),
|
||||||
path("c/<slug:campslug>/edit", views.CampaignEditView.as_view(), name="campaignedit"),
|
# path("c/<slug:campslug>/edit", views.CampaignEditView.as_view(), name="campaignedit"),
|
||||||
path("c/<slug:campslug>/delete", views.CampaignDeleteView.as_view(), name="campaigndelete"),
|
# path("c/<slug:campslug>/delete", views.CampaignDeleteView.as_view(), name="campaigndelete"),
|
||||||
path("c/<slug:campslug>/loot", views.LootListView.as_view(), name="lootlist"),
|
path("loot", views.LootListView.as_view(), name="lootlist"),
|
||||||
path("c/<slug:campslug>/loot/<int:pk>/edit", views.LootEditView.as_view(), name="lootedit"),
|
path("loot/<int:pk>/edit", views.LootEditView.as_view(), name="lootedit"),
|
||||||
path("c/<slug:campslug>/loot/<int:pk>/delete", views.LootDeleteView.as_view(), name="lootdelete"),
|
path("loot/<int:pk>/delete", views.LootDeleteView.as_view(), name="lootdelete"),
|
||||||
path("c/<slug:campslug>/loot/add", views.LootCreateView.as_view(), name="lootadd"),
|
path("loot/add", views.LootCreateView.as_view(), name="lootadd"),
|
||||||
path("c/<slug:campslug>/character/", views.list_character_redirect, name="characterlist"),
|
path("character/", views.list_character_redirect, name="characterlist"),
|
||||||
path("c/<slug:campslug>/character/add", views.CharacterCreateView.as_view(), name="characteradd"),
|
path("character/add", views.CharacterCreateView.as_view(), name="characteradd"),
|
||||||
path("c/<slug:campslug>/character/<slug:charslug>", views.CharacterDetailView.as_view(), name="characterdetail"),
|
path("character/<slug:slug>", views.CharacterDetailView.as_view(), name="characterdetail"),
|
||||||
path("c/<slug:campslug>/character/<slug:charslug>/edit", views.CharacterEditView.as_view(), name="characteredit"),
|
path("character/<slug:slug>/edit", views.CharacterEditView.as_view(), name="characteredit"),
|
||||||
path("c/<slug:campslug>/character/<slug:charslug>/delete", views.CharacterDeleteView.as_view(), name="characterdelete"),
|
path("character/<slug:slug>/delete", views.CharacterDeleteView.as_view(), name="characterdelete"),
|
||||||
path("c/<slug:campslug>/day/", views.list_day_redirect, name="daylist"),
|
path("day/", views.list_day_redirect, name="daylist"),
|
||||||
path("c/<slug:campslug>/day/add", views.DayCreateView.as_view(), name="dayadd"),
|
path("day/add", views.DayCreateView.as_view(), name="dayadd"),
|
||||||
path("c/<slug:campslug>/day/<int:day>", views.DayDetailView.as_view(), name="daydetail"),
|
path("day/<int:day>", views.DayDetailView.as_view(), name="daydetail"),
|
||||||
path("c/<slug:campslug>/day/<int:day>/edit", views.DayEditView.as_view(), name="dayedit"),
|
path("day/<int:day>/edit", views.DayEditView.as_view(), name="dayedit"),
|
||||||
path("c/<slug:campslug>/day/<int:day>/delete", views.DayDeleteView.as_view(), name="daydelete"),
|
path("day/<int:day>/delete", views.DayDeleteView.as_view(), name="daydelete"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -13,7 +13,8 @@ currencies_HTML = [
|
||||||
"<span class='pp'>PP</span>"
|
"<span class='pp'>PP</span>"
|
||||||
]
|
]
|
||||||
|
|
||||||
use_platinum=False
|
use_platinum = False
|
||||||
|
|
||||||
|
|
||||||
def digitize(n):
|
def digitize(n):
|
||||||
print("n", n)
|
print("n", n)
|
||||||
|
@ -33,6 +34,8 @@ def digitize(n):
|
||||||
|
|
||||||
|
|
||||||
def format_money(money: Decimal, html=False) -> str:
|
def format_money(money: Decimal, html=False) -> str:
|
||||||
|
if not money:
|
||||||
|
return ""
|
||||||
currencies = currencies_HTML if html else currencies_text
|
currencies = currencies_HTML if html else currencies_text
|
||||||
output = []
|
output = []
|
||||||
cp = round(money / copper, ndigits=0)
|
cp = round(money / copper, ndigits=0)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from .campaign_views import *
|
# from .campaign_views import *
|
||||||
from .loot_views import *
|
from .loot_views import *
|
||||||
from .character_views import *
|
from .character_views import *
|
||||||
from .day_views import *
|
from .day_views import *
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.urls import reverse_lazy
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
|
|
||||||
from notes.forms import CharacterForm
|
from notes.forms import CharacterForm
|
||||||
from notes.models import Character, Campaign
|
from notes.models import Character
|
||||||
|
|
||||||
|
|
||||||
# class CharacterListView(generic.ListView):
|
# class CharacterListView(generic.ListView):
|
||||||
|
@ -15,8 +15,9 @@ from notes.models import Character, Campaign
|
||||||
#
|
#
|
||||||
|
|
||||||
def list_character_redirect(request, *args, **kwargs):
|
def list_character_redirect(request, *args, **kwargs):
|
||||||
first_character:Character=Character.objects.first()
|
first_character: Character = Character.objects.first()
|
||||||
|
if not first_character:
|
||||||
|
return redirect("characteradd")
|
||||||
return redirect(first_character)
|
return redirect(first_character)
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,18 +29,17 @@ class CharacterDetailView(generic.DetailView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
data = super().get_context_data(**kwargs)
|
data = super().get_context_data(**kwargs)
|
||||||
data["player_characters"] = Character.objects.filter(
|
data["player_characters"] = Character.objects.filter(
|
||||||
campaign__slug=self.kwargs['campslug'],
|
|
||||||
player__isnull=False
|
player__isnull=False
|
||||||
).select_related()
|
).select_related()
|
||||||
data["npcs"] = Character.objects.filter(
|
data["npcs"] = Character.objects.filter(
|
||||||
campaign__slug=self.kwargs['campslug'], player__isnull=True
|
player__isnull=True
|
||||||
).select_related()
|
).select_related()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_object(self, queryset=None):
|
# def get_object(self, queryset=None):
|
||||||
return Character.objects.get(
|
# return Character.objects.get(
|
||||||
campaign__slug=self.kwargs['campslug'], slug=self.kwargs['charslug']
|
# campaign__slug=self.kwargs['campslug'], slug=self.kwargs['charslug']
|
||||||
)
|
# )
|
||||||
|
|
||||||
|
|
||||||
class CharacterCreateView(generic.CreateView):
|
class CharacterCreateView(generic.CreateView):
|
||||||
|
@ -48,18 +48,14 @@ class CharacterCreateView(generic.CreateView):
|
||||||
form_class = CharacterForm
|
form_class = CharacterForm
|
||||||
context_object_name = "object"
|
context_object_name = "object"
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
form.instance.campaign = Campaign.objects.get(slug=self.kwargs['campslug'])
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
|
|
||||||
class CharacterEditView(generic.UpdateView):
|
class CharacterEditView(generic.UpdateView):
|
||||||
template_name = "notes/loot_edit.html"
|
template_name = "notes/loot_edit.html"
|
||||||
model = Character
|
model = Character
|
||||||
form_class = CharacterForm
|
form_class = CharacterForm
|
||||||
|
|
||||||
def get_object(self, queryset=None):
|
# def get_object(self, queryset=None):
|
||||||
return Character.objects.get(campaign__slug=self.kwargs['campslug'], slug=self.kwargs['charslug'])
|
# return Character.objects.get(campaign__slug=self.kwargs['campslug'], slug=self.kwargs['charslug'])
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
data = super().get_context_data(**kwargs)
|
data = super().get_context_data(**kwargs)
|
||||||
|
|
|
@ -3,11 +3,13 @@ from django.urls import reverse_lazy
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
|
|
||||||
from notes.forms import DayForm
|
from notes.forms import DayForm
|
||||||
from notes.models import Session, Campaign, IngameDay
|
from notes.models import IngameDay
|
||||||
|
|
||||||
|
|
||||||
def list_day_redirect(request, *args, **kwargs):
|
def list_day_redirect(request, *args, **kwargs):
|
||||||
latest_day: IngameDay = IngameDay.objects.first()
|
latest_day: IngameDay = IngameDay.objects.first()
|
||||||
|
if not latest_day:
|
||||||
|
return redirect("dayadd")
|
||||||
return redirect(latest_day)
|
return redirect(latest_day)
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,10 +33,6 @@ class DayCreateView(generic.CreateView):
|
||||||
form_class = DayForm
|
form_class = DayForm
|
||||||
context_object_name = "object"
|
context_object_name = "object"
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
form.instance.campaign = Campaign.objects.get(slug=self.kwargs['campslug'])
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
|
|
||||||
class DayEditView(generic.UpdateView):
|
class DayEditView(generic.UpdateView):
|
||||||
template_name = "notes/loot_edit.html"
|
template_name = "notes/loot_edit.html"
|
||||||
|
@ -46,13 +44,14 @@ class DayEditView(generic.UpdateView):
|
||||||
data['edit'] = True
|
data['edit'] = True
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_object(self, queryset=None):
|
# def get_object(self, queryset=None):
|
||||||
return IngameDay.objects.get(campaign__slug=self.kwargs['campslug'], day=self.kwargs['day'])
|
# return IngameDay.objects.get(campaign__slug=self.kwargs['campslug'], day=self.kwargs['day'])
|
||||||
|
|
||||||
|
|
||||||
class DayDeleteView(generic.DeleteView):
|
class DayDeleteView(generic.DeleteView):
|
||||||
template_name = "notes/campaign_confirm_delete.html"
|
template_name = "notes/campaign_confirm_delete.html"
|
||||||
model = IngameDay
|
model = IngameDay
|
||||||
success_url = reverse_lazy('daylist')
|
success_url = reverse_lazy('daylist')
|
||||||
|
|
||||||
def get_object(self, queryset=None):
|
# def get_object(self, queryset=None):
|
||||||
return IngameDay.objects.get(campaign__slug=self.kwargs['campslug'], day=self.kwargs['day'])
|
# return IngameDay.objects.get(campaign__slug=self.kwargs['campslug'], day=self.kwargs['day'])
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.urls import reverse_lazy, reverse
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
|
|
||||||
from notes.forms import LootForm
|
from notes.forms import LootForm
|
||||||
from notes.models import Loot, Campaign
|
from notes.models import Loot
|
||||||
|
|
||||||
|
|
||||||
class LootListView(generic.ListView):
|
class LootListView(generic.ListView):
|
||||||
|
@ -11,8 +11,8 @@ class LootListView(generic.ListView):
|
||||||
model = Loot
|
model = Loot
|
||||||
context_object_name = "loot"
|
context_object_name = "loot"
|
||||||
|
|
||||||
def get_queryset(self):
|
# def get_queryset(self):
|
||||||
return Loot.objects.filter(campaign__slug=self.kwargs['campslug'])
|
# return Loot.objects.filter(campaign__slug=self.kwargs['campslug'])
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
data = super().get_context_data(**kwargs)
|
data = super().get_context_data(**kwargs)
|
||||||
|
@ -31,12 +31,8 @@ class LootCreateView(generic.CreateView):
|
||||||
model = Loot
|
model = Loot
|
||||||
form_class = LootForm
|
form_class = LootForm
|
||||||
|
|
||||||
def form_valid(self, form):
|
success_url = reverse_lazy("lootlist")
|
||||||
form.instance.campaign = Campaign.objects.get(slug=self.kwargs['campslug'])
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse("lootlist", kwargs={"campslug": self.kwargs.get("campslug")})
|
|
||||||
|
|
||||||
|
|
||||||
class LootEditView(generic.UpdateView):
|
class LootEditView(generic.UpdateView):
|
||||||
|
@ -45,8 +41,7 @@ class LootEditView(generic.UpdateView):
|
||||||
form_class = LootForm
|
form_class = LootForm
|
||||||
context_object_name = "object"
|
context_object_name = "object"
|
||||||
|
|
||||||
def get_success_url(self):
|
success_url = reverse_lazy("lootlist")
|
||||||
return reverse("lootlist", kwargs={"campslug": self.kwargs.get("campslug")})
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
data = super().get_context_data(**kwargs)
|
data = super().get_context_data(**kwargs)
|
||||||
|
@ -57,5 +52,5 @@ class LootEditView(generic.UpdateView):
|
||||||
class LootDeleteView(generic.DeleteView):
|
class LootDeleteView(generic.DeleteView):
|
||||||
template_name = "notes/campaign_confirm_delete.html"
|
template_name = "notes/campaign_confirm_delete.html"
|
||||||
model = Loot
|
model = Loot
|
||||||
def get_success_url(self):
|
|
||||||
return reverse("lootlist", kwargs={"campslug": self.kwargs.get("campslug")})
|
success_url = reverse_lazy("lootlist")
|
||||||
|
|
38
poetry.lock
generated
38
poetry.lock
generated
|
@ -26,7 +26,7 @@ lxml = ["lxml"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bleach"
|
name = "bleach"
|
||||||
version = "4.0.0"
|
version = "4.1.0"
|
||||||
description = "An easy safelist-based HTML-sanitizing tool."
|
description = "An easy safelist-based HTML-sanitizing tool."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -94,6 +94,32 @@ category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-tenant-users"
|
||||||
|
version = "0.3.12"
|
||||||
|
description = "A Django app to extend django-tenants to incorporate global multi-tenant users"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "^3.6.2"
|
||||||
|
develop = false
|
||||||
|
|
||||||
|
[package.source]
|
||||||
|
type = "git"
|
||||||
|
url = "https://github.com/Corvia/django-tenant-users.git"
|
||||||
|
reference = "master"
|
||||||
|
resolved_reference = "6a50750e9fd8411e698acbcaa70676a09f141d99"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-tenants"
|
||||||
|
version = "3.3.2"
|
||||||
|
description = "Tenant support for Django using PostgreSQL schemas."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
Django = ">=2.1,<4.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libsass"
|
name = "libsass"
|
||||||
version = "0.21.0"
|
version = "0.21.0"
|
||||||
|
@ -202,7 +228,7 @@ python-versions = "*"
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.9"
|
python-versions = "^3.9"
|
||||||
content-hash = "de84127badac66220a787600d18455d7cf562aba0c81c05d7219f39f54a97cf9"
|
content-hash = "efbcfa2f67a6955f114171ea17e50bf2d5da52108aaf0c335bb29fe87cb40451"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
asgiref = [
|
asgiref = [
|
||||||
|
@ -215,8 +241,8 @@ beautifulsoup4 = [
|
||||||
{file = "beautifulsoup4-4.9.3.tar.gz", hash = "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25"},
|
{file = "beautifulsoup4-4.9.3.tar.gz", hash = "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25"},
|
||||||
]
|
]
|
||||||
bleach = [
|
bleach = [
|
||||||
{file = "bleach-4.0.0-py2.py3-none-any.whl", hash = "sha256:c1685a132e6a9a38bf93752e5faab33a9517a6c0bb2f37b785e47bf253bdb51d"},
|
{file = "bleach-4.1.0-py2.py3-none-any.whl", hash = "sha256:4d2651ab93271d1129ac9cbc679f524565cc8a1b791909c4a51eac4446a15994"},
|
||||||
{file = "bleach-4.0.0.tar.gz", hash = "sha256:ffa9221c6ac29399cc50fcc33473366edd0cf8d5e2cbbbb63296dc327fb67cc8"},
|
{file = "bleach-4.1.0.tar.gz", hash = "sha256:0900d8b37eba61a802ee40ac0061f8c2b5dee29c1927dd1d233e075ebf5a71da"},
|
||||||
]
|
]
|
||||||
bleach-allowlist = [
|
bleach-allowlist = [
|
||||||
{file = "bleach-allowlist-1.0.3.tar.gz", hash = "sha256:56e22086079a0e6a3100ae99e4dbaf20eb2585486598eb0e994008a11428da9b"},
|
{file = "bleach-allowlist-1.0.3.tar.gz", hash = "sha256:56e22086079a0e6a3100ae99e4dbaf20eb2585486598eb0e994008a11428da9b"},
|
||||||
|
@ -238,6 +264,10 @@ django-simple-history = [
|
||||||
{file = "django-simple-history-3.0.0.tar.gz", hash = "sha256:66fe76c560054be393c52b1799661e104fbe372918d37d151e5d41c676158118"},
|
{file = "django-simple-history-3.0.0.tar.gz", hash = "sha256:66fe76c560054be393c52b1799661e104fbe372918d37d151e5d41c676158118"},
|
||||||
{file = "django_simple_history-3.0.0-py2.py3-none-any.whl", hash = "sha256:a312adfe8fbec4c450b08e641b11249a8a589a7e7d1ba2404764b8b5bed53552"},
|
{file = "django_simple_history-3.0.0-py2.py3-none-any.whl", hash = "sha256:a312adfe8fbec4c450b08e641b11249a8a589a7e7d1ba2404764b8b5bed53552"},
|
||||||
]
|
]
|
||||||
|
django-tenant-users = []
|
||||||
|
django-tenants = [
|
||||||
|
{file = "django-tenants-3.3.2.tar.gz", hash = "sha256:e3897662bb88007216ef6e3dde4467db394d4f1a4430b10aebc3ec9e58e8e20f"},
|
||||||
|
]
|
||||||
libsass = [
|
libsass = [
|
||||||
{file = "libsass-0.21.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:06c8776417fe930714bdc930a3d7e795ae3d72be6ac883ff72a1b8f7c49e5ffb"},
|
{file = "libsass-0.21.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:06c8776417fe930714bdc930a3d7e795ae3d72be6ac883ff72a1b8f7c49e5ffb"},
|
||||||
{file = "libsass-0.21.0-cp27-cp27m-win32.whl", hash = "sha256:a005f298f64624f313a3ac618ab03f844c71d84ae4f4a4aec4b68d2a4ffe75eb"},
|
{file = "libsass-0.21.0-cp27-cp27m-win32.whl", hash = "sha256:a005f298f64624f313a3ac618ab03f844c71d84ae4f4a4aec4b68d2a4ffe75eb"},
|
||||||
|
|
|
@ -17,6 +17,8 @@ Pillow = "^8.3.1"
|
||||||
sorl-thumbnail = "^12.7.0"
|
sorl-thumbnail = "^12.7.0"
|
||||||
libsass = "^0.21.0"
|
libsass = "^0.21.0"
|
||||||
django-debug-toolbar = "^3.2.2"
|
django-debug-toolbar = "^3.2.2"
|
||||||
|
django-tenants = "^3.3.2"
|
||||||
|
django-tenant-users = {git = "https://github.com/Corvia/django-tenant-users.git"}
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
|
|
||||||
|
|
|
@ -25,22 +25,57 @@ ALLOWED_HOSTS = []
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
DATABASE_ROUTERS = (
|
||||||
'notes.apps.NotesConfig',
|
'django_tenants.routers.TenantSyncRouter',
|
||||||
'django.contrib.admin',
|
)
|
||||||
|
SHARED_APPS = (
|
||||||
|
'django_tenants', # mandatory
|
||||||
|
'campaigns', # you must list the app where your tenant model resides in
|
||||||
|
'users',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
|
'tenant_users.permissions', # Defined in both shared apps and tenant apps
|
||||||
|
'tenant_users.tenants', # defined only in shared apps
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.admin',
|
||||||
'django.contrib.humanize',
|
'django.contrib.humanize',
|
||||||
'simple_history',
|
'django.contrib.staticfiles',
|
||||||
'django_bootstrap5',
|
'django_bootstrap5',
|
||||||
'sorl.thumbnail',
|
'sorl.thumbnail',
|
||||||
'debug_toolbar'
|
'debug_toolbar'
|
||||||
]
|
)
|
||||||
|
|
||||||
|
TENANT_APPS = (
|
||||||
|
# The following Django contrib apps must be in TENANT_APPS
|
||||||
|
'django.contrib.auth', # Defined in both shared apps and tenant apps
|
||||||
|
'django.contrib.contenttypes', # Defined in both shared apps and tenant apps
|
||||||
|
'tenant_users.permissions', # Defined in both shared apps and tenant apps
|
||||||
|
'django.contrib.admin',
|
||||||
|
|
||||||
|
'notes',
|
||||||
|
'simple_history',
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
INSTALLED_APPS = list(SHARED_APPS) + [app for app in TENANT_APPS if app not in SHARED_APPS]
|
||||||
|
|
||||||
|
TENANT_MODEL = "campaigns.Campaign"
|
||||||
|
|
||||||
|
TENANT_DOMAIN_MODEL = "campaigns.Domain"
|
||||||
|
|
||||||
|
TENANT_USERS_DOMAIN = "test.localhost"
|
||||||
|
|
||||||
|
AUTH_USER_MODEL = 'users.TenantUser'
|
||||||
|
|
||||||
|
AUTHENTICATION_BACKENDS = (
|
||||||
|
'tenant_users.permissions.backend.UserBackend',
|
||||||
|
)
|
||||||
|
|
||||||
|
SESSION_COOKIE_DOMAIN = '.test.localhost'
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
'django_tenants.middleware.main.TenantMainMiddleware',
|
||||||
'debug_toolbar.middleware.DebugToolbarMiddleware',
|
'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
@ -61,6 +96,7 @@ TEMPLATES = [
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'context_processors': [
|
'context_processors': [
|
||||||
|
'django.template.context_processors.request',
|
||||||
'django.template.context_processors.debug',
|
'django.template.context_processors.debug',
|
||||||
'django.template.context_processors.request',
|
'django.template.context_processors.request',
|
||||||
'django.contrib.auth.context_processors.auth',
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
|
28
templates/registration/login.html
Normal file
28
templates/registration/login.html
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load django_bootstrap5 %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% block mainpage %}
|
||||||
|
<h1>Login</h1>
|
||||||
|
|
||||||
|
{% if next %}
|
||||||
|
<div class="alert alert-info">
|
||||||
|
You need to log in before you can access
|
||||||
|
<code>{{ next }}</code>.
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post" class="form">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_form form %}
|
||||||
|
{% url 'admin_password_reset' as password_reset_url %}
|
||||||
|
<div class="password-reset-link">
|
||||||
|
<a href="{% url 'password_reset' %}">Forgotten your password or username?</a>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-lg btn-primary btn-block">
|
||||||
|
Sign in
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
0
users/__init__.py
Normal file
0
users/__init__.py
Normal file
8
users/admin.py
Normal file
8
users/admin.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
|
from django.contrib.auth.admin import UserAdmin
|
||||||
|
|
||||||
|
from users.models import TenantUser
|
||||||
|
|
||||||
|
admin.site.register(TenantUser)
|
6
users/apps.py
Normal file
6
users/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class UsersConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'users'
|
33
users/migrations/0001_initial.py
Normal file
33
users/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# Generated by Django 3.2.6 on 2021-08-28 21:33
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import tenant_users.permissions.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('campaigns', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TenantUser',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('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=100, verbose_name='Name')),
|
||||||
|
('tenants', models.ManyToManyField(blank=True, help_text='The tenants this user belongs to.', related_name='user_set', to='campaigns.Campaign', verbose_name='tenants')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
bases=(models.Model, tenant_users.permissions.models.PermissionsMixinFacade),
|
||||||
|
),
|
||||||
|
]
|
0
users/migrations/__init__.py
Normal file
0
users/migrations/__init__.py
Normal file
11
users/models.py
Normal file
11
users/models.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
from tenant_users.tenants.models import UserProfile
|
||||||
|
|
||||||
|
|
||||||
|
class TenantUser(UserProfile):
|
||||||
|
name = models.CharField(
|
||||||
|
"Name",
|
||||||
|
max_length=100,
|
||||||
|
blank=True,
|
||||||
|
)
|
3
users/tests.py
Normal file
3
users/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
3
users/views.py
Normal file
3
users/views.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
Loading…
Reference in a new issue